Attempt to fix the IPv4/IPv6 problems once and for all
authorJuho Vähä-Herttua <juhovh@iki.fi>
Tue, 15 May 2012 20:00:12 +0000 (23:00 +0300)
committerJuho Vähä-Herttua <juhovh@iki.fi>
Wed, 16 May 2012 21:57:07 +0000 (00:57 +0300)
src/lib/httpd.c
src/lib/netutils.c

index 2bed984203ebbdd907eb480daa8ea20749f2c682..7606933696e1cb8548dc57e326bd346221a831d5 100644 (file)
@@ -48,8 +48,9 @@ struct httpd_s {
        thread_handle_t thread;
        mutex_handle_t run_mutex;
 
-       /* Server fd for accepting connections */
-       int server_fd;
+       /* Server fds for accepting connections */
+       int server_fd4;
+       int server_fd6;
 };
 
 httpd_t *
@@ -110,7 +111,7 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len
                }
        }
        if (i == httpd->max_connections) {
-               /* This code should never be reached, we do not select server_fd when full */
+               /* This code should never be reached, we do not select server_fds when full */
                logger_log(httpd->logger, LOGGER_INFO, "Max connections reached");
                shutdown(fd, SHUT_RDWR);
                closesocket(fd);
@@ -123,6 +124,40 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len
        httpd->connections[i].user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len);
 }
 
