"If set will limit number of clients to accept"),
ECORE_GETOPT_STORE_BOOL('r', "clients-reject-excess",
"If true, excess clients will be immediately rejected."),
+ ECORE_GETOPT_STORE_BOOL(0, "ipv6-only",
+ "If true (default), only IPv6 clients will be allowed for a server if an IPv6 was used, otherwise IPv4 clients will be automatically converted into IPv6 and handled transparently."),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
char *address = NULL;
unsigned int clients_limit = 0;
Eina_Bool clients_reject_excess = EINA_FALSE;
+ Eina_Bool ipv6_only = EINA_TRUE;
Eina_Bool quit_option = EINA_FALSE;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_BOOL(echo),
ECORE_GETOPT_VALUE_UINT(clients_limit),
ECORE_GETOPT_VALUE_BOOL(clients_reject_excess),
+ ECORE_GETOPT_VALUE_BOOL(ipv6_only),
/* standard block to provide version, copyright, license and help */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
goto end;
}
+ if (cls == EFL_NET_SERVER_TCP_CLASS)
+ efl_net_server_tcp_ipv6_only_set(server, ipv6_only);
+
/* an explicit call to efl_net_server_serve() after the object is
* constructed allows for more complex setup, such as interacting
* with the object to add more properties that couldn't be done
#define MY_CLASS EFL_NET_SERVER_TCP_CLASS
+typedef struct _Efl_Net_Server_Tcp_Data
+{
+ Eina_Bool ipv6_only;
+} Efl_Net_Server_Tcp_Data;
+
EOLIAN static Eina_Error
-_efl_net_server_tcp_efl_net_server_serve(Eo *o, void *pd EINA_UNUSED, const char *address)
+_efl_net_server_tcp_efl_net_server_serve(Eo *o, Efl_Net_Server_Tcp_Data *pd, const char *address)
{
struct sockaddr_storage addr = {};
char *str, *host, *port;
efl_net_server_fd_family_set(o, addr.ss_family);
- if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
- efl_net_server_address_set(o, buf);
-
fd = efl_net_socket4(addr.ss_family, SOCK_STREAM, IPPROTO_TCP,
efl_net_server_fd_close_on_exec_get(o));
if (fd < 0)
efl_loop_fd_set(o, fd);
+ /* apply pending value BEFORE bind() */
+ if (addr.ss_family == AF_INET6)
+ efl_net_server_tcp_ipv6_only_set(o, pd->ipv6_only);
+
r = bind(fd, (struct sockaddr *)&addr, addrlen);
if (r < 0)
{
goto error_listen;
}
+ if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
+ {
+ ERR("getsockname(%d): %s", fd, strerror(errno));
+ goto error_listen;
+ }
+ else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
+ efl_net_server_address_set(o, buf);
+
r = listen(fd, 0);
if (r < 0)
{
{ EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_tcp_client_event_closed });
static void
-_efl_net_server_tcp_efl_net_server_fd_client_add(Eo *o, void *pd EINA_UNUSED, int client_fd)
+_efl_net_server_tcp_efl_net_server_fd_client_add(Eo *o, Efl_Net_Server_Tcp_Data *pd EINA_UNUSED, int client_fd)
{
Eo *client = efl_add(EFL_NET_SOCKET_TCP_CLASS, o,
efl_event_callback_array_add(efl_added, _efl_net_server_tcp_client_cbs(), o),
}
static void
-_efl_net_server_tcp_efl_net_server_fd_client_reject(Eo *o, void *pd EINA_UNUSED, int client_fd)
+_efl_net_server_tcp_efl_net_server_fd_client_reject(Eo *o, Efl_Net_Server_Tcp_Data *pd EINA_UNUSED, int client_fd)
{
struct sockaddr_storage addr;
socklen_t addrlen;
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
}
+EOLIAN void
+_efl_net_server_tcp_ipv6_only_set(Eo *o, Efl_Net_Server_Tcp_Data *pd, Eina_Bool ipv6_only)
+{
+ Eina_Bool old = pd->ipv6_only;
+ int fd = efl_loop_fd_get(o);
+ int value = ipv6_only;
+
+ pd->ipv6_only = ipv6_only;
+
+ if (fd < 0) return;
+ if (efl_net_server_fd_family_get(o) != AF_INET6) return;
+
+#ifdef IPV6_V6ONLY
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value)) < 0)
+ {
+ ERR("could not set socket=%d IPV6_V6ONLY=%d: %s", fd, value, strerror(errno));
+ pd->ipv6_only = old;
+ }
+#endif
+}
+
+EOLIAN Eina_Bool
+_efl_net_server_tcp_ipv6_only_get(Eo *o EINA_UNUSED, Efl_Net_Server_Tcp_Data *pd)
+{
+#ifdef IPV6_V6ONLY
+ int fd = efl_loop_fd_get(o);
+ int value = 0;
+ socklen_t size = sizeof(value);
+
+ if (fd < 0) goto end;
+ if (efl_net_server_fd_family_get(o) != AF_INET6) goto end;
+
+ if (getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &value, &size) < 0)
+ {
+ WRN("getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY): %s", fd, strerror(errno));
+ goto end;
+ }
+ pd->ipv6_only = !!value;
+
+ end:
+#endif
+ return pd->ipv6_only;
+}
+
+
#include "efl_net_server_tcp.eo.c"
@since 1.19
]]
- data: null;
+ methods {
+ @property ipv6_only {
+ [[Whenever IPv6 listen address will accept only same-family clients or will allow IPv4 to connect as well.
+
+ Since Linux 2.4.21, Windows Vista and MacOS X these
+ control whenever a server that did bind to an IPv6
+ address will accept only IPv6 clients or will also
+ accept IPv4 by automatically converting them in an IPv6
+ address, allowing a single socket to handle both
+ protocols.
+
+ If an IPv6 address was used in @Efl.Net.Server.address,
+ this property is $false and an IPv4 connects, then an
+ address such as [::ffff:IPv4]:PORT will be used, such as
+ [::ffff:192.168.0.2]:1234, where the IPv4 address can be
+ extracted.
+
+ If an IPv4 address was used in @Efl.Net.Server.address,
+ this has no effect.
+
+ Systems can configure their default value, usually true
+ (allows only IPv6 clients).
+ ]]
+ values {
+ ipv6_only: bool;
+ }
+ }
+ }
implements {
Efl.Net.Server.serve;