]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Add optional options= argument to Listen to add listener-specific
authorJoe Orton <jorton@apache.org>
Thu, 23 Apr 2020 08:26:26 +0000 (08:26 +0000)
committerJoe Orton <jorton@apache.org>
Thu, 23 Apr 2020 08:26:26 +0000 (08:26 +0000)
socket options.

Reimplement "use_specific_errors" listener flag under generic
ap_listen_rec flags field holding all listener-specific options.

* include/ap_listen.h: Add AP_LISTEN_* flags.
  (ap_listen_rec): Rename use_specific_errors to flags.

* server/listen.c (make_sock): Set APR_SO_FREEBIND if
  AP_LISTEN_FREEBIND flag is set on listener; set APR_SO_REUSEPORT
  unconditionally if AP_LISTEN_REUSEPORT is set.
  (alloc_listener): Take flags argument.
  (ap_setup_listeners): Set AP_LISTEN_SPECIFIC_ERRORS flag here.
  (ap_set_listener): Parse optional options=... argument, catch
  typos and fail if protocol name contains a "=".
  (ap_duplicate_listeners): Duplicate flags.

Submitted by: jkaluza, Lubos Uhliarik <luhliari redhat.com>, jorton
PR: 61865
Github: closes #114

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

CHANGES
docs/manual/mod/mpm_common.xml
include/ap_listen.h
include/ap_mmn.h
os/unix/unixd.c
server/listen.c

diff --git a/CHANGES b/CHANGES
index bc24d2408f5fd52d3fcf3d6a7f7e3abc9f6918d6..f6aae5ee536a47d0606c086990287b8e8e054528 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,10 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.1
 
+  *) core: Add optional "options=" argument to Listen.  Supported
+     keywords are "freebind" and "reuseport".  PR 61865.
+     [Jan Kaluza, Lubos Uhliarik <luhliari redhat.com>, Joe Orton]
+
   *) config: Allow for environment variable substitution with default value,
      for when the variable is not defined, using format ${VAR?=default value}.
      [Yann Ylavic]
index ecf242aa72645fb9ecf8d11ec69b17547cb09987..be8a2ff5b2fc1c971f9b7abb3e07ecb5098c1706 100644 (file)
@@ -182,12 +182,13 @@ of the daemon</description>
 <name>Listen</name>
 <description>IP addresses and ports that the server
 listens to</description>
-<syntax>Listen [<var>IP-address</var>:]<var>portnumber</var> [<var>protocol</var>]</syntax>
+<syntax>Listen [<var>IP-address</var>:]<var>portnumber</var> [<var>protocol</var>] [options=<var>flag</var>[,<var>flag..</var>]]</syntax>
 <contextlist><context>server config</context></contextlist>
 <modulelist><module>event</module><module>worker</module>
 <module>prefork</module><module>mpm_winnt</module>
 <module>mpm_netware</module><module>mpmt_os2</module>
 </modulelist>
+<compatibility>The optional <code>options=</code> argument is available in httpd 2.5.1 and later.</compatibility>
 
 <usage>
     <p>The <directive>Listen</directive> directive instructs Apache httpd to
@@ -254,8 +255,25 @@ Listen 192.170.2.5:8000
       Listen 192.170.2.1:8443 https
     </highlight>
 
+    <p>The optional <var>options=flag,flag...</var> argument can be
+    used to enable certain socket options for the listening port.
+    These options are not required for most configurations and should
+    be used with care.  Availability of each flag varies across
+    operating systems.  The available <em>flag</em>s are:</p>
+
+    <ul>
+      <li><code>freebind</code>: The <code>IP_FREEBIND</code> socket
+      option is enabled, allowing a Listen directive to be used for an
+      address which is not (yet) available on the system. (Linux
+      only)</li>
+
+      <li><code>reuseport</code>: The <code>SO_REUSEPORT</code> socket
+      option is enabled, allowing a Listen directive to bind to a port
+      which may already be in use by another process.</li>
+    </ul>
+       
     <note><title>Error condition</title>
-      Multiple <directive>Listen</directive> directives for the same ip
+      Multiple <directive>Listen</directive> directives for the same IP
       address and port will result in an <code>Address already in use</code>
       error message.
     </note>
index 9cbaaa491097451b2da932939c1cdc8c426b2b41..2329cae70cbd8f338296f8287f4cf3da4edbab93 100644 (file)
@@ -38,6 +38,11 @@ typedef struct ap_slave_t ap_slave_t;
 typedef struct ap_listen_rec ap_listen_rec;
 typedef apr_status_t (*accept_function)(void **csd, ap_listen_rec *lr, apr_pool_t *ptrans);
 
