-/* 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;
"make_sock: failed to set ReceiveBufferSize for "
"address %pI, using default",
server->bind_addr);
- strerror(errno);
/* not a fatal error */
}
}
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;
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;
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;
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
}
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) {
* 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) {
* 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 */
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;
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);
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";
}
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,