]> granicus.if.org Git - apache/blob - server/listen.c
Enhancements for APR network_io. Adds separate local/remote access methods for
[apache] / server / listen.c
1 /* ====================================================================
2  * Copyright (c) 1998-1999 The Apache Group.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer. 
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * 3. All advertising materials mentioning features or use of this
17  *    software must display the following acknowledgment:
18  *    "This product includes software developed by the Apache Group
19  *    for use in the Apache HTTP server project (http://www.apache.org/)."
20  *
21  * 4. The names "Apache Server" and "Apache Group" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission. For written permission, please contact
24  *    apache@apache.org.
25  *
26  * 5. Products derived from this software may not be called "Apache"
27  *    nor may "Apache" appear in their names without prior written
28  *    permission of the Apache Group.
29  *
30  * 6. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by the Apache Group
33  *    for use in the Apache HTTP server project (http://www.apache.org/)."
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
36  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
41  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46  * OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Group and was originally based
51  * on public domain software written at the National Center for
52  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
53  * For more information on the Apache Group and the Apache HTTP server
54  * project, please see <http://www.apache.org/>.
55  *
56  */
57
58 #include "apr_network_io.h"
59 #include "httpd.h"
60 #include "http_config.h"
61 #include "ap_listen.h"
62 #include "http_log.h"
63
64 ap_listen_rec *ap_listeners;
65 static ap_listen_rec *old_listeners;
66 static int ap_listenbacklog;
67 static int send_buffer_size;
68
69 /* TODO: make_sock is just begging and screaming for APR abstraction */
70 static ap_status_t make_sock(ap_context_t *p, ap_listen_rec *server)
71 {
72     ap_socket_t *s = server->sd;
73     int one = 1;
74     char addr[512];
75     ap_status_t stat;
76
77     ap_cpystrn(addr, "[@main/listen.c:make_sock(): inet_ntoa(server->sin_addr)]", sizeof addr);
78
79     stat = ap_setsocketopt(s, APR_SO_REUSEADDR, one);
80     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
81         ap_log_error(APLOG_MARK, APLOG_CRIT, stat, NULL,
82                     "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr);
83         ap_close_socket(s);
84         return stat;
85     }
86     
87     stat = ap_setsocketopt(s, APR_SO_KEEPALIVE, one);
88     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
89         ap_log_error(APLOG_MARK, APLOG_CRIT, stat, NULL,
90                     "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr);
91         ap_close_socket(s);
92         return stat;
93     }
94
95     /*
96      * To send data over high bandwidth-delay connections at full
97      * speed we must force the TCP window to open wide enough to keep the
98      * pipe full.  The default window size on many systems
99      * is only 4kB.  Cross-country WAN connections of 100ms
100      * at 1Mb/s are not impossible for well connected sites.
101      * If we assume 100ms cross-country latency,
102      * a 4kB buffer limits throughput to 40kB/s.
103      *
104      * To avoid this problem I've added the SendBufferSize directive
105      * to allow the web master to configure send buffer size.
106      *
107      * The trade-off of larger buffers is that more kernel memory
108      * is consumed.  YMMV, know your customers and your network!
109      *
110      * -John Heidemann <johnh@isi.edu> 25-Oct-96
111      *
112      * If no size is specified, use the kernel default.
113      */
114     if (send_buffer_size) {
115         stat = ap_setsocketopt(s, APR_SO_SNDBUF,  send_buffer_size);
116         if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
117             ap_log_error(APLOG_MARK, APLOG_WARNING, stat, NULL,
118                         "make_sock: failed to set SendBufferSize for %s, "
119                         "using default", addr);
120             /* not a fatal error */
121         }
122     }
123
124     if ((stat = ap_bind(s)) != APR_SUCCESS) {
125         ap_log_error(APLOG_MARK, APLOG_CRIT, stat, NULL,
126             "make_sock: could not bind to %s", addr);
127         ap_close_socket(s);
128         return stat;
129     }
130
131     if ((stat = ap_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
132         ap_log_error(APLOG_MARK, APLOG_ERR, stat, NULL,
133             "make_sock: unable to listen for connections on %s", addr);
134         ap_close_socket(s);
135         return stat;
136     }
137
138     server->sd = s;
139     server->active = 1;
140     return APR_SUCCESS;
141 }
142
143
144 static ap_status_t close_listeners_on_exec(void *v)
145 {
146     ap_listen_rec *lr;
147
148     for (lr = ap_listeners; lr; lr = lr->next) {
149         ap_close_socket(lr->sd);
150         lr->active = 0;
151     }
152     return APR_SUCCESS;
153 }
154
155
156 static void alloc_listener(process_rec *process, char *addr, unsigned int port)
157 {
158     ap_listen_rec **walk;
159     ap_listen_rec *new;
160     ap_status_t status;
161     char *oldaddr;
162     unsigned int oldport;
163
164     /* see if we've got an old listener for this address:port */
165     for (walk = &old_listeners; *walk; walk = &(*walk)->next) {
166         ap_get_local_port(&oldport, (*walk)->sd);
167         ap_get_local_ipaddr(&oldaddr,(*walk)->sd);
168         if (!strcmp(oldaddr, addr) && port == oldport) {
169             /* re-use existing record */
170             new = *walk;
171             *walk = new->next;
172             new->next = ap_listeners;
173             ap_listeners = new;
174             return;
175         }
176     }
177
178     /* this has to survive restarts */
179     /* XXX - We need to deal with freeing this structure properly. */
180     new = ap_palloc(process->pool, sizeof(ap_listen_rec));
181     new->active = 0;
182     if ((status = ap_create_tcp_socket(&new->sd, NULL)) != APR_SUCCESS) {
183         ap_log_error(APLOG_MARK, APLOG_CRIT, status, NULL,
184                  "make_sock: failed to get a socket for %s", addr);
185         return;
186     }
187     ap_set_local_port(new->sd, port);
188     ap_set_local_ipaddr(new->sd, addr);
189     new->next = ap_listeners;
190     ap_listeners = new;
191 }
192
193
194 int ap_listen_open(process_rec *process, unsigned port)
195 {
196     ap_context_t *pconf = process->pconf;
197     ap_listen_rec *lr;
198     ap_listen_rec *next;
199     int num_open;
200
201     /* allocate a default listener if necessary */
202     if (ap_listeners == NULL) {
203         alloc_listener(process, APR_ANYADDR, port ? port : DEFAULT_HTTP_PORT);
204     }
205
206     num_open = 0;
207     for (lr = ap_listeners; lr; lr = lr->next) {
208         if (lr->active) {
209             ++num_open;
210         }
211         else {
212             if (make_sock(pconf, lr) == APR_SUCCESS) {
213                 ++num_open;
214                 lr->active = 1;
215             }
216         }
217     }
218
219     /* close the old listeners */
220     for (lr = old_listeners; lr; lr = next) {
221         ap_close_socket(lr->sd);
222         lr->active = 0;
223         next = lr->next;
224 /*      free(lr);*/
225     }
226     old_listeners = NULL;
227
228     ap_register_cleanup(pconf, NULL, ap_null_cleanup, close_listeners_on_exec);
229
230     return num_open ? 0 : -1;
231 }
232
233
234 void ap_listen_pre_config(void)
235 {
236     old_listeners = ap_listeners;
237     ap_listeners = NULL;
238     ap_listenbacklog = DEFAULT_LISTENBACKLOG;
239 }
240
241
242 const char *ap_set_listener(cmd_parms *cmd, void *dummy, char *ips)
243 {
244     char *ports;
245     unsigned short port;
246
247     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
248     if (err != NULL) {
249         return err;
250     }
251
252     ports = strchr(ips, ':');
253     if (ports != NULL) {
254         if (ports == ips) {
255             return "Missing IP address";
256         }
257         else if (ports[1] == '\0') {
258             return "Address must end in :<port-number>";
259         }
260         *(ports++) = '\0';
261     }
262     else {
263         ports = ips;
264     }
265
266     port = atoi(ports);
267     if (!port) {
268         return "Port must be numeric";
269     }
270
271     if (ports == ips) { /* no address */
272         alloc_listener(cmd->server->process, APR_ANYADDR, port);
273     }
274     else {
275         ips[(ports - ips) - 1] = '\0';
276         alloc_listener(cmd->server->process, ips, port);
277     }
278
279     return NULL;
280 }
281
282 const char *ap_set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg) 
283 {
284     int b;
285
286     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
287     if (err != NULL) {
288         return err;
289     }
290
291     b = atoi(arg);
292     if (b < 1) {
293         return "ListenBacklog must be > 0";
294     }
295     ap_listenbacklog = b;
296     return NULL;
297 }
298
299 const char *ap_set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg)
300 {
301     int s = atoi(arg);
302     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
303     if (err != NULL) {
304         return err;
305     }
306
307     if (s < 512 && s != 0) {
308         return "SendBufferSize must be >= 512 bytes, or 0 for system default.";
309     }
310     send_buffer_size = s;
311     return NULL;
312 }