another attempt to get Linux and IPv6 right
svn: r1386
This commit is contained in:
parent
b8e937ac1f
commit
08b681655c
|
@ -234,13 +234,7 @@
|
||||||
|
|
||||||
# define USE_TIMEZONE_VAR
|
# define USE_TIMEZONE_VAR
|
||||||
|
|
||||||
# include <linux/version.h>
|
# define MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,20)
|
|
||||||
# define MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
|
||||||
# define MZ_TCP_LISTEN_IPV4_DEFAULT
|
|
||||||
# else
|
|
||||||
# define MZ_TCP_LISTEN_IPV4_ONLY
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define FLAGS_ALREADY_SET
|
# define FLAGS_ALREADY_SET
|
||||||
|
|
||||||
|
@ -1023,18 +1017,18 @@
|
||||||
/* MZ_BINARY is combinaed with other flags in all calls to open();
|
/* MZ_BINARY is combinaed with other flags in all calls to open();
|
||||||
it can be defined to O_BINARY in Cygwin, for example. */
|
it can be defined to O_BINARY in Cygwin, for example. */
|
||||||
|
|
||||||
/* MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT uses IPV6_V6ONLY for IPv6 listeners,
|
/* MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT uses IPV6_V6ONLY for IPv6
|
||||||
which means that the listener accepts only IPv6 connections. This is
|
listeners when the same listener has an IPv4 address, which means
|
||||||
used with Linux, for example, because a port cannot have both an
|
that the IpV6 listener accepts only IPv6 connections. This is used
|
||||||
IPv4 and IPv6 listener if the IPv6 one doesn't use IPV6_V6ONLY. */
|
with Linux, for example, because a port cannot have both an IPv4
|
||||||
|
and IPv6 listener if the IPv6 one doesn't use IPV6_V6ONLY. (The
|
||||||
|
two listeners might be for different interfaces, in which case
|
||||||
|
IPV6_V6ONLY is not necessary, but we must err on the side of being
|
||||||
|
too restrictive. If IPV6_V6ONLY is not #defined or if setting the
|
||||||
|
option doesn't work, then the IPv6 addresses are silently ignored
|
||||||
|
when creating the listener (but only where there is at least once
|
||||||
|
IPv4 address). */
|
||||||
|
|
||||||
/* MZ_TCP_LISTEN_IPV4_DEFAULT creates an IPv4 listener, only, when no
|
|
||||||
hostname is specified. */
|
|
||||||
|
|
||||||
/* MZ_TCP_LISTEN_IPV4_ONLY ignores any IPv6 addresses for an listener
|
|
||||||
hostname. This is used for Linux versions that do not support
|
|
||||||
IPV6_V6ONLY, which is needed to support both IPv6 and IPv4
|
|
||||||
listeners. */
|
|
||||||
|
|
||||||
/***********************/
|
/***********************/
|
||||||
/* Threads & Signals */
|
/* Threads & Signals */
|
||||||
|
|
|
@ -1753,6 +1753,9 @@ tcp_listen(int argc, Scheme_Object *argv[])
|
||||||
unsigned short id, origid;
|
unsigned short id, origid;
|
||||||
int backlog, errid;
|
int backlog, errid;
|
||||||
int reuse = 0;
|
int reuse = 0;
|
||||||
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
|
int no_ipv6 = 0;
|
||||||
|
#endif
|
||||||
const char *address;
|
const char *address;
|
||||||
|
|
||||||
if (!CHECK_PORT_ID(argv[0]))
|
if (!CHECK_PORT_ID(argv[0]))
|
||||||
|
@ -1791,37 +1794,82 @@ tcp_listen(int argc, Scheme_Object *argv[])
|
||||||
id = origid;
|
id = origid;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
retry:
|
||||||
|
|
||||||
{
|
{
|
||||||
GC_CAN_IGNORE struct addrinfo *tcp_listen_addr, *addr;
|
GC_CAN_IGNORE struct addrinfo *tcp_listen_addr, *addr;
|
||||||
int err, count = 0, pos = 0, i;
|
int err, count = 0, pos = 0, i;
|
||||||
listener_t *l = NULL;
|
listener_t *l = NULL;
|
||||||
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
|
int any_v4 = 0, any_v6 = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
tcp_listen_addr = scheme_get_host_address(address, id, &err,
|
tcp_listen_addr = scheme_get_host_address(address, id, &err,
|
||||||
#ifdef MZ_TCP_LISTEN_IPV4_ONLY
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
MZ_PF_INET,
|
no_ipv6 ? MZ_PF_INET : -1,
|
||||||
#else
|
#else
|
||||||
# ifdef MZ_TCP_LISTEN_IPV4_DEFAULT
|
|
||||||
!address ? MZ_PF_INET : -1,
|
|
||||||
# else
|
|
||||||
-1,
|
-1,
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
1, 1);
|
1, 1);
|
||||||
|
|
||||||
for (addr = tcp_listen_addr; addr; addr = addr->ai_next) {
|
for (addr = tcp_listen_addr; addr; addr = addr->ai_next) {
|
||||||
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
|
if (addr->ai_family == MZ_PF_INET)
|
||||||
|
any_v4 = 1;
|
||||||
|
else if (addr->ai_family == PF_INET6)
|
||||||
|
any_v6 = 1;
|
||||||
|
#endif
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tcp_listen_addr) {
|
if (tcp_listen_addr) {
|
||||||
tcp_t s;
|
tcp_t s;
|
||||||
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
|
/* Try IPv6 listeners first, so we can retry and use just IPv4 if
|
||||||
|
IPv6 doesn't work right. */
|
||||||
|
int v6_loop = (any_v6 && any_v4), skip_v6 = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
errid = 0;
|
errid = 0;
|
||||||
for (addr = tcp_listen_addr; addr; addr = addr->ai_next) {
|
for (addr = tcp_listen_addr; addr; ) {
|
||||||
s = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
|
||||||
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
if (addr->ai_family == PF_INET6) {
|
if ((v6_loop && (addr->ai_family != PF_INET6))
|
||||||
int on = 1;
|
|| (skip_v6 && (addr->ai_family == PF_INET6))) {
|
||||||
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
|
addr = addr->ai_next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
s = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||||
|
|
||||||
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
|
if (s == INVALID_SOCKET) {
|
||||||
|
/* Maybe it failed because IPv6 is not available: */
|
||||||
|
if ((addr->ai_family == PF_INET6) && (errno == EAFNOSUPPORT)) {
|
||||||
|
if (any_v4 && !pos) {
|
||||||
|
/* Maybe we can make it work with just IPv4. Try again. */
|
||||||
|
no_ipv6 = 1;
|
||||||
|
freeaddrinfo(tcp_listen_addr);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s != INVALID_SOCKET) {
|
||||||
|
if (any_v4 && (addr->ai_family == PF_INET6)) {
|
||||||
|
int ok;
|
||||||
|
# ifdef IPV6_V6ONLY
|
||||||
|
int on = 1;
|
||||||
|
ok = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
|
||||||
|
# else
|
||||||
|
ok = -1;
|
||||||
|
# endif
|
||||||
|
if (ok && !pos) {
|
||||||
|
/* IPV6_V6ONLY doesn't work */
|
||||||
|
no_ipv6 = 1;
|
||||||
|
freeaddrinfo(tcp_listen_addr);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1877,6 +1925,16 @@ tcp_listen(int argc, Scheme_Object *argv[])
|
||||||
errid = SOCK_ERRNO();
|
errid = SOCK_ERRNO();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addr = addr->ai_next;
|
||||||
|
|
||||||
|
#ifdef MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT
|
||||||
|
if (!addr && v6_loop) {
|
||||||
|
v6_loop = 0;
|
||||||
|
skip_v6 = 1;
|
||||||
|
addr = tcp_listen_addr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < pos; i++) {
|
for (i = 0; i < pos; i++) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user