]> granicus.if.org Git - apache/commitdiff
core: Fix address-in-use startup failure on some platforms caused
authorJeff Trawick <trawick@apache.org>
Sun, 8 Jun 2008 18:13:35 +0000 (18:13 +0000)
committerJeff Trawick <trawick@apache.org>
Sun, 8 Jun 2008 18:13:35 +0000 (18:13 +0000)
by attempting to set up an IPv4 listener which overlaps with an
existing IPv6 listener.

The failure occurred on the second pass of the open-logs hook in
a configuration such as the following:

  Listen 8080
  Listen 0.0.0.0:8081
  Listen [::]:8081

During the first pass, the two port 8081 listen recs were
adjacent and existing logic prevented binding to 0.0.0.0:8081.
On the second pass, they were not adjacent and we then tried
to bind to 0.0.0.0:8081, leading to failure on some platforms
(seen on SLES 9 and Ubuntu 7.10, not seen on many other Unix-ish
platforms).

Leave a note about other unhandled configurations.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@664535 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
server/listen.c

diff --git a/CHANGES b/CHANGES
index 8b07da76d5fdf56c39f23458a92f2ae4f3c1c01d..0256f6bb9b1a7ab58849c29ef491c09e330819a2 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,10 @@
 Changes with Apache 2.3.0
 [ When backported to 2.2.x, remove entry from this file ]
 
+  *) core: Fix address-in-use startup failure on some platforms caused
+     by creating an IPv4 listener which overlaps with an existing IPv6
+     listener.  [Jeff Trawick]
+
   *) mod_proxy_http: Do not forward requests with 'Expect: 100-continue' to
      known HTTP/1.0 servers. Return 'Expectation failed' (417) instead.
      [Ruediger Pluem]
index f2d78f576254f16556248d41d48e205f126bcf00..eff2751075a6227225444f68892d5fee863645af 100644 (file)
@@ -376,14 +376,22 @@ static int open_listeners(apr_pool_t *pool)
         }
         else {
 #if APR_HAVE_IPV6
+            ap_listen_rec *cur;
             int v6only_setting;
+            int skip = 0;
 
             /* If we have the unspecified IPv4 address (0.0.0.0) and
              * the unspecified IPv6 address (::) is next, we need to
              * swap the order of these in the list. We always try to
              * bind to IPv6 first, then IPv4, since an IPv6 socket
              * might be able to receive IPv4 packets if V6ONLY is not
-             * enabled, but never the other way around. */
+             * enabled, but never the other way around.
+             * Note: In some configurations, the unspecified IPv6 address
+             * could be even later in the list.  This logic only corrects
+             * the situation where it is next in the list, such as when
+             * apr_sockaddr_info_get() returns an IPv4 and an IPv6 address,
+             * in that order.
+             */
             if (lr->next != NULL
                 && IS_INADDR_ANY(lr->bind_addr)
                 && lr->bind_addr->port == lr->next->bind_addr->port
@@ -401,26 +409,32 @@ static int open_listeners(apr_pool_t *pool)
                 lr = next;
             }
 
-            /* If we are trying to bind to 0.0.0.0 and the previous listener
+            /* If we are trying to bind to 0.0.0.0 and a previous listener
              * was :: on the same port and in turn that socket does not have
              * the IPV6_V6ONLY flag set; we must skip the current attempt to
              * listen (which would generate an error). IPv4 will be handled
              * on the established IPv6 socket.
              */
-            if (previous != NULL
-                && IS_INADDR_ANY(lr->bind_addr)
-                && lr->bind_addr->port == previous->bind_addr->port
-                && IS_IN6ADDR_ANY(previous->bind_addr)
-                && apr_socket_opt_get(previous->sd, APR_IPV6_V6ONLY,
-                                      &v6only_setting) == APR_SUCCESS
-                && v6only_setting == 0) {
-
-                /* Remove the current listener from the list */
-                previous->next = lr->next;
-                lr = previous; /* maintain current value of previous after
-                                * post-loop expression is evaluated
-                                */
-                continue;
+            if (IS_INADDR_ANY(lr->bind_addr)) {
+                for (cur = ap_listeners; cur != lr; cur = cur->next) {
+                    if (lr->bind_addr->port == cur->bind_addr->port
+                        && IS_IN6ADDR_ANY(cur->bind_addr)
+                        && apr_socket_opt_get(cur->sd, APR_IPV6_V6ONLY,
+                                              &v6only_setting) == APR_SUCCESS
+                        && v6only_setting == 0) {
+
+                        /* Remove the current listener from the list */
+                        previous->next = lr->next;
+                        lr = previous; /* maintain current value of previous after
+                                        * post-loop expression is evaluated
+                                        */
+                        skip = 1;
+                        break;
+                    }
+                }
+                if (skip) {
+                    continue;
+                }
             }
 #endif
             if (make_sock(pool, lr) == APR_SUCCESS) {