]> granicus.if.org Git - apache/blob - server/listen.c
fix Win32 compile failure in r883540, reported by Gregg Smith
[apache] / server / listen.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "apr_network_io.h"
18 #include "apr_strings.h"
19
20 #define APR_WANT_STRFUNC
21 #include "apr_want.h"
22
23 #include "ap_config.h"
24 #include "httpd.h"
25 #include "http_config.h"
26 #include "http_core.h"
27 #include "ap_listen.h"
28 #include "http_log.h"
29 #include "mpm_common.h"
30
31 AP_DECLARE_DATA ap_listen_rec *ap_listeners = NULL;
32
33 static ap_listen_rec *old_listeners;
34 static int ap_listenbacklog;
35 static int send_buffer_size;
36 static int receive_buffer_size;
37
38 /* TODO: make_sock is just begging and screaming for APR abstraction */
39 static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
40 {
41     apr_socket_t *s = server->sd;
42     int one = 1;
43 #if APR_HAVE_IPV6
44 #ifdef AP_ENABLE_V4_MAPPED
45     int v6only_setting = 0;
46 #else
47     int v6only_setting = 1;
48 #endif
49 #endif
50     apr_status_t stat;
51
52 #ifndef WIN32
53     stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
54     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
55         ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
56                       "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
57                       server->bind_addr);
58         apr_socket_close(s);
59         return stat;
60     }
61 #endif
62
63     stat = apr_socket_opt_set(s, APR_SO_KEEPALIVE, one);
64     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
65         ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
66                       "make_sock: for address %pI, apr_socket_opt_set: (SO_KEEPALIVE)",
67                       server->bind_addr);
68         apr_socket_close(s);
69         return stat;
70     }
71
72 #if APR_HAVE_IPV6
73     if (server->bind_addr->family == APR_INET6) {
74         stat = apr_socket_opt_set(s, APR_IPV6_V6ONLY, v6only_setting);
75         if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
76             ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
77                           "make_sock: for address %pI, apr_socket_opt_set: "
78                           "(IPV6_V6ONLY)",
79                           server->bind_addr);
80             apr_socket_close(s);
81             return stat;
82         }
83     }
84 #endif
85
86     /*
87      * To send data over high bandwidth-delay connections at full
88      * speed we must force the TCP window to open wide enough to keep the
89      * pipe full.  The default window size on many systems
90      * is only 4kB.  Cross-country WAN connections of 100ms
91      * at 1Mb/s are not impossible for well connected sites.
92      * If we assume 100ms cross-country latency,
93      * a 4kB buffer limits throughput to 40kB/s.
94      *
95      * To avoid this problem I've added the SendBufferSize directive
96      * to allow the web master to configure send buffer size.
97      *
98      * The trade-off of larger buffers is that more kernel memory
99      * is consumed.  YMMV, know your customers and your network!
100      *
101      * -John Heidemann <johnh@isi.edu> 25-Oct-96
102      *
103      * If no size is specified, use the kernel default.
104      */
105     if (send_buffer_size) {
106         stat = apr_socket_opt_set(s, APR_SO_SNDBUF,  send_buffer_size);
107         if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
108             ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
109                           "make_sock: failed to set SendBufferSize for "
110                           "address %pI, using default",
111                           server->bind_addr);
112             /* not a fatal error */
113         }
114     }
115     if (receive_buffer_size) {
116         stat = apr_socket_opt_set(s, APR_SO_RCVBUF, receive_buffer_size);
117         if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
118             ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
119                           "make_sock: failed to set ReceiveBufferSize for "
120                           "address %pI, using default",
121                           server->bind_addr);
122             /* not a fatal error */
123         }
124     }
125
126 #if APR_TCP_NODELAY_INHERITED
127     ap_sock_disable_nagle(s);
128 #endif
129
130     if ((stat = apr_socket_bind(s, server->bind_addr)) != APR_SUCCESS) {
131         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p,
132                       "make_sock: could not bind to address %pI",
133                       server->bind_addr);
134         apr_socket_close(s);
135         return stat;
136     }
137
138     if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
139         ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p,
140                       "make_sock: unable to listen for connections "
141                       "on address %pI",
142                       server->bind_addr);
143         apr_socket_close(s);
144         return stat;
145     }
146
147 #ifdef WIN32
148     /* I seriously doubt that this would work on Unix; I have doubts that
149      * it entirely solves the problem on Win32.  However, since setting
150      * reuseaddr on the listener -prior- to binding the socket has allowed
151      * us to attach to the same port as an already running instance of
152      * Apache, or even another web server, we cannot identify that this
153      * port was exclusively granted to this instance of Apache.
154      *
155      * So set reuseaddr, but do not attempt to do so until we have the
156      * parent listeners successfully bound.
157      */
158     stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
159     if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
160         ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
161                     "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
162                      server->bind_addr);
163         apr_socket_close(s);
164         return stat;
165     }
166 #endif
167
168     server->sd = s;
169     server->active = 1;
170
171     server->accept_func = NULL;
172
173     return APR_SUCCESS;
174 }
175
176 static const char* find_accf_name(server_rec *s, const char *proto)
177 {
178     const char* accf;
179     core_server_config *conf = ap_get_module_config(s->module_config,
180                                                     &core_module);
181     if (!proto) {
182         return NULL;
183     }
184
185     accf = apr_table_get(conf->accf_map, proto);
186
187     if (accf && !strcmp("none", accf)) {
188         return NULL;
189     }
190
191     return accf;
192 }
193
194 static void ap_apply_accept_filter(apr_pool_t *p, ap_listen_rec *lis,
195                                            server_rec *server)
196 {
197     apr_socket_t *s = lis->sd;
198     const char *accf;
199     apr_status_t rv;
200     const char *proto;
201
202     proto = lis->protocol;
203
204     if (!proto) {
205         proto = ap_get_server_protocol(server);
206     }
207
208
209     accf = find_accf_name(server, proto);
210
211     if (accf) {
212 #if APR_HAS_SO_ACCEPTFILTER
213         rv = apr_socket_accept_filter(s, apr_pstrdup(p, accf),
214                                       apr_pstrdup(p,""));
215         if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
216             ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
217                           "Failed to enable the '%s' Accept Filter",
218                           accf);
219         }
220 #else
221         rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
222         if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
223             ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
224                               "Failed to enable APR_TCP_DEFER_ACCEPT");
225         }
226 #endif
227     }
228 }
229
230 static apr_status_t close_listeners_on_exec(void *v)
231 {
232     ap_close_listeners();
233     return APR_SUCCESS;
234 }
235
236 static const char *alloc_listener(process_rec *process, char *addr,
237                                   apr_port_t port, const char* proto,
238                                   void *dummy)
239 {
240     ap_listen_rec **walk, *last;
241     apr_status_t status;
242     apr_sockaddr_t *sa;
243     int found_listener = 0;
244
245     /* see if we've got an old listener for this address:port */
246     for (walk = &old_listeners; *walk;) {
247         sa = (*walk)->bind_addr;
248         /* Some listeners are not real so they will not have a bind_addr. */
249         if (sa) {
250             ap_listen_rec *new;
251             apr_port_t oldport;
252
253             oldport = sa->port;
254             /* If both ports are equivalent, then if their names are equivalent,
255              * then we will re-use the existing record.
256              */
257             if (port == oldport &&
258                 ((!addr && !sa->hostname) ||
259                  ((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) {
260                 new = *walk;
261                 *walk = new->next;
262                 new->next = ap_listeners;
263                 ap_listeners = new;
264                 found_listener = 1;
265                 continue;
266             }
267         }
268
269         walk = &(*walk)->next;
270     }
271
272     if (found_listener) {
273         if (ap_listeners->slave != dummy) {
274             return "Cannot define a slave on the same IP:port as a Listener";
275         }
276         return NULL;
277     }
278
279     if ((status = apr_sockaddr_info_get(&sa, addr, APR_UNSPEC, port, 0,
280                                         process->pool))
281         != APR_SUCCESS) {
282         ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
283                       "alloc_listener: failed to set up sockaddr for %s",
284                       addr);
285         return "Listen setup failed";
286     }
287
288     /* Initialize to our last configured ap_listener. */
289     last = ap_listeners;
290     while (last && last->next) {
291         last = last->next;
292     }
293
294     while (sa) {
295         ap_listen_rec *new;
296
297         /* this has to survive restarts */
298         new = apr_palloc(process->pool, sizeof(ap_listen_rec));
299         new->active = 0;
300         new->next = 0;
301         new->bind_addr = sa;
302         new->protocol = apr_pstrdup(process->pool, proto);
303
304         /* Go to the next sockaddr. */
305         sa = sa->next;
306
307         status = apr_socket_create(&new->sd, new->bind_addr->family,
308                                     SOCK_STREAM, 0, process->pool);
309
310 #if APR_HAVE_IPV6
311         /* What could happen is that we got an IPv6 address, but this system
312          * doesn't actually support IPv6.  Try the next address.
313          */
314         if (status != APR_SUCCESS && !addr &&
315             new->bind_addr->family == APR_INET6) {
316             continue;
317         }
318 #endif
319         if (status != APR_SUCCESS) {
320             ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
321                           "alloc_listener: failed to get a socket for %s",
322                           addr);
323             return "Listen setup failed";
324         }
325
326         /* We need to preserve the order returned by getaddrinfo() */
327         if (last == NULL) {
328             ap_listeners = last = new;
329         } else {
330             last->next = new;
331             last = new;
332         }
333         new->slave = dummy;
334     }
335
336     return NULL;
337 }
338 /* Evaluates to true if the (apr_sockaddr_t *) addr argument is the
339  * IPv4 match-any-address, 0.0.0.0. */
340 #define IS_INADDR_ANY(addr) ((addr)->family == APR_INET \
341                              && (addr)->sa.sin.sin_addr.s_addr == INADDR_ANY)
342
343 /* Evaluates to true if the (apr_sockaddr_t *) addr argument is the
344  * IPv6 match-any-address, [::]. */
345 #define IS_IN6ADDR_ANY(addr) ((addr)->family == APR_INET6 \
346                               && IN6_IS_ADDR_UNSPECIFIED(&(addr)->sa.sin6.sin6_addr))
347
348 /**
349  * Create, open, listen, and bind all sockets.
350  * @param process The process record for the currently running server
351  * @return The number of open sockets
352  */
353 static int open_listeners(apr_pool_t *pool)
354 {
355     ap_listen_rec *lr;
356     ap_listen_rec *next;
357     ap_listen_rec *previous;
358     int num_open;
359     const char *userdata_key = "ap_open_listeners";
360     void *data;
361 #if AP_NONBLOCK_WHEN_MULTI_LISTEN
362     int use_nonblock;
363 #endif
364
365     /* Don't allocate a default listener.  If we need to listen to a
366      * port, then the user needs to have a Listen directive in their
367      * config file.
368      */
369     num_open = 0;
370     previous = NULL;
371     for (lr = ap_listeners; lr; previous = lr, lr = lr->next) {
372         if (lr->active) {
373             ++num_open;
374         }
375         else {
376 #if APR_HAVE_IPV6
377             ap_listen_rec *cur;
378             int v6only_setting;
379             int skip = 0;
380
381             /* If we have the unspecified IPv4 address (0.0.0.0) and
382              * the unspecified IPv6 address (::) is next, we need to
383              * swap the order of these in the list. We always try to
384              * bind to IPv6 first, then IPv4, since an IPv6 socket
385              * might be able to receive IPv4 packets if V6ONLY is not
386              * enabled, but never the other way around.
387              * Note: In some configurations, the unspecified IPv6 address
388              * could be even later in the list.  This logic only corrects
389              * the situation where it is next in the list, such as when
390              * apr_sockaddr_info_get() returns an IPv4 and an IPv6 address,
391              * in that order.
392              */
393             if (lr->next != NULL
394                 && IS_INADDR_ANY(lr->bind_addr)
395                 && lr->bind_addr->port == lr->next->bind_addr->port
396                 && IS_IN6ADDR_ANY(lr->next->bind_addr)) {
397                 /* Exchange lr and lr->next */
398                 next = lr->next;
399                 lr->next = next->next;
400                 next->next = lr;
401                 if (previous) {
402                     previous->next = next;
403                 }
404                 else {
405                     ap_listeners = next;
406                 }
407                 lr = next;
408             }
409
410             /* If we are trying to bind to 0.0.0.0 and a previous listener
411              * was :: on the same port and in turn that socket does not have
412              * the IPV6_V6ONLY flag set; we must skip the current attempt to
413              * listen (which would generate an error). IPv4 will be handled
414              * on the established IPv6 socket.
415              */
416             if (IS_INADDR_ANY(lr->bind_addr)) {
417                 for (cur = ap_listeners; cur != lr; cur = cur->next) {
418                     if (lr->bind_addr->port == cur->bind_addr->port
419                         && IS_IN6ADDR_ANY(cur->bind_addr)
420                         && apr_socket_opt_get(cur->sd, APR_IPV6_V6ONLY,
421                                               &v6only_setting) == APR_SUCCESS
422                         && v6only_setting == 0) {
423
424                         /* Remove the current listener from the list */
425                         previous->next = lr->next;
426                         lr = previous; /* maintain current value of previous after
427                                         * post-loop expression is evaluated
428                                         */
429                         skip = 1;
430                         break;
431                     }
432                 }
433                 if (skip) {
434                     continue;
435                 }
436             }
437 #endif
438             if (make_sock(pool, lr) == APR_SUCCESS) {
439                 ++num_open;
440                 lr->active = 1;
441             }
442             else {
443 #if APR_HAVE_IPV6
444                 /* If we tried to bind to ::, and the next listener is
445                  * on 0.0.0.0 with the same port, don't give a fatal
446                  * error. The user will still get a warning from make_sock
447                  * though.
448                  */
449                 if (lr->next != NULL
450                     && IS_IN6ADDR_ANY(lr->bind_addr)
451                     && lr->bind_addr->port == lr->next->bind_addr->port
452                     && IS_INADDR_ANY(lr->next->bind_addr)) {
453
454                     /* Remove the current listener from the list */
455                     if (previous) {
456                         previous->next = lr->next;
457                     }
458                     else {
459                         ap_listeners = lr->next;
460                     }
461
462                     /* Although we've removed ourselves from the list,
463                      * we need to make sure that the next iteration won't
464                      * consider "previous" a working IPv6 '::' socket.
465                      * Changing the family is enough to make sure the
466                      * conditions before make_sock() fail.
467                      */
468                     lr->bind_addr->family = AF_INET;
469
470                     continue;
471                 }
472 #endif
473                 /* fatal error */
474                 return -1;
475             }
476         }
477     }
478
479     /* close the old listeners */
480     for (lr = old_listeners; lr; lr = next) {
481         apr_socket_close(lr->sd);
482         lr->active = 0;
483         next = lr->next;
484     }
485     old_listeners = NULL;
486
487 #if AP_NONBLOCK_WHEN_MULTI_LISTEN
488     /* if multiple listening sockets, make them non-blocking so that
489      * if select()/poll() reports readability for a reset connection that
490      * is already forgotten about by the time we call accept, we won't
491      * be hung until another connection arrives on that port
492      */
493     use_nonblock = (ap_listeners && ap_listeners->next);
494     for (lr = ap_listeners; lr; lr = lr->next) {
495         apr_status_t status;
496
497         status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, use_nonblock);
498         if (status != APR_SUCCESS) {
499             ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool,
500                           "unable to control socket non-blocking status");
501             return -1;
502         }
503     }
504 #endif /* AP_NONBLOCK_WHEN_MULTI_LISTEN */
505
506     /* we come through here on both passes of the open logs phase
507      * only register the cleanup once... otherwise we try to close
508      * listening sockets twice when cleaning up prior to exec
509      */
510     apr_pool_userdata_get(&data, userdata_key, pool);
511     if (!data) {
512         apr_pool_userdata_set((const void *)1, userdata_key,
513                               apr_pool_cleanup_null, pool);
514         apr_pool_cleanup_register(pool, NULL, apr_pool_cleanup_null,
515                                   close_listeners_on_exec);
516     }
517
518     return num_open ? 0 : -1;
519 }
520
521 AP_DECLARE(int) ap_setup_listeners(server_rec *s)
522 {
523     server_rec *ls;
524     server_addr_rec *addr;
525     ap_listen_rec *lr;
526     int num_listeners = 0;
527     const char* proto;
528     int found;
529
530     for (ls = s; ls; ls = ls->next) {
531         proto = ap_get_server_protocol(ls);
532         if (!proto) {
533             found = 0;
534             /* No protocol was set for this vhost,
535              * use the default for this listener.
536              */
537             for (addr = ls->addrs; addr && !found; addr = addr->next) {
538                 for (lr = ap_listeners; lr; lr = lr->next) {
539                     if (apr_sockaddr_equal(lr->bind_addr, addr->host_addr) &&
540                         lr->bind_addr->port == addr->host_port) {
541                         ap_set_server_protocol(ls, lr->protocol);
542                         found = 1;
543                         break;
544                     }
545                 }
546             }
547
548             if (!found) {
549                 /* TODO: set protocol defaults per-Port, eg 25=smtp */
550                 ap_set_server_protocol(ls, "http");
551             }
552         }
553     }
554
555     if (open_listeners(s->process->pool)) {
556        return 0;
557     }
558
559     for (lr = ap_listeners; lr; lr = lr->next) {
560         num_listeners++;
561         found = 0;
562         for (ls = s; ls && !found; ls = ls->next) {
563             for (addr = ls->addrs; addr && !found; addr = addr->next) {
564                 if (apr_sockaddr_equal(lr->bind_addr, addr->host_addr) &&
565                     lr->bind_addr->port == addr->host_port) {
566                     found = 1;
567                     ap_apply_accept_filter(s->process->pool, lr, ls);
568                 }
569             }
570         }
571
572         if (!found) {
573             ap_apply_accept_filter(s->process->pool, lr, s);
574         }
575     }
576
577     return num_listeners;
578 }
579
580 AP_DECLARE_NONSTD(void) ap_close_listeners(void)
581 {
582     ap_listen_rec *lr;
583
584     for (lr = ap_listeners; lr; lr = lr->next) {
585         apr_socket_close(lr->sd);
586         lr->active = 0;
587     }
588 }
589 AP_DECLARE_NONSTD(int) ap_close_selected_listeners(ap_slave_t *slave)
590 {
591     ap_listen_rec *lr;
592     int n = 0;
593
594     for (lr = ap_listeners; lr; lr = lr->next) {
595         if (lr->slave != slave) {
596             apr_socket_close(lr->sd);
597             lr->active = 0;
598         }
599         else {
600             ++n;
601         }
602     }
603     return n;
604 }
605
606 AP_DECLARE(void) ap_listen_pre_config(void)
607 {
608     old_listeners = ap_listeners;
609     ap_listeners = NULL;
610     ap_listenbacklog = DEFAULT_LISTENBACKLOG;
611 }
612
613 /* Hack: populate an extra field
614  * When this gets called from a Listen directive, dummy is null.
615  * So we can use non-null dummy to pass a data pointer without conflict
616  */
617 AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
618                                                 int argc, char *const argv[])
619 {
620     char *host, *scope_id, *proto;
621     apr_port_t port;
622     apr_status_t rv;
623     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
624
625     if (err != NULL) {
626         return err;
627     }
628
629     if (argc < 1 || argc > 2) {
630         return "Listen requires 1 or 2 arguments.";
631     }
632
633     rv = apr_parse_addr_port(&host, &scope_id, &port, argv[0], cmd->pool);
634     if (rv != APR_SUCCESS) {
635         return "Invalid address or port";
636     }
637
638     if (host && !strcmp(host, "*")) {
639         host = NULL;
640     }
641
642     if (scope_id) {
643         /* XXX scope id support is useful with link-local IPv6 addresses */
644         return "Scope id is not supported";
645     }
646
647     if (!port) {
648         return "Port must be specified";
649     }
650
651     if (argc != 2) {
652         if (port == 443) {
653             proto = "https";
654         } else {
655             proto = "http";
656         }
657     }
658     else {
659         proto = apr_pstrdup(cmd->pool, argv[1]);
660         ap_str_tolower(proto);
661     }
662
663     return alloc_listener(cmd->server->process, host, port, proto, dummy);
664 }
665
666 AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,
667                                                      void *dummy,
668                                                      const char *arg)
669 {
670     int b;
671     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
672
673     if (err != NULL) {
674         return err;
675     }
676
677     b = atoi(arg);
678     if (b < 1) {
679         return "ListenBacklog must be > 0";
680     }
681
682     ap_listenbacklog = b;
683     return NULL;
684 }
685
686 AP_DECLARE_NONSTD(const char *) ap_set_send_buffer_size(cmd_parms *cmd,
687                                                         void *dummy,
688                                                         const char *arg)
689 {
690     int s = atoi(arg);
691     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
692
693     if (err != NULL) {
694         return err;
695     }
696
697     if (s < 512 && s != 0) {
698         return "SendBufferSize must be >= 512 bytes, or 0 for system default.";
699     }
700
701     send_buffer_size = s;
702     return NULL;
703 }
704
705 AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
706                                                            void *dummy,
707                                                            const char *arg)
708 {
709     int s = atoi(arg);
710     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
711
712     if (err != NULL) {
713         return err;
714     }
715
716     if (s < 512 && s != 0) {
717         return "ReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
718     }
719
720     receive_buffer_size = s;
721     return NULL;
722 }