]> granicus.if.org Git - apache/blob - server/listen.c
Forgot listen.c in the earlier AcceptEx patch to winnt.c.
[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 "httpd.h"
59 #include "http_config.h"
60 #include "ap_listen.h"
61 #include "http_log.h"
62
63 ap_listen_rec *ap_listeners;
64 static ap_listen_rec *old_listeners;
65 static int ap_listenbacklog;
66 static int send_buffer_size;
67
68 /* TODO: make_sock is just begging and screaming for APR abstraction */
69 static int make_sock(const struct sockaddr_in *server)
70 {
71     int s;
72     int one = 1;
73     char addr[512];
74
75     if (server->sin_addr.s_addr != htonl(INADDR_ANY))
76         ap_snprintf(addr, sizeof(addr), "address %s port %d",
77                 inet_ntoa(server->sin_addr), ntohs(server->sin_port));
78     else
79         ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port));
80
81 #ifdef WIN32
82     s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
83     if (s == INVALID_SOCKET) {
84         ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
85                      "make_sock: failed to get a socket for %s", addr);
86         return -1;
87     }
88 #else
89     if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
90         ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
91                     "make_sock: failed to get a socket for %s", addr);
92         return -1;
93     }
94 #endif
95
96 #ifdef SO_REUSEADDR
97     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) {
98         ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
99                     "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr);
100         close(s);
101         return -1;
102     }
103 #endif
104     one = 1;
105 #ifdef SO_KEEPALIVE
106     if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) {
107         ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
108                     "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr);
109         close(s);
110         return -1;
111     }
112 #endif
113
114     /*
115      * To send data over high bandwidth-delay connections at full
116      * speed we must force the TCP window to open wide enough to keep the
117      * pipe full.  The default window size on many systems
118      * is only 4kB.  Cross-country WAN connections of 100ms
119      * at 1Mb/s are not impossible for well connected sites.
120      * If we assume 100ms cross-country latency,
121      * a 4kB buffer limits throughput to 40kB/s.
122      *
123      * To avoid this problem I've added the SendBufferSize directive
124      * to allow the web master to configure send buffer size.
125      *
126      * The trade-off of larger buffers is that more kernel memory
127      * is consumed.  YMMV, know your customers and your network!
128      *
129      * -John Heidemann <johnh@isi.edu> 25-Oct-96
130      *
131      * If no size is specified, use the kernel default.
132      */
133 #ifdef SO_SNDBUF
134     if (send_buffer_size) {
135         if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
136                 (char *) &send_buffer_size, sizeof(int)) < 0) {
137             ap_log_error(APLOG_MARK, APLOG_WARNING, NULL,
138                         "make_sock: failed to set SendBufferSize for %s, "
139                         "using default", addr);
140             /* not a fatal error */
141         }
142     }
143 #endif
144
145     if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) {
146         ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
147             "make_sock: could not bind to %s", addr);
148         close(s);
149         return -1;
150     }
151
152     if (listen(s, ap_listenbacklog) == -1) {
153         ap_log_error(APLOG_MARK, APLOG_ERR, NULL,
154             "make_sock: unable to listen for connections on %s", addr);
155         close(s);
156         return -1;
157     }
158
159     return s;
160 }
161
162
163 static void close_listeners_on_exec(void *v)
164 {
165     ap_listen_rec *lr;
166
167     for (lr = ap_listeners; lr; lr = lr->next) {
168         close(lr->fd);
169     }
170 }
171
172
173 static void alloc_listener(struct sockaddr_in *local_addr)
174 {
175     ap_listen_rec **walk;
176     ap_listen_rec *new;
177
178     /* see if we've got an old listener for this address:port */
179     for (walk = &old_listeners; *walk; walk = &(*walk)->next) {
180         if (!memcmp(&(*walk)->local_addr, local_addr, sizeof(local_addr))) {
181             /* re-use existing record */
182             new = *walk;
183             *walk = new->next;
184             new->next = ap_listeners;
185             ap_listeners = new;
186             return;
187         }
188     }
189
190     /* this has to survive restarts */
191     new = malloc(sizeof(ap_listen_rec));
192     new->local_addr = *local_addr;
193     new->fd = -1;
194     new->next = ap_listeners;
195     ap_listeners = new;
196 }
197
198
199 int ap_listen_open(pool *pconf, unsigned port)
200 {
201     ap_listen_rec *lr;
202     ap_listen_rec *next;
203     int num_open;
204     struct sockaddr_in local_addr;
205
206     /* allocate a default listener if necessary */
207     if (ap_listeners == NULL) {
208         local_addr.sin_family = AF_INET;
209         local_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* XXX */
210         local_addr.sin_port = htons(port ? port : DEFAULT_HTTP_PORT);
211         alloc_listener(&local_addr);
212     }
213
214     num_open = 0;
215     for (lr = ap_listeners; lr; lr = lr->next) {
216         if (lr->fd < 0) {
217             lr->fd = make_sock(&lr->local_addr);
218         }
219         if (lr->fd >= 0) {
220             ++num_open;
221         }
222     }
223
224     /* close the old listeners */
225     for (lr = old_listeners; lr; lr = next) {
226         close(lr->fd);
227         next = lr->next;
228         free(lr);
229     }
230     old_listeners = NULL;
231
232     ap_register_cleanup(pconf, NULL, ap_null_cleanup, close_listeners_on_exec);
233
234     return num_open ? 0 : -1;
235 }
236
237
238 void ap_listen_pre_config(void)
239 {
240     old_listeners = ap_listeners;
241     ap_listeners = NULL;
242     ap_listenbacklog = DEFAULT_LISTENBACKLOG;
243 }
244
245
246 const char *ap_set_listener(cmd_parms *cmd, void *dummy, char *ips)
247 {
248     char *ports;
249     unsigned short port;
250     struct sockaddr_in local_addr;
251
252     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
253     if (err != NULL) {
254         return err;
255     }
256
257     ports = strchr(ips, ':');
258     if (ports != NULL) {
259         if (ports == ips) {
260             return "Missing IP address";
261         }
262         else if (ports[1] == '\0') {
263             return "Address must end in :<port-number>";
264         }
265         *(ports++) = '\0';
266     }
267     else {
268         ports = ips;
269     }
270
271     local_addr.sin_family = AF_INET;
272     if (ports == ips) { /* no address */
273         local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
274     }
275     else {
276         local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL);
277     }
278     port = atoi(ports);
279     if (!port) {
280         return "Port must be numeric";
281     }
282     local_addr.sin_port = htons(port);
283
284     alloc_listener(&local_addr);
285
286     return NULL;
287 }
288
289 const char *ap_set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg) 
290 {
291     int b;
292
293     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
294     if (err != NULL) {
295         return err;
296     }
297
298     b = atoi(arg);
299     if (b < 1) {
300         return "ListenBacklog must be > 0";
301     }
302     ap_listenbacklog = b;
303     return NULL;
304 }
305
306 const char *ap_set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg)
307 {
308     int s = atoi(arg);
309     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
310     if (err != NULL) {
311         return err;
312     }
313
314     if (s < 512 && s != 0) {
315         return "SendBufferSize must be >= 512 bytes, or 0 for system default.";
316     }
317     send_buffer_size = s;
318     return NULL;
319 }