From 70f17645defd2250b25b857105a2b0f4640ea5b5 Mon Sep 17 00:00:00 2001 From: KrazyIvan Date: Wed, 21 Jan 2015 23:46:03 +0800 Subject: [PATCH] Dual mode IPv4/IPv6 stack support Listen on all IP address (ipv4 and ipv6 dual mode) in single bind by default if not specify '-s' Create an IPv6 server socket that can also accept IPv4 connections. To do so, create a regular IPv6 socket, turn off the socket option IPV6_V6ONLY, bind it to the "any" address, and start receiving. IPv4 addresses will be presented as IPv6 addresses, in the IPv4-mapped format. --- src/server.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/server.c b/src/server.c index 1d39d30f..6ab60371 100644 --- a/src/server.c +++ b/src/server.c @@ -147,12 +147,14 @@ int setinterface(int socket_fd, const char * interface_name) int create_and_bind(const char *host, const char *port) { struct addrinfo hints; - struct addrinfo *result, *rp; + struct addrinfo *result, *rp, *ipv4v6bindall; int s, listen_sock; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ + hints.ai_flags = AI_ALL|AI_V4MAPPED|AI_PASSIVE|AI_ADDRCONFIG; /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_TCP; s = getaddrinfo(host, port, &hints, &result); if (s != 0) { @@ -160,7 +162,29 @@ int create_and_bind(const char *host, const char *port) return -1; } - for (rp = result; rp != NULL; rp = rp->ai_next) { + rp = result; + + /* + On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with + AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to + return a list of addresses to listen on, but it is impossible to listen on + 0.0.0.0 and :: at the same time, if :: implies dualstack mode. + */ + if (!host) { + ipv4v6bindall = result; + + /* Loop over all address infos found until a IPV6 address is found. */ + while (ipv4v6bindall) { + if (ipv4v6bindall->ai_family == AF_INET6) { + rp = ipv4v6bindall; /* Take first IPV6 address available */ + break; + } + + ipv4v6bindall= ipv4v6bindall->ai_next; /* Get next address info, if any */ + } + } + + for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { listen_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (listen_sock == -1) { continue; @@ -168,7 +192,11 @@ int create_and_bind(const char *host, const char *port) int opt = 1; - setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); + if (rp->ai_family == AF_INET6) { + int ipv6only = 0; + if (host) ipv6only = 1; + setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); + } setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); #ifdef SO_NOSIGPIPE @@ -1104,6 +1132,10 @@ int main(int argc, char **argv) } } + if (server_num == 0) { + server_host[server_num++]=NULL; + } + if (server_num == 0 || server_port == NULL || password == NULL) { usage(); exit(EXIT_FAILURE); @@ -1244,4 +1276,3 @@ int main(int argc, char **argv) return 0; } -