+/* Flags for ap_listen_rec.flags */
+#define AP_LISTEN_SPECIFIC_ERRORS (0x0001)
+#define AP_LISTEN_FREEBIND        (0x0002)
+#define AP_LISTEN_REUSEPORT       (0x0004)
+
 /**
  * @brief Apache's listeners record.
  *
@@ -73,10 +78,9 @@ struct ap_listen_rec {
     ap_slave_t *slave;
 
     /**
-     * Allow the accept_func to return a wider set of return codes
+     * Various AP_LISTEN_* flags.
      */
-    int use_specific_errors;
-
+    apr_uint32_t flags;
 };
 
 /**
index 24ac648ac9472cf726d05e33e1cbd217f1bce9a4..1271ce18ed60e77d48cc5a2ffeb88551a791331f 100644 (file)
  * 20200331.2 (2.5.1-dev)  Add ap_proxy_should_override to mod_proxy.h
  * 20200331.3 (2.5.1-dev)  Add ap_parse_request_line() and
  *                         ap_check_request_header()
+ * 20200420.0 (2.5.1-dev)  Add flags to listen_rec in place of use_specific_errors
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
 
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
-#define MODULE_MAGIC_NUMBER_MAJOR 20200331
+#define MODULE_MAGIC_NUMBER_MAJOR 20200420
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 3            /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 0            /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index bde859022bd65a6f3db78321040f268b1447c2df..3b0e695727845df30af6d0f3acbb2fd41bddcf6f 100644 (file)
@@ -323,7 +323,7 @@ AP_DECLARE(apr_status_t) ap_unixd_accept(void **accepted, ap_listen_rec *lr,
     }
   
     /* Let the caller handle slightly more varied return values */