+static int
+httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
+{
+       struct sockaddr_storage remote_saddr;
+       socklen_t remote_saddrlen;
+       struct sockaddr_storage local_saddr;
+       socklen_t local_saddrlen;
+       unsigned char *local, *remote;
+       int local_len, remote_len;
+       int ret, fd;
+
+       remote_saddrlen = sizeof(remote_saddr);
+       fd = accept(server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen);
+       if (fd == -1) {
+               /* FIXME: Error happened */
+               return -1;
+       }
+
+       local_saddrlen = sizeof(local_saddr);
+       ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen);
+       if (ret == -1) {
+               closesocket(fd);
+               return 0;
+       }
+
+       logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d",
+                  (is_ipv6 ? "IPv6"  : "IPv4"), fd);
+       local = netutils_get_address(&local_saddr, &local_len);
+       remote = netutils_get_address(&remote_saddr, &remote_len);
+
+       httpd_add_connection(httpd, fd, local, local_len, remote, remote_len);
+       return 1;
+}
+
 static void
 httpd_remove_connection(httpd_t *httpd, http_connection_t *connection)
 {
@@ -166,8 +201,14 @@ httpd_thread(void *arg)
                /* Get the correct nfds value and set rfds */
                FD_ZERO(&rfds);
                if (httpd->open_connections < httpd->max_connections) {
-                       FD_SET(httpd->server_fd, &rfds);
-                       nfds = httpd->server_fd+1;
+                       FD_SET(httpd->server_fd4, &rfds);
+                       nfds = httpd->server_fd4+1;
+                       if (httpd->server_fd6 != -1) {
+                               FD_SET(httpd->server_fd6, &rfds);
+                               if (nfds <= httpd->server_fd6) {
+                                       nfds = httpd->server_fd6+1;
+                               }
+                       }
                }
                for (i=0; i<httpd->max_connections; i++) {
                        int socket_fd;
@@ -191,34 +232,21 @@ httpd_thread(void *arg)
                        break;
                }
 
-               if (FD_ISSET(httpd->server_fd, &rfds)) {
-                       struct sockaddr_storage remote_saddr;
-                       socklen_t remote_saddrlen;
-                       struct sockaddr_storage local_saddr;
-                       socklen_t local_saddrlen;
-                       unsigned char *local, *remote;
-                       int local_len, remote_len;
-                       int fd;
-
-                       remote_saddrlen = sizeof(remote_saddr);
-                       fd = accept(httpd->server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen);
-                       if (fd == -1) {
-                               /* FIXME: Error happened */
+               if (FD_ISSET(httpd->server_fd4, &rfds)) {
+                       ret = httpd_accept_connection(httpd, httpd->server_fd4, 0);
+                       if (ret == -1) {
                                break;
+                       } else if (ret == 0) {
+                               continue;
                        }
-
-                       local_saddrlen = sizeof(local_saddr);
-                       ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen);
+               }
+               if (FD_ISSET(httpd->server_fd6, &rfds)) {
+                       ret = httpd_accept_connection(httpd, httpd->server_fd6, 1);
                        if (ret == -1) {
-                               closesocket(fd);
+                               break;
+                       } else if (ret == 0) {
                                continue;
                        }
-
-                       logger_log(httpd->logger, LOGGER_INFO, "Accepted client on socket %d", fd);
-                       local = netutils_get_address(&local_saddr, &local_len);
-                       remote = netutils_get_address(&remote_saddr, &remote_len);
-
-                       httpd_add_connection(httpd, fd, local, local_len, remote, remote_len);
                }
                for (i=0; i<httpd->max_connections; i++) {
                        http_connection_t *connection = &httpd->connections[i];
@@ -315,23 +343,33 @@ httpd_start(httpd_t *httpd, unsigned short *port)
                return 0;
        }
 
-       httpd->server_fd = netutils_init_socket(port, 1, 0);
-       if (httpd->server_fd == -1) {
-               logger_log(httpd->logger, LOGGER_INFO, "Error initialising IPv6 socket %d", SOCKET_GET_ERROR());
-               logger_log(httpd->logger, LOGGER_INFO, "Attempting to fall back to IPv4");
-               httpd->server_fd = netutils_init_socket(port, 0, 0);
-       }
-       if (httpd->server_fd == -1) {
-               logger_log(httpd->logger, LOGGER_INFO, "Error initialising socket %d", SOCKET_GET_ERROR());
+       httpd->server_fd4 = netutils_init_socket(port, 0, 0);
+       if (httpd->server_fd4 == -1) {
+               logger_log(httpd->logger, LOGGER_ERR, "Error initialising socket %d", SOCKET_GET_ERROR());
                MUTEX_UNLOCK(httpd->run_mutex);
                return -1;
        }
-       if (listen(httpd->server_fd, 5) == -1) {
-               logger_log(httpd->logger, LOGGER_INFO, "Error listening to socket");
+       httpd->server_fd6 = netutils_init_socket(port, 1, 0);
+       if (httpd->server_fd6 == -1) {
+               logger_log(httpd->logger, LOGGER_WARNING, "Error initialising IPv6 socket %d", SOCKET_GET_ERROR());
+               logger_log(httpd->logger, LOGGER_WARNING, "Continuing without IPv6 support");
+       }
+
+       if (listen(httpd->server_fd4, 5) == -1) {
+               logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv4 socket");
+               closesocket(httpd->server_fd4);
+               closesocket(httpd->server_fd6);
+               MUTEX_UNLOCK(httpd->run_mutex);
+               return -2;
+       }
+       if (httpd->server_fd6 != -1 && listen(httpd->server_fd6, 5) == -1) {
+               logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv6 socket");
+               closesocket(httpd->server_fd4);
+               closesocket(httpd->server_fd6);
                MUTEX_UNLOCK(httpd->run_mutex);
                return -2;
        }
-       logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket");
+       logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket(s)");
 
        /* Set values correctly and create new thread */
        httpd->running = 1;
index 8c7c5f74914b786f77432eef750e8133dce22c81..180d32eed7f6b510880a9a6b1fe4c75351536d74 100644 (file)
@@ -71,19 +71,16 @@ netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
        memset(&saddr, 0, sizeof(saddr));
        if (use_ipv6) {
                struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
-               int v6only = 0;
+               int v6only = 1;
 
                /* Initialize sockaddr for bind */
                sin6ptr->sin6_family = family;
                sin6ptr->sin6_addr = in6addr_any;
                sin6ptr->sin6_port = htons(*port);
 
-               /* Make sure we also listen to IPv4 addresses */
-               ret = setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
-                                (char *) &v6only, sizeof(v6only));
-               if (ret == -1) {
-                       goto cleanup;
-               }
+               /* Make sure we only listen to IPv6 addresses */
+               setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
+                          (char *) &v6only, sizeof(v6only));
 
                socklen = sizeof(*sin6ptr);
                ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);