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
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)
{
* 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
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.
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;