-    if (lr->use_specific_errors && ap_accept_error_is_nonfatal(status)) { 
+    if ((lr->flags & AP_LISTEN_SPECIFIC_ERRORS) && ap_accept_error_is_nonfatal(status)) {
         return status;
     }
 
index 87a4bafe80c18051eeee2cc08b2378303a181c4d..d4f9936828d5d5a7493020d4db30c60407600322 100644 (file)
@@ -150,7 +150,8 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server, int do_bind_
 #endif
 
 #if defined(SO_REUSEPORT)
-    if (ap_have_so_reuseport && ap_listencbratio > 0) {
+    if (server->flags & AP_LISTEN_REUSEPORT
+        || (ap_have_so_reuseport && ap_listencbratio > 0)) {
         int thesock;
         apr_os_sock_get(&thesock, s);
         if (setsockopt(thesock, SOL_SOCKET, SO_REUSEPORT,
@@ -166,6 +167,21 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server, int do_bind_
     }
 #endif
 
+
+#if defined(APR_SO_FREEBIND)
+    if (server->flags & AP_LISTEN_FREEBIND) {
+        if (apr_socket_opt_set(s, APR_SO_FREEBIND, one) < 0) {
+            stat = apr_get_netos_error();
+            ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO()
+                          "make_sock: apr_socket_opt_set: "
+                          "error setting APR_SO_FREEBIND");
+            apr_socket_close(s);
+            return stat;
+        }
+    }
+#endif
+
+
     if (do_bind_listen) {
 #if APR_HAVE_IPV6
         if (server->bind_addr->family == APR_INET6) {
@@ -467,7 +483,7 @@ static int find_listeners(ap_listen_rec **from, ap_listen_rec **to,
 static const char *alloc_listener(process_rec *process, const char *addr,
                                   apr_port_t port, const char* proto,
                                   const char *scope_id, void *slave,
-                                  apr_pool_t *temp_pool)
+                                  apr_pool_t *temp_pool, apr_uint32_t flags)
 {
     ap_listen_rec *last;
     apr_status_t status;
@@ -511,6 +527,7 @@ static const char *alloc_listener(process_rec *process, const char *addr,
         new->next = 0;
         new->bind_addr = sa;
         new->protocol = apr_pstrdup(process->pool, proto);
+        new->flags = flags;
 
         /* Go to the next sockaddr. */
         sa = sa->next;
@@ -795,7 +812,7 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
     }
 
     for (lr = ap_listeners; lr; lr = lr->next) {
-        lr->use_specific_errors = ap_accept_errors_nonfatal;
+        if (ap_accept_errors_nonfatal) lr->flags |= AP_LISTEN_SPECIFIC_ERRORS;
         num_listeners++;
         found = 0;
         for (ls = s; ls && !found; ls = ls->next) {
@@ -885,6 +902,7 @@ AP_DECLARE(apr_status_t) ap_duplicate_listeners(apr_pool_t *p, server_rec *s,
                 apr_sockaddr_info_get(&sa, hostname, APR_UNSPEC, port, 0, p);
                 duplr->bind_addr = sa;
                 duplr->next = NULL;
+                duplr->flags = lr->flags;
                 stat = apr_socket_create(&duplr->sd, duplr->bind_addr->family,
                                          SOCK_STREAM, 0, p);
                 if (stat != APR_SUCCESS) {
@@ -1015,20 +1033,48 @@ AP_DECLARE(int) ap_accept_error_is_nonfatal(apr_status_t status)
              || APR_STATUS_IS_ECONNRESET(status);
 }
 
+/* Parse optional flags argument for Listen.  Currently just boolean
+ * flags handled; would need to be extended to incorporate
+ * ListenBacklog */
+static const char *parse_listen_flags(apr_pool_t *temp_pool, const char *arg,
+                                      apr_uint32_t *flags_out)
+{
+    apr_uint32_t flags = 0;
+    char *str = apr_pstrdup(temp_pool, arg), *token, *state = NULL;
+
+    token = apr_strtok(str, ",", &state);
+    while (token) {
+        if (ap_cstr_casecmp(token, "freebind") == 0)
+            flags |= AP_LISTEN_FREEBIND;
+        else if (ap_cstr_casecmp(token, "reuseport") == 0)
+            flags |= AP_LISTEN_REUSEPORT;
+        else
+            return apr_psprintf(temp_pool, "Unknown Listen option '%s' in '%s'",
+                                token, arg);
+
+        token = apr_strtok(NULL, ",", &state);
+    }
+
+    *flags_out = flags;
+
+    return NULL;
+}
+
 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
                                                 int argc, char *const argv[])
 {
-    char *host, *scope_id, *proto;
+    char *host, *scope_id, *proto = NULL;
     apr_port_t port;
     apr_status_t rv;
     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    apr_uint32_t flags = 0;
 
     if (err != NULL) {
         return err;
     }
 
-    if (argc < 1 || argc > 2) {
-        return "Listen requires 1 or 2 arguments.";
+    if (argc < 1 || argc > 3) {
+        return "Listen requires 1-3 arguments.";
     }
 #ifdef HAVE_SYSTEMD
     if (use_systemd == -1) {
@@ -1058,17 +1104,49 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
         return "Port must be specified";
     }
 
-    if (argc != 2) {
+    if (argc == 3) {
+        if (strncasecmp(argv[2], "options=", 8)) {
+            return "Third argument to Listen must be options=...";
+        }
+
+        err = parse_listen_flags(cmd->temp_pool, argv[2] + 8, &flags);
+        if (err) {
+            return err;
+        }
+
+        proto = argv[1];
+    }
+
+    if (argc == 2) {
+        /* 2-arg form is either 'Listen host:port options=...' or
+         * 'Listen host:port protocol' */
+        if (strncasecmp(argv[1], "options=", 8) == 0) {
+            err = parse_listen_flags(cmd->temp_pool, argv[1] + 8, &flags);
+            if (err) {
+                return err;
+            }
+        }
+        else {
+            proto = argv[1];
+        }
+    }
+
+    /* Catch case where 2-arg form has typoed options=X and doesn't
+     * match above. */
+    if (proto && ap_strchr_c(proto, '=') != NULL) {
+        return apr_psprintf(cmd->pool, "Invalid protocol name '%s'", proto);
+    }
+    else if (proto) {
+        proto = apr_pstrdup(cmd->pool, proto);
+        ap_str_tolower(proto);
+    }
+    else {
         if (port == 443) {
             proto = "https";
         } else {
             proto = "http";
         }
     }
-    else {
-        proto = apr_pstrdup(cmd->pool, argv[1]);
-        ap_str_tolower(proto);
-    }
 
 #ifdef HAVE_SYSTEMD
     if (use_systemd) {
@@ -1077,7 +1155,7 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
 #endif
 
     return alloc_listener(cmd->server->process, host, port, proto,
-                          scope_id, NULL, cmd->temp_pool);
+                          scope_id, NULL, cmd->temp_pool, flags);
 }
 
 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,