]> granicus.if.org Git - apache/blobdiff - server/listen.c
Catch up to apr_dbm projects
[apache] / server / listen.c
index 6a17e43dd84c03c5f9bb4997cfff3eb87b183e1f..8361fb3c35d132f5443886c0bb3746f31b77d0e6 100644 (file)
@@ -1,9 +1,9 @@
-/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
- * applicable.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
 #define APR_WANT_STRFUNC
 #include "apr_want.h"
 
-#define CORE_PRIVATE
 #include "ap_config.h"
 #include "httpd.h"
 #include "http_config.h"
+#include "http_core.h"
 #include "ap_listen.h"
 #include "http_log.h"
-#include "mpm.h"
 #include "mpm_common.h"
 
 AP_DECLARE_DATA ap_listen_rec *ap_listeners = NULL;
@@ -120,7 +119,6 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
                           "make_sock: failed to set ReceiveBufferSize for "
                           "address %pI, using default",
                           server->bind_addr);
-            strerror(errno);
             /* not a fatal error */
         }
     }
@@ -160,65 +158,83 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
     stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
         ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
-                    "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)", 
+                    "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
                      server->bind_addr);
         apr_socket_close(s);
         return stat;
     }
 #endif
 
-#if APR_HAS_SO_ACCEPTFILTER
-#ifndef ACCEPT_FILTER_NAME
-#define ACCEPT_FILTER_NAME "httpready"
-#ifdef __FreeBSD_version
-#if __FreeBSD_version < 411000 /* httpready broken before 4.1.1 */
-#undef ACCEPT_FILTER_NAME
-#define ACCEPT_FILTER_NAME "dataready"
-#endif
-#endif
-#endif
-    stat = apr_socket_accept_filter(s, ACCEPT_FILTER_NAME, "");
-    if (stat != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(stat)) {
-        ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
-                      "Failed to enable the '%s' Accept Filter",
-                      ACCEPT_FILTER_NAME);
-    }
-#else
-#ifdef APR_TCP_DEFER_ACCEPT
-    stat = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 1);   
-    if (stat != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(stat)) {
-        ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
-                              "Failed to enable APR_TCP_DEFER_ACCEPT");
-    }
-#endif
-#endif
-
     server->sd = s;
     server->active = 1;
 
-#ifdef MPM_ACCEPT_FUNC
-    server->accept_func = MPM_ACCEPT_FUNC;
-#else
     server->accept_func = NULL;
-#endif
 
     return APR_SUCCESS;
 }
 
-static apr_status_t close_listeners_on_exec(void *v)
+static const char* find_accf_name(server_rec *s, const char *proto)
 {
-    ap_listen_rec *lr;
+    const char* accf;
+    core_server_config *conf = ap_get_module_config(s->module_config,
+                                                    &core_module);
+    if (!proto) {
+        return NULL;
+    }
 
-    for (lr = ap_listeners; lr; lr = lr->next) {
-        apr_socket_close(lr->sd);
-        lr->active = 0;
+    accf = apr_table_get(conf->accf_map, proto);
+
+    if (accf && !strcmp("none", accf)) {
+        return NULL;
     }
 
-    return APR_SUCCESS;
+    return accf;
 }
 
+static void ap_apply_accept_filter(apr_pool_t *p, ap_listen_rec *lis,
+                                           server_rec *server)
+{
+    apr_socket_t *s = lis->sd;
+    const char *accf;
+    apr_status_t rv;
+    const char *proto;
+
+    proto = lis->protocol;
+
+    if (!proto) {
+        proto = ap_get_server_protocol(server);
+    }
+
 
-static const char *alloc_listener(process_rec *process, char *addr, apr_port_t port)
+    accf = find_accf_name(server, proto);
+
+    if (accf) {
+#if APR_HAS_SO_ACCEPTFILTER
+        rv = apr_socket_accept_filter(s, apr_pstrdup(p, accf),
+                                      apr_pstrdup(p,""));
+        if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+                          "Failed to enable the '%s' Accept Filter",
+                          accf);
+        }
+#else
+        rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
+        if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+                              "Failed to enable APR_TCP_DEFER_ACCEPT");
+        }
+#endif
+    }
+}
+
+static apr_status_t close_listeners_on_exec(void *v)
+{
+    ap_close_listeners();
+    return APR_SUCCESS;
+}
+
+static const char *alloc_listener(process_rec *process, char *addr,
+                                  apr_port_t port, const char* proto)
 {
     ap_listen_rec **walk, *last;
     apr_status_t status;
@@ -279,6 +295,7 @@ static const char *alloc_listener(process_rec *process, char *addr, apr_port_t p
         new->active = 0;
         new->next = 0;
         new->bind_addr = sa;
+        new->protocol = apr_pstrdup(process->pool, proto);
 
         /* Go to the next sockaddr. */
         sa = sa->next;
@@ -313,15 +330,22 @@ static const char *alloc_listener(process_rec *process, char *addr, apr_port_t p
 
     return NULL;
 }
+/* Evaluates to true if the (apr_sockaddr_t *) addr argument is the
+ * IPv4 match-any-address, 0.0.0.0. */
+#define IS_INADDR_ANY(addr) ((addr)->family == APR_INET \
+                             && (addr)->sa.sin.sin_addr.s_addr == INADDR_ANY)
+
+/* Evaluates to true if the (apr_sockaddr_t *) addr argument is the
+ * IPv6 match-any-address, [::]. */
+#define IS_IN6ADDR_ANY(addr) ((addr)->family == APR_INET6 \
+                              && IN6_IS_ADDR_UNSPECIFIED(&(addr)->sa.sin6.sin6_addr))
 
 /**
- * Create and open a socket on the specified port.  This includes listening
- * and binding the socket.
+ * Create, open, listen, and bind all sockets.
  * @param process The process record for the currently running server
- * @param port The port to open a socket on.
  * @return The number of open sockets
  */
