diff options
Diffstat (limited to 'data/lighttpd/lighttpd-1.4.53/src/network.c')
-rw-r--r-- | data/lighttpd/lighttpd-1.4.53/src/network.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/data/lighttpd/lighttpd-1.4.53/src/network.c b/data/lighttpd/lighttpd-1.4.53/src/network.c new file mode 100644 index 000000000..016d0d5bb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/network.c @@ -0,0 +1,540 @@ +#include "first.h" + +#include "network.h" +#include "base.h" +#include "fdevent.h" +#include "log.h" +#include "connections.h" +#include "configfile.h" +#include "sock_addr.h" + +#include "network_write.h" +#include "sys-socket.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +void +network_accept_tcp_nagle_disable (const int fd) +{ + static int noinherit_tcpnodelay = -1; + int opt; + + if (!noinherit_tcpnodelay) /* TCP_NODELAY inherited from listen socket */ + return; + + if (noinherit_tcpnodelay < 0) { + socklen_t optlen = sizeof(opt); + if (0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen)) { + noinherit_tcpnodelay = !opt; + if (opt) /* TCP_NODELAY inherited from listen socket */ + return; + } + } + + opt = 1; + (void)setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); +} + +static handler_t network_server_handle_fdevent(server *srv, void *context, int revents) { + server_socket *srv_socket = (server_socket *)context; + connection *con; + int loops = 0; + + UNUSED(context); + + if (0 == (revents & FDEVENT_IN)) { + log_error_write(srv, __FILE__, __LINE__, "sdd", + "strange event for server socket", + srv_socket->fd, + revents); + return HANDLER_ERROR; + } + + /* accept()s at most 100 connections directly + * + * we jump out after 100 to give the waiting connections a chance */ + for (loops = 0; loops < 100 && NULL != (con = connection_accept(srv, srv_socket)); loops++) { + connection_state_machine(srv, con); + } + return HANDLER_GO_ON; +} + +static void network_host_normalize_addr_str(buffer *host, sock_addr *addr) { + buffer_clear(host); + sock_addr_stringify_append_buffer(host, addr); +} + +static int network_host_parse_addr(server *srv, sock_addr *addr, socklen_t *addr_len, buffer *host, int use_ipv6) { + char *h; + char *colon = NULL; + const char *chost; + sa_family_t family = use_ipv6 ? AF_INET6 : AF_INET; + unsigned int port = srv->srvconf.port; + if (buffer_string_is_empty(host)) { + log_error_write(srv, __FILE__, __LINE__, "s", "value of $SERVER[\"socket\"] must not be empty"); + return -1; + } + h = host->ptr; + if (h[0] == '/') { + #ifdef HAVE_SYS_UN_H + return (1 == sock_addr_from_str_hints(srv,addr,addr_len,h,AF_UNIX,0)) + ? 0 + : -1; + #else + log_error_write(srv, __FILE__, __LINE__, "s", + "ERROR: Unix Domain sockets are not supported."); + return -1; + #endif + } + buffer_copy_buffer(srv->tmp_buf, host); + h = srv->tmp_buf->ptr; + if (h[0] == '[') { + family = AF_INET6; + if ((h = strchr(h, ']'))) { + *h++ = '\0'; + if (*h == ':') colon = h; + } /*(else should not happen; validated in configparser.y)*/ + h = srv->tmp_buf->ptr+1; + } + else { + colon = strrchr(h, ':'); + } + if (colon) { + *colon++ = '\0'; + port = strtol(colon, NULL, 10); + if (port == 0 || port > 65535) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "port not set or out of range:", port); + return -1; + } + } + chost = *h ? h : family == AF_INET ? "0.0.0.0" : "::"; + if (1 != sock_addr_from_str_hints(srv,addr,addr_len,chost,family,port)) { + return -1; + } + return 0; +} + +static void network_srv_sockets_append(server *srv, server_socket *srv_socket) { + if (srv->srv_sockets.size == 0) { + srv->srv_sockets.size = 4; + srv->srv_sockets.used = 0; + srv->srv_sockets.ptr = malloc(srv->srv_sockets.size * sizeof(server_socket*)); + force_assert(NULL != srv->srv_sockets.ptr); + } else if (srv->srv_sockets.used == srv->srv_sockets.size) { + srv->srv_sockets.size += 4; + srv->srv_sockets.ptr = realloc(srv->srv_sockets.ptr, srv->srv_sockets.size * sizeof(server_socket*)); + force_assert(NULL != srv->srv_sockets.ptr); + } + srv->srv_sockets.ptr[srv->srv_sockets.used++] = srv_socket; +} + +static int network_server_init(server *srv, buffer *host_token, size_t sidx, int stdin_fd) { + server_socket *srv_socket; + const char *host; + specific_config *s = srv->config_storage[sidx]; + socklen_t addr_len = sizeof(sock_addr); + sock_addr addr; + int family = 0; + int set_v6only = 0; + + if (buffer_string_is_empty(host_token)) { + log_error_write(srv, __FILE__, __LINE__, "s", "value of $SERVER[\"socket\"] must not be empty"); + return -1; + } + + /* check if we already know this socket, and if yes, don't init it + * (optimization: check strings here to filter out exact matches; + * binary addresses are matched further below) */ + for (size_t i = 0; i < srv->srv_sockets.used; ++i) { + if (buffer_is_equal(srv->srv_sockets.ptr[i]->srv_token, host_token)) { + buffer_copy_buffer(host_token, srv->srv_sockets.ptr[i]->srv_token); + return 0; + } + } + + host = host_token->ptr; + if ((s->use_ipv6 && (*host == '\0' || *host == ':')) || (host[0] == '[' && host[1] == ']')) { + 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"); + } + if (*host == '[') s->use_ipv6 = 1; + + memset(&addr, 0, sizeof(addr)); + if (-1 != stdin_fd) { + if (-1 == getsockname(stdin_fd, (struct sockaddr *)&addr, &addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "getsockname()", strerror(errno)); + return -1; + } + } else if (0 != network_host_parse_addr(srv, &addr, &addr_len, host_token, s->use_ipv6)) { + return -1; + } + + family = sock_addr_get_family(&addr); + + #ifdef HAVE_IPV6 + if (*host != '\0' && AF_INET6 == family) { + if (s->set_v6only) { + set_v6only = 1; + } else { + 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"); + } + } + #endif + + network_host_normalize_addr_str(host_token, &addr); + host = host_token->ptr; + + if (srv->srvconf.preflight_check) { + return 0; + } + + /* check if we already know this socket (after potential DNS resolution), and if yes, don't init it */ + for (size_t i = 0; i < srv->srv_sockets.used; ++i) { + if (0 == memcmp(&srv->srv_sockets.ptr[i]->addr, &addr, sizeof(addr))) { + return 0; + } + } + + srv_socket = calloc(1, sizeof(*srv_socket)); + force_assert(NULL != srv_socket); + memcpy(&srv_socket->addr, &addr, addr_len); + srv_socket->fd = -1; + srv_socket->fde_ndx = -1; + srv_socket->sidx = sidx; + srv_socket->is_ssl = s->ssl_enabled; + srv_socket->srv_token = buffer_init_buffer(host_token); + + network_srv_sockets_append(srv, srv_socket); + + if (srv->sockets_disabled) { /* lighttpd -1 (one-shot mode) */ + return 0; + } + + if (srv->srvconf.systemd_socket_activation) { + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + if (0 != memcmp(&srv->srv_sockets_inherited.ptr[i]->addr, &srv_socket->addr, addr_len)) continue; + if ((unsigned short)~0u == srv->srv_sockets_inherited.ptr[i]->sidx) { + srv->srv_sockets_inherited.ptr[i]->sidx = sidx; + } + stdin_fd = srv->srv_sockets_inherited.ptr[i]->fd; + break; + } + } + + if (-1 != stdin_fd) { + srv_socket->fd = stdin_fd; + if (-1 == fdevent_fcntl_set_nb_cloexec(srv->ev, stdin_fd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl:", strerror(errno)); + return -1; + } + } else +#ifdef HAVE_SYS_UN_H + if (AF_UNIX == family) { + /* check if the socket exists and try to connect to it. */ + force_assert(host); /*(static analysis hint)*/ + if (-1 == (srv_socket->fd = fdevent_socket_cloexec(AF_UNIX, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); + return -1; + } + if (0 == connect(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "server socket is still in use:", + host); + + + return -1; + } + + /* connect failed */ + switch(errno) { + case ECONNREFUSED: + unlink(host); + break; + case ENOENT: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sds", + "testing socket failed:", + host, strerror(errno)); + + return -1; + } + + fdevent_fcntl_set_nb(srv->ev, srv_socket->fd); + } else +#endif + { + if (-1 == (srv_socket->fd = fdevent_socket_nb_cloexec(family, SOCK_STREAM, IPPROTO_TCP))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); + return -1; + } + } + +#ifdef HAVE_IPV6 + if (set_v6only && -1 == stdin_fd) { + int val = 1; + if (-1 == setsockopt(srv_socket->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setsockopt(IPV6_V6ONLY) failed:", strerror(errno)); + return -1; + } + } +#endif + + /* */ + srv->cur_fds = srv_socket->fd; + + if (fdevent_set_so_reuseaddr(srv_socket->fd, 1) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setsockopt(SO_REUSEADDR) failed:", strerror(errno)); + return -1; + } + + if (family != AF_UNIX) { + if (fdevent_set_tcp_nodelay(srv_socket->fd, 1) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setsockopt(TCP_NODELAY) failed:", strerror(errno)); + return -1; + } + } + + if (-1 != stdin_fd) { } else + if (0 != bind(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "can't bind to socket:", host, strerror(errno)); + return -1; + } + + if (-1 != stdin_fd) { } else + if (AF_UNIX == family && !buffer_string_is_empty(s->socket_perms)) { + mode_t m = 0; + for (char *str = s->socket_perms->ptr; *str; ++str) { + m <<= 3; + m |= (*str - '0'); + } + if (0 != m && -1 == chmod(host, m)) { + log_error_write(srv, __FILE__, __LINE__, "sssbss", "chmod(\"", host, "\", ", s->socket_perms, "):", strerror(errno)); + } + } + + if (-1 != stdin_fd) { } else + if (-1 == listen(srv_socket->fd, s->listen_backlog)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed: ", strerror(errno)); + return -1; + } + + if (s->ssl_enabled) { +#ifdef TCP_DEFER_ACCEPT + } else if (s->defer_accept) { + int v = s->defer_accept; + if (-1 == setsockopt(srv_socket->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &v, sizeof(v))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't set TCP_DEFER_ACCEPT: ", strerror(errno)); + } +#endif +#if defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonFly__) + } else if (!buffer_is_empty(s->bsd_accept_filter) + && (buffer_is_equal_string(s->bsd_accept_filter, CONST_STR_LEN("httpready")) + || buffer_is_equal_string(s->bsd_accept_filter, CONST_STR_LEN("dataready")))) { +#ifdef SO_ACCEPTFILTER + /* FreeBSD accf_http filter */ + struct accept_filter_arg afa; + memset(&afa, 0, sizeof(afa)); + strncpy(afa.af_name, s->bsd_accept_filter->ptr, sizeof(afa.af_name)); + if (setsockopt(srv_socket->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) { + if (errno != ENOENT) { + log_error_write(srv, __FILE__, __LINE__, "SBss", "can't set accept-filter '", s->bsd_accept_filter, "':", strerror(errno)); + } + } +#endif +#endif + } + + return 0; +} + +int network_close(server *srv) { + size_t i; + for (i = 0; i < srv->srv_sockets.used; i++) { + server_socket *srv_socket = srv->srv_sockets.ptr[i]; + if (srv_socket->fd != -1) { + network_unregister_sock(srv, srv_socket); + close(srv_socket->fd); + } + + buffer_free(srv_socket->srv_token); + + free(srv_socket); + } + + free(srv->srv_sockets.ptr); + srv->srv_sockets.ptr = NULL; + srv->srv_sockets.used = 0; + srv->srv_sockets.size = 0; + + for (i = 0; i < srv->srv_sockets_inherited.used; i++) { + server_socket *srv_socket = srv->srv_sockets_inherited.ptr[i]; + if (srv_socket->fd != -1 && srv_socket->sidx != (unsigned short)~0u) { + close(srv_socket->fd); + } + + buffer_free(srv_socket->srv_token); + + free(srv_socket); + } + + free(srv->srv_sockets_inherited.ptr); + srv->srv_sockets_inherited.ptr = NULL; + srv->srv_sockets_inherited.used = 0; + srv->srv_sockets_inherited.size = 0; + + return 0; +} + +static int network_socket_activation_nfds(server *srv, int nfds) { + buffer *host = buffer_init(); + socklen_t addr_len; + sock_addr addr; + int rc = 0; + nfds += 3; /* #define SD_LISTEN_FDS_START 3 */ + for (int fd = 3; fd < nfds; ++fd) { + addr_len = sizeof(sock_addr); + if (-1 == (rc = getsockname(fd, (struct sockaddr *)&addr, &addr_len))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "socket activation getsockname()", strerror(errno)); + break; + } + network_host_normalize_addr_str(host, &addr); + rc = network_server_init(srv, host, 0, fd); + if (0 != rc) break; + srv->srv_sockets.ptr[srv->srv_sockets.used-1]->sidx = (unsigned short)~0u; + } + buffer_free(host); + memcpy(&srv->srv_sockets_inherited, &srv->srv_sockets, sizeof(server_socket_array)); + memset(&srv->srv_sockets, 0, sizeof(server_socket_array)); + return rc; +} + +static int network_socket_activation_from_env(server *srv) { + char *listen_pid = getenv("LISTEN_PID"); + char *listen_fds = getenv("LISTEN_FDS"); + pid_t lpid = listen_pid ? (pid_t)strtoul(listen_pid,NULL,10) : 0; + int nfds = listen_fds ? atoi(listen_fds) : 0; + int rc = (lpid == getpid() && nfds > 0) + ? network_socket_activation_nfds(srv, nfds) + : 0; + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_FDNAMES"); + /*(upon graceful restart, unsetenv will result in no-op above)*/ + return rc; +} + +int network_init(server *srv, int stdin_fd) { + #ifdef __WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + if (0 != WSAStartup(wVersionRequested, &wsaData)) { + /* Tell the user that we could not find a usable WinSock DLL */ + return -1; + } + #endif + + if (0 != network_write_init(srv)) return -1; + + if (srv->srvconf.systemd_socket_activation) { + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + srv->srv_sockets_inherited.ptr[i]->sidx = (unsigned short)~0u; + } + if (0 != network_socket_activation_from_env(srv)) return -1; + if (0 == srv->srv_sockets_inherited.used) { + srv->srvconf.systemd_socket_activation = 0; + } + } + + /* process srv->srvconf.bindhost + * (skip if systemd socket activation is enabled and bindhost is empty; do not additionally listen on "*") */ + if (!srv->srvconf.systemd_socket_activation || !buffer_string_is_empty(srv->srvconf.bindhost)) { + int rc; + buffer *b = buffer_init(); + buffer_copy_buffer(b, srv->srvconf.bindhost); + if (b->ptr[0] != '/') { /*(skip adding port if unix socket path)*/ + buffer_append_string_len(b, CONST_STR_LEN(":")); + buffer_append_int(b, srv->srvconf.port); + } + + rc = (-1 == stdin_fd || 0 == srv->srv_sockets.used) + ? network_server_init(srv, b, 0, stdin_fd) + : close(stdin_fd);/*(graceful restart listening to "/dev/stdin")*/ + buffer_free(b); + if (0 != rc) return -1; + } + + /* check for $SERVER["socket"] */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + + /* not our stage */ + if (COMP_SERVER_SOCKET != dc->comp) continue; + + if (dc->cond == CONFIG_COND_NE) { + socklen_t addr_len = sizeof(sock_addr); + sock_addr addr; + if (0 != network_host_parse_addr(srv, &addr, &addr_len, dc->string, srv->config_storage[i]->use_ipv6)) { + return -1; + } + network_host_normalize_addr_str(dc->string, &addr); + continue; + } + + if (dc->cond != CONFIG_COND_EQ) continue; + + if (0 != network_server_init(srv, dc->string, i, -1)) return -1; + } + + if (srv->srvconf.systemd_socket_activation) { + /* activate any inherited sockets not explicitly listed in config file */ + server_socket *srv_socket; + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + if ((unsigned short)~0u != srv->srv_sockets_inherited.ptr[i]->sidx) continue; + srv->srv_sockets_inherited.ptr[i]->sidx = 0; + srv_socket = calloc(1, sizeof(server_socket)); + force_assert(NULL != srv_socket); + memcpy(srv_socket, srv->srv_sockets_inherited.ptr[i], sizeof(server_socket)); + network_srv_sockets_append(srv, srv_socket); + } + } + + return 0; +} + +void network_unregister_sock(server *srv, server_socket *srv_socket) { + if (-1 == srv_socket->fd || -1 == srv_socket->fde_ndx) return; + fdevent_event_del(srv->ev, &srv_socket->fde_ndx, srv_socket->fd); + fdevent_unregister(srv->ev, srv_socket->fd); +} + +int network_register_fdevents(server *srv) { + size_t i; + + if (-1 == fdevent_reset(srv->ev)) { + return -1; + } + + if (srv->sockets_disabled) return 0; /* lighttpd -1 (one-shot mode) */ + + /* register fdevents after reset */ + for (i = 0; i < srv->srv_sockets.used; i++) { + server_socket *srv_socket = srv->srv_sockets.ptr[i]; + + fdevent_register(srv->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket); + fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); + } + return 0; +} |