mirror of
https://github.com/apache/httpd.git
synced 2025-07-29 12:37:06 +00:00
Add optional options= argument to Listen to add listener-specific
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
This commit is contained in:
4
CHANGES
4
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]
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -628,14 +628,15 @@
|
||||
* 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
102
server/listen.c
102
server/listen.c
@ -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,
|
||||
|
Reference in New Issue
Block a user