-static int open_listeners(apr_pool_t *pool, apr_port_t port)
+static int open_listeners(apr_pool_t *pool)
 {
     ap_listen_rec *lr;
     ap_listen_rec *next;
@@ -329,6 +353,9 @@ static int open_listeners(apr_pool_t *pool, apr_port_t port)
     int num_open;
     const char *userdata_key = "ap_open_listeners";
     void *data;
+#if AP_NONBLOCK_WHEN_MULTI_LISTEN
+    int use_nonblock;
+#endif
 
     /* Don't allocate a default listener.  If we need to listen to a
      * port, then the user needs to have a Listen directive in their
@@ -342,27 +369,65 @@ static int open_listeners(apr_pool_t *pool, apr_port_t port)
         }
         else {
 #if APR_HAVE_IPV6
+            ap_listen_rec *cur;
             int v6only_setting;
-            /* If we are trying to bind to 0.0.0.0 and the previous listener
+            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.
+             * 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
+                && IS_IN6ADDR_ANY(lr->next->bind_addr)) {
+                /* Exchange lr and lr->next */
+                next = lr->next;
+                lr->next = next->next;
+                next->next = lr;
+                if (previous) {
+                    previous->next = next;
+                }
+                else {
+                    ap_listeners = next;
+                }
+                lr = next;
+            }
+
+            /* 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 &&
-                lr->bind_addr->family == APR_INET &&
-                lr->bind_addr->sa.sin.sin_addr.s_addr == INADDR_ANY &&
-                lr->bind_addr->port == previous->bind_addr->port &&
-                previous->bind_addr->family == APR_INET6 &&
-                IN6_IS_ADDR_UNSPECIFIED(
-                    &previous->bind_addr->sa.sin6.sin6_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;
-                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) {
@@ -376,12 +441,10 @@ static int open_listeners(apr_pool_t *pool, apr_port_t port)
                  * error. The user will still get a warning from make_sock
                  * though.
                  */
