Annotation of embedaddon/lighttpd/src/network.c, revision 1.1.1.2
1.1 misho 1: #include "network.h"
2: #include "fdevent.h"
3: #include "log.h"
4: #include "connections.h"
5: #include "plugin.h"
6: #include "joblist.h"
7: #include "configfile.h"
8:
9: #include "network_backends.h"
10: #include "sys-mmap.h"
11: #include "sys-socket.h"
12:
13: #include <sys/types.h>
14: #include <sys/stat.h>
15: #include <sys/time.h>
16:
17: #include <errno.h>
18: #include <fcntl.h>
19: #include <unistd.h>
20: #include <string.h>
21: #include <stdlib.h>
22: #include <assert.h>
23:
24: #ifdef USE_OPENSSL
25: # include <openssl/ssl.h>
26: # include <openssl/err.h>
27: # include <openssl/rand.h>
28: # ifndef OPENSSL_NO_DH
29: # include <openssl/dh.h>
30: # endif
31: # include <openssl/bn.h>
32:
33: # if OPENSSL_VERSION_NUMBER >= 0x0090800fL
34: # ifndef OPENSSL_NO_ECDH
35: # include <openssl/ecdh.h>
36: # endif
37: # endif
38: #endif
39:
40: #ifdef USE_OPENSSL
41: static void ssl_info_callback(const SSL *ssl, int where, int ret) {
42: UNUSED(ret);
43:
44: if (0 != (where & SSL_CB_HANDSHAKE_START)) {
45: connection *con = SSL_get_app_data(ssl);
46: ++con->renegotiations;
47: }
48: }
49: #endif
50:
51: static handler_t network_server_handle_fdevent(server *srv, void *context, int revents) {
52: server_socket *srv_socket = (server_socket *)context;
53: connection *con;
54: int loops = 0;
55:
56: UNUSED(context);
57:
58: if (0 == (revents & FDEVENT_IN)) {
59: log_error_write(srv, __FILE__, __LINE__, "sdd",
60: "strange event for server socket",
61: srv_socket->fd,
62: revents);
63: return HANDLER_ERROR;
64: }
65:
66: /* accept()s at most 100 connections directly
67: *
68: * we jump out after 100 to give the waiting connections a chance */
69: for (loops = 0; loops < 100 && NULL != (con = connection_accept(srv, srv_socket)); loops++) {
70: handler_t r;
71:
72: connection_state_machine(srv, con);
73:
74: switch(r = plugins_call_handle_joblist(srv, con)) {
75: case HANDLER_FINISHED:
76: case HANDLER_GO_ON:
77: break;
78: default:
79: log_error_write(srv, __FILE__, __LINE__, "d", r);
80: break;
81: }
82: }
83: return HANDLER_GO_ON;
84: }
85:
86: #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT
87: static int network_ssl_servername_callback(SSL *ssl, int *al, server *srv) {
88: const char *servername;
89: connection *con = (connection *) SSL_get_app_data(ssl);
90: UNUSED(al);
91:
92: buffer_copy_string(con->uri.scheme, "https");
93:
94: if (NULL == (servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) {
95: #if 0
96: /* this "error" just means the client didn't support it */
97: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
98: "failed to get TLS server name");
99: #endif
100: return SSL_TLSEXT_ERR_NOACK;
101: }
102: buffer_copy_string(con->tlsext_server_name, servername);
103: buffer_to_lower(con->tlsext_server_name);
104:
105: /* Sometimes this is still set, confusing COMP_HTTP_HOST */
106: buffer_reset(con->uri.authority);
107:
108: config_cond_cache_reset(srv, con);
109: config_setup_connection(srv, con);
110:
111: config_patch_connection(srv, con, COMP_SERVER_SOCKET);
112: config_patch_connection(srv, con, COMP_HTTP_SCHEME);
113: config_patch_connection(srv, con, COMP_HTTP_HOST);
114:
1.1.1.2 ! misho 115: if (NULL == con->conf.ssl_pemfile_x509 || NULL == con->conf.ssl_pemfile_pkey) {
! 116: /* x509/pkey available <=> pemfile was set <=> pemfile got patched: so this should never happen, unless you nest $SERVER["socket"] */
1.1 misho 117: log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:",
1.1.1.2 ! misho 118: "no certificate/private key for TLS server name", con->tlsext_server_name);
1.1 misho 119: return SSL_TLSEXT_ERR_ALERT_FATAL;
120: }
121:
1.1.1.2 ! misho 122: /* first set certificate! setting private key checks whether certificate matches it */
! 123: if (!SSL_use_certificate(ssl, con->conf.ssl_pemfile_x509)) {
! 124: log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:",
! 125: "failed to set certificate for TLS server name", con->tlsext_server_name,
! 126: ERR_error_string(ERR_get_error(), NULL));
! 127: return SSL_TLSEXT_ERR_ALERT_FATAL;
! 128: }
! 129:
! 130: if (!SSL_use_PrivateKey(ssl, con->conf.ssl_pemfile_pkey)) {
! 131: log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:",
! 132: "failed to set private key for TLS server name", con->tlsext_server_name,
! 133: ERR_error_string(ERR_get_error(), NULL));
1.1 misho 134: return SSL_TLSEXT_ERR_ALERT_FATAL;
135: }
136:
1.1.1.2 ! misho 137: if (con->conf.ssl_verifyclient) {
! 138: if (NULL == con->conf.ssl_ca_file_cert_names) {
! 139: log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:",
! 140: "can't verify client without ssl.ca-file for TLS server name", con->tlsext_server_name,
! 141: ERR_error_string(ERR_get_error(), NULL));
! 142: return SSL_TLSEXT_ERR_ALERT_FATAL;
! 143: }
! 144:
! 145: SSL_set_client_CA_list(ssl, SSL_dup_CA_list(con->conf.ssl_ca_file_cert_names));
! 146: /* forcing verification here is really not that useful - a client could just connect without SNI */
! 147: SSL_set_verify(
! 148: ssl,
! 149: SSL_VERIFY_PEER | (con->conf.ssl_verifyclient_enforce ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0),
! 150: NULL
! 151: );
! 152: SSL_set_verify_depth(ssl, con->conf.ssl_verifyclient_depth);
! 153: }
! 154:
1.1 misho 155: return SSL_TLSEXT_ERR_OK;
156: }
157: #endif
158:
159: static int network_server_init(server *srv, buffer *host_token, specific_config *s) {
160: int val;
161: socklen_t addr_len;
162: server_socket *srv_socket;
163: char *sp;
164: unsigned int port = 0;
165: const char *host;
166: buffer *b;
167: int is_unix_domain_socket = 0;
168: int fd;
169:
170: #ifdef __WIN32
171: WORD wVersionRequested;
172: WSADATA wsaData;
173: int err;
174:
175: wVersionRequested = MAKEWORD( 2, 2 );
176:
177: err = WSAStartup( wVersionRequested, &wsaData );
178: if ( err != 0 ) {
179: /* Tell the user that we could not find a usable */
180: /* WinSock DLL. */
181: return -1;
182: }
183: #endif
184:
185: srv_socket = calloc(1, sizeof(*srv_socket));
186: srv_socket->fd = -1;
187: srv_socket->fde_ndx = -1;
188:
189: srv_socket->srv_token = buffer_init();
190: buffer_copy_string_buffer(srv_socket->srv_token, host_token);
191:
192: b = buffer_init();
193: buffer_copy_string_buffer(b, host_token);
194:
195: /* ipv4:port
196: * [ipv6]:port
197: */
198: if (NULL == (sp = strrchr(b->ptr, ':'))) {
199: log_error_write(srv, __FILE__, __LINE__, "sb", "value of $SERVER[\"socket\"] has to be \"ip:port\".", b);
200:
201: goto error_free_socket;
202: }
203:
204: host = b->ptr;
205:
206: /* check for [ and ] */
207: if (b->ptr[0] == '[' && *(sp-1) == ']') {
208: *(sp-1) = '\0';
209: host++;
210:
211: s->use_ipv6 = 1;
212: }
213:
214: *(sp++) = '\0';
215:
216: port = strtol(sp, NULL, 10);
217:
218: if (host[0] == '/') {
219: /* host is a unix-domain-socket */
220: is_unix_domain_socket = 1;
221: } else if (port == 0 || port > 65535) {
222: log_error_write(srv, __FILE__, __LINE__, "sd", "port out of range:", port);
223:
224: goto error_free_socket;
225: }
226:
227: if (*host == '\0') host = NULL;
228:
229: if (is_unix_domain_socket) {
230: #ifdef HAVE_SYS_UN_H
231:
232: srv_socket->addr.plain.sa_family = AF_UNIX;
233:
234: if (-1 == (srv_socket->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, 0))) {
235: log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno));
236: goto error_free_socket;
237: }
238: #else
239: log_error_write(srv, __FILE__, __LINE__, "s",
240: "ERROR: Unix Domain sockets are not supported.");
241: goto error_free_socket;
242: #endif
243: }
244:
245: #ifdef HAVE_IPV6
246: if (s->use_ipv6) {
247: srv_socket->addr.plain.sa_family = AF_INET6;
248:
249: if (-1 == (srv_socket->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, IPPROTO_TCP))) {
250: log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno));
251: goto error_free_socket;
252: }
253: }
254: #endif
255:
256: if (srv_socket->fd == -1) {
257: srv_socket->addr.plain.sa_family = AF_INET;
258: if (-1 == (srv_socket->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, IPPROTO_TCP))) {
259: log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno));
260: goto error_free_socket;
261: }
262: }
263:
264: /* set FD_CLOEXEC now, fdevent_fcntl_set is called later; needed for pipe-logger forks */
1.1.1.2 ! misho 265: fd_close_on_exec(srv_socket->fd);
1.1 misho 266:
267: /* */
268: srv->cur_fds = srv_socket->fd;
269:
270: val = 1;
271: if (setsockopt(srv_socket->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
272: log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt(SO_REUSEADDR) failed:", strerror(errno));
273: goto error_free_socket;
274: }
275:
276: switch(srv_socket->addr.plain.sa_family) {
277: #ifdef HAVE_IPV6
278: case AF_INET6:
279: memset(&srv_socket->addr, 0, sizeof(struct sockaddr_in6));
280: srv_socket->addr.ipv6.sin6_family = AF_INET6;
281: if (host == NULL) {
282: srv_socket->addr.ipv6.sin6_addr = in6addr_any;
283: log_error_write(srv, __FILE__, __LINE__, "s", "warning: please use server.use-ipv6 only for hostnames, not without server.bind / empty address; your config will break if the kernel default for IPV6_V6ONLY changes");
284: } else {
285: struct addrinfo hints, *res;
286: int r;
287:
288: if (s->set_v6only) {
289: val = 1;
290: if (-1 == setsockopt(srv_socket->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) {
291: log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt(IPV6_V6ONLY) failed:", strerror(errno));
292: goto error_free_socket;
293: }
294: } else {
295: log_error_write(srv, __FILE__, __LINE__, "s", "warning: server.set-v6only will be removed soon, update your config to have different sockets for ipv4 and ipv6");
296: }
297:
298: memset(&hints, 0, sizeof(hints));
299:
300: hints.ai_family = AF_INET6;
301: hints.ai_socktype = SOCK_STREAM;
302: hints.ai_protocol = IPPROTO_TCP;
303:
304: if (0 != (r = getaddrinfo(host, NULL, &hints, &res))) {
305: log_error_write(srv, __FILE__, __LINE__,
306: "sssss", "getaddrinfo failed: ",
307: gai_strerror(r), "'", host, "'");
308:
309: goto error_free_socket;
310: }
311:
312: memcpy(&(srv_socket->addr), res->ai_addr, res->ai_addrlen);
313:
314: freeaddrinfo(res);
315: }
316: srv_socket->addr.ipv6.sin6_port = htons(port);
317: addr_len = sizeof(struct sockaddr_in6);
318: break;
319: #endif
320: case AF_INET:
321: memset(&srv_socket->addr, 0, sizeof(struct sockaddr_in));
322: srv_socket->addr.ipv4.sin_family = AF_INET;
323: if (host == NULL) {
324: srv_socket->addr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY);
325: } else {
326: struct hostent *he;
327: if (NULL == (he = gethostbyname(host))) {
328: log_error_write(srv, __FILE__, __LINE__,
329: "sds", "gethostbyname failed: ",
330: h_errno, host);
331: goto error_free_socket;
332: }
333:
334: if (he->h_addrtype != AF_INET) {
335: log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype);
336: goto error_free_socket;
337: }
338:
339: if (he->h_length != sizeof(struct in_addr)) {
340: log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length);
341: goto error_free_socket;
342: }
343:
344: memcpy(&(srv_socket->addr.ipv4.sin_addr.s_addr), he->h_addr_list[0], he->h_length);
345: }
346: srv_socket->addr.ipv4.sin_port = htons(port);
347:
348: addr_len = sizeof(struct sockaddr_in);
349:
350: break;
351: case AF_UNIX:
1.1.1.2 ! misho 352: {
! 353: size_t hostlen = strlen(host) + 1;
! 354: if (hostlen > sizeof(srv_socket->addr.un.sun_path)) {
! 355: log_error_write(srv, __FILE__, __LINE__, "sS", "unix socket filename too long:", host);
! 356: goto error_free_socket;
! 357: }
! 358: memcpy(srv_socket->addr.un.sun_path, host, hostlen);
! 359: }
1.1 misho 360: srv_socket->addr.un.sun_family = AF_UNIX;
361:
362: #ifdef SUN_LEN
363: addr_len = SUN_LEN(&srv_socket->addr.un);
364: #else
365: /* stevens says: */
1.1.1.2 ! misho 366: addr_len = hostlen + sizeof(srv_socket->addr.un.sun_family);
1.1 misho 367: #endif
368:
369: /* check if the socket exists and try to connect to it. */
370: if (-1 != (fd = connect(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len))) {
371: close(fd);
372:
373: log_error_write(srv, __FILE__, __LINE__, "ss",
374: "server socket is still in use:",
375: host);
376:
377:
378: goto error_free_socket;
379: }
380:
381: /* connect failed */
382: switch(errno) {
383: case ECONNREFUSED:
384: unlink(host);
385: break;
386: case ENOENT:
387: break;
388: default:
389: log_error_write(srv, __FILE__, __LINE__, "sds",
390: "testing socket failed:",
391: host, strerror(errno));
392:
393: goto error_free_socket;
394: }
395:
396: break;
397: default:
398: goto error_free_socket;
399: }
400:
401: if (0 != bind(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) {
402: switch(srv_socket->addr.plain.sa_family) {
403: case AF_UNIX:
404: log_error_write(srv, __FILE__, __LINE__, "sds",
405: "can't bind to socket:",
406: host, strerror(errno));
407: break;
408: default:
409: log_error_write(srv, __FILE__, __LINE__, "ssds",
410: "can't bind to port:",
411: host, port, strerror(errno));
412: break;
413: }
414: goto error_free_socket;
415: }
416:
417: if (-1 == listen(srv_socket->fd, 128 * 8)) {
418: log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed: ", strerror(errno));
419: goto error_free_socket;
420: }
421:
422: if (s->ssl_enabled) {
423: #ifdef USE_OPENSSL
424: if (NULL == (srv_socket->ssl_ctx = s->ssl_ctx)) {
425: log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set");
426: goto error_free_socket;
427: }
428: #else
429:
430: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
431: "ssl requested but openssl support is not compiled in");
432:
433: goto error_free_socket;
434: #endif
435: #ifdef TCP_DEFER_ACCEPT
436: } else if (s->defer_accept) {
437: int v = s->defer_accept;
438: if (-1 == setsockopt(srv_socket->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &v, sizeof(v))) {
439: log_error_write(srv, __FILE__, __LINE__, "ss", "can't set TCP_DEFER_ACCEPT: ", strerror(errno));
440: }
441: #endif
442: } else {
443: #ifdef SO_ACCEPTFILTER
444: /* FreeBSD accf_http filter */
445: struct accept_filter_arg afa;
446: memset(&afa, 0, sizeof(afa));
447: strcpy(afa.af_name, "httpready");
448: if (setsockopt(srv_socket->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) {
449: if (errno != ENOENT) {
450: log_error_write(srv, __FILE__, __LINE__, "ss", "can't set accept-filter 'httpready': ", strerror(errno));
451: }
452: }
453: #endif
454: }
455:
456: srv_socket->is_ssl = s->ssl_enabled;
457:
458: if (srv->srv_sockets.size == 0) {
459: srv->srv_sockets.size = 4;
460: srv->srv_sockets.used = 0;
1.1.1.2 ! misho 461: srv->srv_sockets.ptr = malloc(srv->srv_sockets.size * sizeof(server_socket*));
1.1 misho 462: } else if (srv->srv_sockets.used == srv->srv_sockets.size) {
463: srv->srv_sockets.size += 4;
1.1.1.2 ! misho 464: srv->srv_sockets.ptr = realloc(srv->srv_sockets.ptr, srv->srv_sockets.size * sizeof(server_socket*));
1.1 misho 465: }
466:
467: srv->srv_sockets.ptr[srv->srv_sockets.used++] = srv_socket;
468:
469: buffer_free(b);
470:
471: return 0;
472:
473: error_free_socket:
474: if (srv_socket->fd != -1) {
475: /* check if server fd are already registered */
476: if (srv_socket->fde_ndx != -1) {
477: fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd);
478: fdevent_unregister(srv->ev, srv_socket->fd);
479: }
480:
481: close(srv_socket->fd);
482: }
483: buffer_free(srv_socket->srv_token);
484: free(srv_socket);
485:
486: buffer_free(b);
487:
488: return -1;
489: }
490:
491: int network_close(server *srv) {
492: size_t i;
493: for (i = 0; i < srv->srv_sockets.used; i++) {
494: server_socket *srv_socket = srv->srv_sockets.ptr[i];
495:
496: if (srv_socket->fd != -1) {
497: /* check if server fd are already registered */
498: if (srv_socket->fde_ndx != -1) {
499: fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd);
500: fdevent_unregister(srv->ev, srv_socket->fd);
501: }
502:
503: close(srv_socket->fd);
504: }
505:
506: buffer_free(srv_socket->srv_token);
507:
508: free(srv_socket);
509: }
510:
511: free(srv->srv_sockets.ptr);
512:
513: return 0;
514: }
515:
516: typedef enum {
517: NETWORK_BACKEND_UNSET,
518: NETWORK_BACKEND_WRITE,
519: NETWORK_BACKEND_WRITEV,
520: NETWORK_BACKEND_LINUX_SENDFILE,
521: NETWORK_BACKEND_FREEBSD_SENDFILE,
522: NETWORK_BACKEND_SOLARIS_SENDFILEV
523: } network_backend_t;
524:
1.1.1.2 ! misho 525: #ifdef USE_OPENSSL
! 526: static X509* x509_load_pem_file(server *srv, const char *file) {
! 527: BIO *in;
! 528: X509 *x = NULL;
! 529:
! 530: in = BIO_new(BIO_s_file());
! 531: if (NULL == in) {
! 532: log_error_write(srv, __FILE__, __LINE__, "S", "SSL: BIO_new(BIO_s_file()) failed");
! 533: goto error;
! 534: }
! 535:
! 536: if (BIO_read_filename(in,file) <= 0) {
! 537: log_error_write(srv, __FILE__, __LINE__, "SSS", "SSL: BIO_read_filename('", file,"') failed");
! 538: goto error;
! 539: }
! 540: x = PEM_read_bio_X509(in, NULL, NULL, NULL);
! 541:
! 542: if (NULL == x) {
! 543: log_error_write(srv, __FILE__, __LINE__, "SSS", "SSL: couldn't read X509 certificate from '", file,"'");
! 544: goto error;
! 545: }
! 546:
! 547: BIO_free(in);
! 548: return x;
! 549:
! 550: error:
! 551: if (NULL != in) BIO_free(in);
! 552: return NULL;
! 553: }
! 554:
! 555: static EVP_PKEY* evp_pkey_load_pem_file(server *srv, const char *file) {
! 556: BIO *in;
! 557: EVP_PKEY *x = NULL;
! 558:
! 559: in=BIO_new(BIO_s_file());
! 560: if (NULL == in) {
! 561: log_error_write(srv, __FILE__, __LINE__, "s", "SSL: BIO_new(BIO_s_file()) failed");
! 562: goto error;
! 563: }
! 564:
! 565: if (BIO_read_filename(in,file) <= 0) {
! 566: log_error_write(srv, __FILE__, __LINE__, "SSS", "SSL: BIO_read_filename('", file,"') failed");
! 567: goto error;
! 568: }
! 569: x = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
! 570:
! 571: if (NULL == x) {
! 572: log_error_write(srv, __FILE__, __LINE__, "SSS", "SSL: couldn't read private key from '", file,"'");
! 573: goto error;
! 574: }
! 575:
! 576: BIO_free(in);
! 577: return x;
! 578:
! 579: error:
! 580: if (NULL != in) BIO_free(in);
! 581: return NULL;
! 582: }
! 583:
! 584: static int network_openssl_load_pemfile(server *srv, size_t ndx) {
! 585: specific_config *s = srv->config_storage[ndx];
! 586:
! 587: #ifdef OPENSSL_NO_TLSEXT
! 588: {
! 589: data_config *dc = (data_config *)srv->config_context->data[ndx];
! 590: if ((ndx > 0 && (COMP_SERVER_SOCKET != dc->comp || dc->cond != CONFIG_COND_EQ))
! 591: || !s->ssl_enabled) {
! 592: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
! 593: "ssl.pemfile only works in SSL socket binding context as openssl version does not support TLS extensions");
! 594: return -1;
! 595: }
! 596: }
! 597: #endif
! 598:
! 599: if (NULL == (s->ssl_pemfile_x509 = x509_load_pem_file(srv, s->ssl_pemfile->ptr))) return -1;
! 600: if (NULL == (s->ssl_pemfile_pkey = evp_pkey_load_pem_file(srv, s->ssl_pemfile->ptr))) return -1;
! 601:
! 602: if (!X509_check_private_key(s->ssl_pemfile_x509, s->ssl_pemfile_pkey)) {
! 603: log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:",
! 604: "Private key does not match the certificate public key, reason:",
! 605: ERR_error_string(ERR_get_error(), NULL),
! 606: s->ssl_pemfile);
! 607: return -1;
! 608: }
! 609:
! 610: return 0;
! 611: }
! 612: #endif
! 613:
1.1 misho 614: int network_init(server *srv) {
615: buffer *b;
1.1.1.2 ! misho 616: size_t i, j;
1.1 misho 617: network_backend_t backend;
618:
619: #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
620: #ifndef OPENSSL_NO_ECDH
621: EC_KEY *ecdh;
622: int nid;
623: #endif
624: #endif
625:
626: #ifdef USE_OPENSSL
627: # ifndef OPENSSL_NO_DH
628: DH *dh;
629: # endif
630: BIO *bio;
631:
632: /* 1024-bit MODP Group with 160-bit prime order subgroup (RFC5114)
633: * -----BEGIN DH PARAMETERS-----
634: * MIIBDAKBgQCxC4+WoIDgHd6S3l6uXVTsUsmfvPsGo8aaap3KUtI7YWBz4oZ1oj0Y
635: * mDjvHi7mUsAT7LSuqQYRIySXXDzUm4O/rMvdfZDEvXCYSI6cIZpzck7/1vrlZEc4
636: * +qMaT/VbzMChUa9fDci0vUW/N982XBpl5oz9p21NpwjfH7K8LkpDcQKBgQCk0cvV
637: * w/00EmdlpELvuZkF+BBN0lisUH/WQGz/FCZtMSZv6h5cQVZLd35pD1UE8hMWAhe0
638: * sBuIal6RVH+eJ0n01/vX07mpLuGQnQ0iY/gKdqaiTAh6CR9THb8KAWm2oorWYqTR
639: * jnOvoy13nVkY0IvIhY9Nzvl8KiSFXm7rIrOy5QICAKA=
640: * -----END DH PARAMETERS-----
641: */
642:
643: static const unsigned char dh1024_p[]={
644: 0xB1,0x0B,0x8F,0x96,0xA0,0x80,0xE0,0x1D,0xDE,0x92,0xDE,0x5E,
645: 0xAE,0x5D,0x54,0xEC,0x52,0xC9,0x9F,0xBC,0xFB,0x06,0xA3,0xC6,
646: 0x9A,0x6A,0x9D,0xCA,0x52,0xD2,0x3B,0x61,0x60,0x73,0xE2,0x86,
647: 0x75,0xA2,0x3D,0x18,0x98,0x38,0xEF,0x1E,0x2E,0xE6,0x52,0xC0,
648: 0x13,0xEC,0xB4,0xAE,0xA9,0x06,0x11,0x23,0x24,0x97,0x5C,0x3C,
649: 0xD4,0x9B,0x83,0xBF,0xAC,0xCB,0xDD,0x7D,0x90,0xC4,0xBD,0x70,
650: 0x98,0x48,0x8E,0x9C,0x21,0x9A,0x73,0x72,0x4E,0xFF,0xD6,0xFA,
651: 0xE5,0x64,0x47,0x38,0xFA,0xA3,0x1A,0x4F,0xF5,0x5B,0xCC,0xC0,
652: 0xA1,0x51,0xAF,0x5F,0x0D,0xC8,0xB4,0xBD,0x45,0xBF,0x37,0xDF,
653: 0x36,0x5C,0x1A,0x65,0xE6,0x8C,0xFD,0xA7,0x6D,0x4D,0xA7,0x08,
654: 0xDF,0x1F,0xB2,0xBC,0x2E,0x4A,0x43,0x71,
655: };
656:
657: static const unsigned char dh1024_g[]={
658: 0xA4,0xD1,0xCB,0xD5,0xC3,0xFD,0x34,0x12,0x67,0x65,0xA4,0x42,
659: 0xEF,0xB9,0x99,0x05,0xF8,0x10,0x4D,0xD2,0x58,0xAC,0x50,0x7F,
660: 0xD6,0x40,0x6C,0xFF,0x14,0x26,0x6D,0x31,0x26,0x6F,0xEA,0x1E,
661: 0x5C,0x41,0x56,0x4B,0x77,0x7E,0x69,0x0F,0x55,0x04,0xF2,0x13,
662: 0x16,0x02,0x17,0xB4,0xB0,0x1B,0x88,0x6A,0x5E,0x91,0x54,0x7F,
663: 0x9E,0x27,0x49,0xF4,0xD7,0xFB,0xD7,0xD3,0xB9,0xA9,0x2E,0xE1,
664: 0x90,0x9D,0x0D,0x22,0x63,0xF8,0x0A,0x76,0xA6,0xA2,0x4C,0x08,
665: 0x7A,0x09,0x1F,0x53,0x1D,0xBF,0x0A,0x01,0x69,0xB6,0xA2,0x8A,
666: 0xD6,0x62,0xA4,0xD1,0x8E,0x73,0xAF,0xA3,0x2D,0x77,0x9D,0x59,
667: 0x18,0xD0,0x8B,0xC8,0x85,0x8F,0x4D,0xCE,0xF9,0x7C,0x2A,0x24,
668: 0x85,0x5E,0x6E,0xEB,0x22,0xB3,0xB2,0xE5,
669: };
670: #endif
671:
672: struct nb_map {
673: network_backend_t nb;
674: const char *name;
675: } network_backends[] = {
676: /* lowest id wins */
677: #if defined USE_LINUX_SENDFILE
678: { NETWORK_BACKEND_LINUX_SENDFILE, "linux-sendfile" },
679: #endif
680: #if defined USE_FREEBSD_SENDFILE
681: { NETWORK_BACKEND_FREEBSD_SENDFILE, "freebsd-sendfile" },
682: #endif
683: #if defined USE_SOLARIS_SENDFILEV
684: { NETWORK_BACKEND_SOLARIS_SENDFILEV, "solaris-sendfilev" },
685: #endif
686: #if defined USE_WRITEV
687: { NETWORK_BACKEND_WRITEV, "writev" },
688: #endif
689: { NETWORK_BACKEND_WRITE, "write" },
690: { NETWORK_BACKEND_UNSET, NULL }
691: };
692:
693: #ifdef USE_OPENSSL
694: /* load SSL certificates */
695: for (i = 0; i < srv->config_context->used; i++) {
696: specific_config *s = srv->config_storage[i];
697: #ifndef SSL_OP_NO_COMPRESSION
698: # define SSL_OP_NO_COMPRESSION 0
699: #endif
700: long ssloptions =
701: SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_NO_COMPRESSION;
702:
1.1.1.2 ! misho 703: if (buffer_is_empty(s->ssl_pemfile) && buffer_is_empty(s->ssl_ca_file)) continue;
1.1 misho 704:
705: if (srv->ssl_is_init == 0) {
706: SSL_load_error_strings();
707: SSL_library_init();
708: OpenSSL_add_all_algorithms();
709: srv->ssl_is_init = 1;
710:
711: if (0 == RAND_status()) {
712: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
713: "not enough entropy in the pool");
714: return -1;
715: }
716: }
717:
1.1.1.2 ! misho 718: if (!buffer_is_empty(s->ssl_pemfile)) {
! 719: #ifdef OPENSSL_NO_TLSEXT
! 720: data_config *dc = (data_config *)srv->config_context->data[i];
! 721: if (COMP_HTTP_HOST == dc->comp) {
! 722: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
! 723: "can't use ssl.pemfile with $HTTP[\"host\"], openssl version does not support TLS extensions");
! 724: return -1;
! 725: }
! 726: #endif
! 727: if (network_openssl_load_pemfile(srv, i)) return -1;
! 728: }
! 729:
! 730:
! 731: if (!buffer_is_empty(s->ssl_ca_file)) {
! 732: s->ssl_ca_file_cert_names = SSL_load_client_CA_file(s->ssl_ca_file->ptr);
! 733: if (NULL == s->ssl_ca_file_cert_names) {
! 734: log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:",
! 735: ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file);
! 736: }
! 737: }
! 738:
! 739: if (buffer_is_empty(s->ssl_pemfile) || !s->ssl_enabled) continue;
! 740:
1.1 misho 741: if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) {
742: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
743: ERR_error_string(ERR_get_error(), NULL));
744: return -1;
745: }
746:
1.1.1.2 ! misho 747: /* completely useless identifier; required for client cert verification to work with sessions */
! 748: if (0 == SSL_CTX_set_session_id_context(s->ssl_ctx, (const unsigned char*) CONST_STR_LEN("lighttpd"))) {
! 749: log_error_write(srv, __FILE__, __LINE__, "ss:s", "SSL:",
! 750: "failed to set session context",
! 751: ERR_error_string(ERR_get_error(), NULL));
! 752: return -1;
! 753: }
! 754:
1.1 misho 755: if (s->ssl_empty_fragments) {
756: #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
757: ssloptions &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
758: #else
759: ssloptions &= ~0x00000800L; /* hardcode constant */
760: log_error_write(srv, __FILE__, __LINE__, "ss", "WARNING: SSL:",
761: "'insert empty fragments' not supported by the openssl version used to compile lighttpd with");
762: #endif
763: }
764:
765: SSL_CTX_set_options(s->ssl_ctx, ssloptions);
766: SSL_CTX_set_info_callback(s->ssl_ctx, ssl_info_callback);
767:
768: if (!s->ssl_use_sslv2) {
769: /* disable SSLv2 */
770: if (!(SSL_OP_NO_SSLv2 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2))) {
771: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
772: ERR_error_string(ERR_get_error(), NULL));
773: return -1;
774: }
775: }
776:
777: if (!s->ssl_use_sslv3) {
778: /* disable SSLv3 */
779: if (!(SSL_OP_NO_SSLv3 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv3))) {
780: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
781: ERR_error_string(ERR_get_error(), NULL));
782: return -1;
783: }
784: }
785:
786: if (!buffer_is_empty(s->ssl_cipher_list)) {
787: /* Disable support for low encryption ciphers */
788: if (SSL_CTX_set_cipher_list(s->ssl_ctx, s->ssl_cipher_list->ptr) != 1) {
789: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
790: ERR_error_string(ERR_get_error(), NULL));
791: return -1;
792: }
793:
794: if (s->ssl_honor_cipher_order) {
795: SSL_CTX_set_options(s->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
796: }
797: }
798:
799: #ifndef OPENSSL_NO_DH
800: /* Support for Diffie-Hellman key exchange */
801: if (!buffer_is_empty(s->ssl_dh_file)) {
802: /* DH parameters from file */
803: bio = BIO_new_file((char *) s->ssl_dh_file->ptr, "r");
804: if (bio == NULL) {
805: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unable to open file", s->ssl_dh_file->ptr);
806: return -1;
807: }
808: dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
809: BIO_free(bio);
810: if (dh == NULL) {
811: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: PEM_read_bio_DHparams failed", s->ssl_dh_file->ptr);
812: return -1;
813: }
814: } else {
815: /* Default DH parameters from RFC5114 */
816: dh = DH_new();
817: if (dh == NULL) {
818: log_error_write(srv, __FILE__, __LINE__, "s", "SSL: DH_new () failed");
819: return -1;
820: }
821: dh->p = BN_bin2bn(dh1024_p,sizeof(dh1024_p), NULL);
822: dh->g = BN_bin2bn(dh1024_g,sizeof(dh1024_g), NULL);
823: dh->length = 160;
824: if ((dh->p == NULL) || (dh->g == NULL)) {
825: DH_free(dh);
826: log_error_write(srv, __FILE__, __LINE__, "s", "SSL: BN_bin2bn () failed");
827: return -1;
828: }
829: }
830: SSL_CTX_set_tmp_dh(s->ssl_ctx,dh);
831: SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_DH_USE);
832: DH_free(dh);
833: #else
834: if (!buffer_is_empty(s->ssl_dh_file)) {
835: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: openssl compiled without DH support, can't load parameters from", s->ssl_dh_file->ptr);
836: }
837: #endif
838:
839: #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
840: #ifndef OPENSSL_NO_ECDH
841: /* Support for Elliptic-Curve Diffie-Hellman key exchange */
842: if (!buffer_is_empty(s->ssl_ec_curve)) {
843: /* OpenSSL only supports the "named curves" from RFC 4492, section 5.1.1. */
844: nid = OBJ_sn2nid((char *) s->ssl_ec_curve->ptr);
845: if (nid == 0) {
846: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unknown curve name", s->ssl_ec_curve->ptr);
847: return -1;
848: }
849: } else {
850: /* Default curve */
851: nid = OBJ_sn2nid("prime256v1");
852: }
853: ecdh = EC_KEY_new_by_curve_name(nid);
854: if (ecdh == NULL) {
855: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unable to create curve", s->ssl_ec_curve->ptr);
856: return -1;
857: }
858: SSL_CTX_set_tmp_ecdh(s->ssl_ctx,ecdh);
859: SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_ECDH_USE);
860: EC_KEY_free(ecdh);
861: #endif
862: #endif
863:
1.1.1.2 ! misho 864: /* load all ssl.ca-files specified in the config into each SSL_CTX to be prepared for SNI */
! 865: for (j = 0; j < srv->config_context->used; j++) {
! 866: specific_config *s1 = srv->config_storage[j];
! 867:
! 868: if (!buffer_is_empty(s1->ssl_ca_file)) {
! 869: if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s1->ssl_ca_file->ptr, NULL)) {
1.1 misho 870: log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:",
1.1.1.2 ! misho 871: ERR_error_string(ERR_get_error(), NULL), s1->ssl_ca_file);
1.1 misho 872: return -1;
873: }
1.1.1.2 ! misho 874: }
! 875: }
! 876:
! 877: if (s->ssl_verifyclient) {
! 878: if (NULL == s->ssl_ca_file_cert_names) {
! 879: log_error_write(srv, __FILE__, __LINE__, "s",
! 880: "SSL: You specified ssl.verifyclient.activate but no ca_file"
1.1 misho 881: );
1.1.1.2 ! misho 882: return -1;
1.1 misho 883: }
1.1.1.2 ! misho 884: SSL_CTX_set_client_CA_list(s->ssl_ctx, SSL_dup_CA_list(s->ssl_ca_file_cert_names));
! 885: SSL_CTX_set_verify(
! 886: s->ssl_ctx,
! 887: SSL_VERIFY_PEER | (s->ssl_verifyclient_enforce ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0),
! 888: NULL
1.1 misho 889: );
1.1.1.2 ! misho 890: SSL_CTX_set_verify_depth(s->ssl_ctx, s->ssl_verifyclient_depth);
1.1 misho 891: }
892:
1.1.1.2 ! misho 893: if (SSL_CTX_use_certificate(s->ssl_ctx, s->ssl_pemfile_x509) < 0) {
1.1 misho 894: log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:",
895: ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile);
896: return -1;
897: }
898:
1.1.1.2 ! misho 899: if (SSL_CTX_use_PrivateKey(s->ssl_ctx, s->ssl_pemfile_pkey) < 0) {
1.1 misho 900: log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:",
901: ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile);
902: return -1;
903: }
904:
905: if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) {
906: log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:",
907: "Private key does not match the certificate public key, reason:",
908: ERR_error_string(ERR_get_error(), NULL),
909: s->ssl_pemfile);
910: return -1;
911: }
912: SSL_CTX_set_default_read_ahead(s->ssl_ctx, 1);
913: SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
914:
915: # ifndef OPENSSL_NO_TLSEXT
916: if (!SSL_CTX_set_tlsext_servername_callback(s->ssl_ctx, network_ssl_servername_callback) ||
917: !SSL_CTX_set_tlsext_servername_arg(s->ssl_ctx, srv)) {
918: log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
919: "failed to initialize TLS servername callback, openssl library does not support TLS servername extension");
920: return -1;
921: }
922: # endif
923: }
924: #endif
925:
926: b = buffer_init();
927:
928: buffer_copy_string_buffer(b, srv->srvconf.bindhost);
929: buffer_append_string_len(b, CONST_STR_LEN(":"));
930: buffer_append_long(b, srv->srvconf.port);
931:
932: if (0 != network_server_init(srv, b, srv->config_storage[0])) {
1.1.1.2 ! misho 933: buffer_free(b);
1.1 misho 934: return -1;
935: }
936: buffer_free(b);
937:
938: #ifdef USE_OPENSSL
939: srv->network_ssl_backend_write = network_write_chunkqueue_openssl;
940: #endif
941:
942: /* get a usefull default */
943: backend = network_backends[0].nb;
944:
945: /* match name against known types */
946: if (!buffer_is_empty(srv->srvconf.network_backend)) {
947: for (i = 0; network_backends[i].name; i++) {
948: /**/
949: if (buffer_is_equal_string(srv->srvconf.network_backend, network_backends[i].name, strlen(network_backends[i].name))) {
950: backend = network_backends[i].nb;
951: break;
952: }
953: }
954: if (NULL == network_backends[i].name) {
955: /* we don't know it */
956:
957: log_error_write(srv, __FILE__, __LINE__, "sb",
958: "server.network-backend has a unknown value:",
959: srv->srvconf.network_backend);
960:
961: return -1;
962: }
963: }
964:
965: switch(backend) {
966: case NETWORK_BACKEND_WRITE:
967: srv->network_backend_write = network_write_chunkqueue_write;
968: break;
969: #ifdef USE_WRITEV
970: case NETWORK_BACKEND_WRITEV:
971: srv->network_backend_write = network_write_chunkqueue_writev;
972: break;
973: #endif
974: #ifdef USE_LINUX_SENDFILE
975: case NETWORK_BACKEND_LINUX_SENDFILE:
976: srv->network_backend_write = network_write_chunkqueue_linuxsendfile;
977: break;
978: #endif
979: #ifdef USE_FREEBSD_SENDFILE
980: case NETWORK_BACKEND_FREEBSD_SENDFILE:
981: srv->network_backend_write = network_write_chunkqueue_freebsdsendfile;
982: break;
983: #endif
984: #ifdef USE_SOLARIS_SENDFILEV
985: case NETWORK_BACKEND_SOLARIS_SENDFILEV:
986: srv->network_backend_write = network_write_chunkqueue_solarissendfilev;
987: break;
988: #endif
989: default:
990: return -1;
991: }
992:
993: /* check for $SERVER["socket"] */
994: for (i = 1; i < srv->config_context->used; i++) {
995: data_config *dc = (data_config *)srv->config_context->data[i];
996: specific_config *s = srv->config_storage[i];
997:
998: /* not our stage */
999: if (COMP_SERVER_SOCKET != dc->comp) continue;
1000:
1001: if (dc->cond != CONFIG_COND_EQ) continue;
1002:
1003: /* check if we already know this socket,
1004: * if yes, don't init it */
1005: for (j = 0; j < srv->srv_sockets.used; j++) {
1006: if (buffer_is_equal(srv->srv_sockets.ptr[j]->srv_token, dc->string)) {
1007: break;
1008: }
1009: }
1010:
1011: if (j == srv->srv_sockets.used) {
1012: if (0 != network_server_init(srv, dc->string, s)) return -1;
1013: }
1014: }
1015:
1016: return 0;
1017: }
1018:
1019: int network_register_fdevents(server *srv) {
1020: size_t i;
1021:
1022: if (-1 == fdevent_reset(srv->ev)) {
1023: return -1;
1024: }
1025:
1026: /* register fdevents after reset */
1027: for (i = 0; i < srv->srv_sockets.used; i++) {
1028: server_socket *srv_socket = srv->srv_sockets.ptr[i];
1029:
1030: fdevent_register(srv->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket);
1031: fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN);
1032: }
1033: return 0;
1034: }
1035:
1036: int network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) {
1037: int ret = -1;
1038: off_t written = 0;
1039: #ifdef TCP_CORK
1040: int corked = 0;
1041: #endif
1042: server_socket *srv_socket = con->srv_socket;
1043:
1044: if (con->conf.global_kbytes_per_second) {
1045: off_t limit = con->conf.global_kbytes_per_second * 1024 - *(con->conf.global_bytes_per_second_cnt_ptr);
1046: if (limit <= 0) {
1047: /* we reached the global traffic limit */
1048:
1049: con->traffic_limit_reached = 1;
1050: joblist_append(srv, con);
1051:
1052: return 1;
1053: } else {
1054: if (max_bytes > limit) max_bytes = limit;
1055: }
1056: }
1057:
1058: if (con->conf.kbytes_per_second) {
1059: off_t limit = con->conf.kbytes_per_second * 1024 - con->bytes_written_cur_second;
1060: if (limit <= 0) {
1061: /* we reached the traffic limit */
1062:
1063: con->traffic_limit_reached = 1;
1064: joblist_append(srv, con);
1065:
1066: return 1;
1067: } else {
1068: if (max_bytes > limit) max_bytes = limit;
1069: }
1070: }
1071:
1072: written = cq->bytes_out;
1073:
1074: #ifdef TCP_CORK
1075: /* Linux: put a cork into the socket as we want to combine the write() calls
1076: * but only if we really have multiple chunks
1077: */
1078: if (cq->first && cq->first->next) {
1079: corked = 1;
1080: setsockopt(con->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
1081: }
1082: #endif
1083:
1084: if (srv_socket->is_ssl) {
1085: #ifdef USE_OPENSSL
1086: ret = srv->network_ssl_backend_write(srv, con, con->ssl, cq, max_bytes);
1087: #endif
1088: } else {
1089: ret = srv->network_backend_write(srv, con, con->fd, cq, max_bytes);
1090: }
1091:
1092: if (ret >= 0) {
1093: chunkqueue_remove_finished_chunks(cq);
1094: ret = chunkqueue_is_empty(cq) ? 0 : 1;
1095: }
1096:
1097: #ifdef TCP_CORK
1098: if (corked) {
1099: corked = 0;
1100: setsockopt(con->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked));
1101: }
1102: #endif
1103:
1104: written = cq->bytes_out - written;
1105: con->bytes_written += written;
1106: con->bytes_written_cur_second += written;
1107:
1108: *(con->conf.global_bytes_per_second_cnt_ptr) += written;
1109:
1110: return ret;
1111: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>