]> granicus.if.org Git - libevent/commitdiff
Add LEV_OPT_BIND_IPV4_AND_IPV6 flag (#1400)
authorEdoardo Lolletti <edoardo762@gmail.com>
Sat, 4 Feb 2023 14:00:48 +0000 (15:00 +0100)
committerGitHub <noreply@github.com>
Sat, 4 Feb 2023 14:00:48 +0000 (15:00 +0100)
Libevent introduced the LEV_OPT_BIND_IPV6ONLY to pass to evconnlistener_new_bind to make it automatically set the underlying socket as accepting ipv6 requests. This works fine on posix compliant platforms as by the standard every new AF_INET6 socket is created as both supporting ipv6 and ipv4 connections. But on windows the default is the opposite, with the flag IPV6_V6ONLY  being always enabled by default.

This makes creating a listener to supports both protocols a bit more tricky as winsock doesn't allow changing this flag after evconnlistener_new_bind does all the initial setup because as stated in the docs, you can't change it after the sonnect connected, so one would have to manually create the socket beforehand and set the flag and then call evconnlistener_new with the socket itself.

It would be nice to have libevent keep a consistent behaviour across the platforms in this scenario, maybe or by making it always set IPV6_V6ONLY  to false unless LEV_OPT_BIND_IPV6ONLY is passed, in which case it's set to true, or add another flag to forcefully set it to false and keep the system dependent behaviour as default.

So this patch add new option for libevent listeners to bind to both - LEV_OPT_BIND_IPV4_AND_IPV6

evutil.c
include/event2/listener.h
include/event2/util.h
listener.c

index d46c997bd9dd1d7f1726a78352925c1595fab7e2..bbf908c06ed3c3f432dba513214d775d5fbbaa03 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -603,6 +603,17 @@ evutil_make_listen_socket_ipv6only(evutil_socket_t sock)
        return 0;
 }
 
+int
+evutil_make_listen_socket_not_ipv6only(evutil_socket_t sock)
+{
+#if defined(IPV6_V6ONLY)
+       int zero = 0;
+       return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&zero,
+               (ev_socklen_t)sizeof(zero));
+#endif
+       return 0;
+}
+
 int
 evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock)
 {
index 53b734e79f536af06534452236e323708472739e..8aa9e1f78fc428d8c864d71bb0c6f4b463a01e6e 100644 (file)
@@ -107,9 +107,18 @@ typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);
  * code works as expected without affected by bindv6only sysctl setting in
  * system.
  *
- * This socket option also supported by Windows.
+ * This socket option on Windows is instead enabled by default.
  */
 #define LEV_OPT_BIND_IPV6ONLY          (1u<<8)
+/** Flag: Indicates that the listener wants to work only in both IPv4 and 
+ * IPv6 socket.
+ *
+ * This flag exists as copmlement to LEV_OPT_BIND_IPV6ONLY to account for
+ * the different default behaviour on Windows so that the code can
+ * explicitly request the socket to support both modes without having
+ * to rely on the default option.
+ */
+#define LEV_OPT_BIND_IPV4_AND_IPV6             (1u<<9)
 
 /**
    Allocate a new evconnlistener object to listen for incoming TCP connections
index 43955bf6f7e286ffea003e1990dd38f7292f1b32..dc190426f995c75cf2a0f32d7e347c7070391a49 100644 (file)
@@ -429,12 +429,28 @@ int evutil_make_listen_socket_reuseable_port(evutil_socket_t sock);
     sockets is to work in IPv4-mapped mode. In IPv4-mapped mode, it is not possible
     to bind same port from different IPv4 and IPv6 handlers.
 
+    On Windows the default value is instead to only work in IPv6 mode.
+
     @param sock The socket to make in ipv6only working mode
     @return 0 on success, -1 on failure
  */
 EVENT2_EXPORT_SYMBOL
 int evutil_make_listen_socket_ipv6only(evutil_socket_t sock);
 
+/** Set ipv6 only bind socket option to make listener work in both ipv4 and ipv6 sockets.
+
+    According to RFC3493 and most Linux distributions, default value for the
+    sockets is to work in IPv4-mapped mode. In IPv4-mapped mode, it is not possible
+    to bind same port from different IPv4 and IPv6 handlers.
+
+    On Windows the default value is instead to only work in IPv6 mode.
+
+    @param sock The socket to make in ipv6only working mode
+    @return 0 on success, -1 on failure
+ */
+EVENT2_EXPORT_SYMBOL
+int evutil_make_listen_socket_not_ipv6only(evutil_socket_t sock);
+
 /** Do platform-specific operations as needed to close a socket upon a
     successful execution of one of the exec*() functions.
 
index fc7c2c583c49a7f6afa21f3dbb5168bd6d38a6c5..ffbc4186a47dc7091c377074958279d258940b3c 100644 (file)
@@ -264,6 +264,11 @@ evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
                        goto err;
        }
 
+       if (flags & LEV_OPT_BIND_IPV4_AND_IPV6) {
+               if (evutil_make_listen_socket_not_ipv6only(fd) < 0)
+                       goto err;
+       }
+
        if (sa) {
                if (bind(fd, sa, socklen)<0)
                        goto err;