-                if (lr->next != NULL && lr->bind_addr->family == APR_INET6 &&
-                    IN6_IS_ADDR_UNSPECIFIED(
-                        &lr->bind_addr->sa.sin6.sin6_addr) &&
-                    lr->bind_addr->port == lr->next->bind_addr->port &&
-                    lr->next->bind_addr->family == APR_INET && 
-                    lr->next->bind_addr->sa.sin.sin_addr.s_addr == INADDR_ANY) {
+                if (lr->next != NULL
+                    && IS_IN6ADDR_ANY(lr->bind_addr)
+                    && lr->bind_addr->port == lr->next->bind_addr->port
+                    && IS_INADDR_ANY(lr->next->bind_addr)) {
 
                     /* Remove the current listener from the list */
                     if (previous) {
@@ -422,16 +485,15 @@ static int open_listeners(apr_pool_t *pool, apr_port_t port)
      * is already forgotten about by the time we call accept, we won't
      * be hung until another connection arrives on that port
      */
-    if (ap_listeners && ap_listeners->next) {
-        for (lr = ap_listeners; lr; lr = lr->next) {
-            apr_status_t status;
-
-            status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, 1);
-            if (status != APR_SUCCESS) {
-                ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool,
-                              "unable to make listening socket non-blocking");
-                return -1;
-            }
+    use_nonblock = (ap_listeners && ap_listeners->next);
+    for (lr = ap_listeners; lr; lr = lr->next) {
+        apr_status_t status;
+
+        status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, use_nonblock);
+        if (status != APR_SUCCESS) {
+            ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool,
+                          "unable to control socket non-blocking status");
+            return -1;
         }
     }
 #endif /* AP_NONBLOCK_WHEN_MULTI_LISTEN */
@@ -453,20 +515,73 @@ static int open_listeners(apr_pool_t *pool, apr_port_t port)
 
 AP_DECLARE(int) ap_setup_listeners(server_rec *s)
 {
+    server_rec *ls;
+    server_addr_rec *addr;
     ap_listen_rec *lr;
     int num_listeners = 0;
+    const char* proto;
+    int found;
+
+    for (ls = s; ls; ls = ls->next) {
+        proto = ap_get_server_protocol(ls);
+        if (!proto) {
+            found = 0;
+            /* No protocol was set for this vhost,
+             * use the default for this listener.
+             */
+            for (addr = ls->addrs; addr && !found; addr = addr->next) {
+                for (lr = ap_listeners; lr; lr = lr->next) {
+                    if (apr_sockaddr_equal(lr->bind_addr, addr->host_addr) &&
+                        lr->bind_addr->port == addr->host_port) {
+                        ap_set_server_protocol(ls, lr->protocol);
+                        found = 1;
+                        break;
+                    }
+                }
+            }
+
+            if (!found) {
+                /* TODO: set protocol defaults per-Port, eg 25=smtp */
+                ap_set_server_protocol(ls, "http");
+            }
+        }
+    }
 
-    if (open_listeners(s->process->pool, s->port)) {
+    if (open_listeners(s->process->pool)) {
        return 0;
     }
 
     for (lr = ap_listeners; lr; lr = lr->next) {
         num_listeners++;
+        found = 0;
+        for (ls = s; ls && !found; ls = ls->next) {
+            for (addr = ls->addrs; addr && !found; addr = addr->next) {
+                if (apr_sockaddr_equal(lr->bind_addr, addr->host_addr) &&
+                    lr->bind_addr->port == addr->host_port) {
+                    found = 1;
+                    ap_apply_accept_filter(s->process->pool, lr, ls);
+                }
+            }
+        }
+
+        if (!found) {
+            ap_apply_accept_filter(s->process->pool, lr, s);
+        }
     }
 
     return num_listeners;
 }
 
+AP_DECLARE_NONSTD(void) ap_close_listeners(void)
+{
+    ap_listen_rec *lr;
+
+    for (lr = ap_listeners; lr; lr = lr->next) {
+        apr_socket_close(lr->sd);
+        lr->active = 0;
+    }
+}
+
 AP_DECLARE(void) ap_listen_pre_config(void)
 {
     old_listeners = ap_listeners;
@@ -476,9 +591,9 @@ AP_DECLARE(void) ap_listen_pre_config(void)
 
 
 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
-                                                const char *ips)
+                                                int argc, char *const argv[])
 {
-    char *host, *scope_id;
+    char *host, *scope_id, *proto;
     apr_port_t port;
     apr_status_t rv;
     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
@@ -487,7 +602,11 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
         return err;
     }
 
-    rv = apr_parse_addr_port(&host, &scope_id, &port, ips, cmd->pool);
+    if (argc < 1 || argc > 2) {
+        return "Listen requires 1 or 2 arguments.";
+    }
+
+    rv = apr_parse_addr_port(&host, &scope_id, &port, argv[0], cmd->pool);
     if (rv != APR_SUCCESS) {
         return "Invalid address or port";
     }
@@ -505,7 +624,19 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
         return "Port must be specified";
     }
 
-    return alloc_listener(cmd->server->process, host, port);
+    if (argc != 2) {
+        if (port == 443) {
+            proto = "https";
+        } else {
+            proto = "http";
+        }
+    }
+    else {
+        proto = apr_pstrdup(cmd->pool, argv[1]);
+        ap_str_tolower(proto);
+    }
+
+    return alloc_listener(cmd->server->process, host, port, proto);
 }
 
 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,