|
|
|
#include "first.h"
|
|
|
|
|
|
|
|
#include "base.h"
|
|
|
|
#include "buffer.h"
|
|
|
|
#include "network.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "rand.h"
|
|
|
|
#include "chunk.h"
|
|
|
|
#include "h2.h" /* h2_send_1xx() */
|
|
|
|
#include "http_auth.h" /* http_auth_dumbdata_reset() */
|
|
|
|
#include "http_vhostdb.h" /* http_vhostdb_dumbdata_reset() */
|
|
|
|
#include "fdevent.h"
|
|
|
|
#include "connections.h"
|
|
|
|
#include "sock_addr.h"
|
|
|
|
#include "stat_cache.h"
|
|
|
|
#include "plugin.h"
|
|
|
|
#include "plugin_config.h" /* config_plugin_value_tobool() */
|
|
|
|
#include "network_write.h" /* network_write_show_handlers() */
|
|
|
|
#include "reqpool.h" /* request_pool_init() request_pool_free() */
|
|
|
|
#include "response.h" /* http_response_send_1xx_cb_set() strftime_cache_reset() */
|
|
|
|
|
|
|
|
#ifdef HAVE_VERSIONSTAMP_H
|
|
|
|
# include "versionstamp.h"
|
|
|
|
#else
|
|
|
|
# define REPO_VERSION ""
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define PACKAGE_DESC PACKAGE_NAME "/" PACKAGE_VERSION REPO_VERSION
|
|
|
|
static const buffer default_server_tag = { CONST_STR_LEN(PACKAGE_DESC)+1, 0 };
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <locale.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_GETOPT_H
|
|
|
|
# include <getopt.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_VALGRIND_VALGRIND_H
|
|
|
|
# include <valgrind/valgrind.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
|
|
# include <sys/wait.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_PWD_H
|
|
|
|
# include <grp.h>
|
|
|
|
# include <pwd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_LOADAVG_H
|
|
|
|
# include <sys/loadavg.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
|
|
# include <sys/resource.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SYS_PRCTL_H
|
|
|
|
# include <sys/prctl.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "sys-crypto.h"
|
|
|
|
#if defined(USE_OPENSSL_CRYPTO) \
|
|
|
|
|| defined(USE_MBEDTLS_CRYPTO) \
|
|
|
|
|| defined(USE_NSS_CRYPTO) \
|
|
|
|
|| defined(USE_GNUTLS_CRYPTO) \
|
|
|
|
|| defined(USE_WOLFTLS_CRYPTO)
|
|
|
|
#define TEXT_SSL " (ssl)"
|
|
|
|
#else
|
|
|
|
#define TEXT_SSL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef __sgi
|
|
|
|
/* IRIX doesn't like the alarm based time() optimization */
|
|
|
|
/* #define USE_ALARM */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int oneshot_fd = 0;
|
|
|
|
static int oneshot_fdout = -1;
|
|
|
|
static fdnode *oneshot_fdn = NULL;
|
|
|
|
static int (*oneshot_read_cq)(connection *con, chunkqueue *cq, off_t max_bytes);
|
|
|
|
static volatile int pid_fd = -2;
|
|
|
|
static server_socket_array graceful_sockets;
|
|
|
|
static server_socket_array inherited_sockets;
|
|
|
|
static volatile sig_atomic_t graceful_restart = 0;
|
|
|
|
static volatile sig_atomic_t graceful_shutdown = 0;
|
|
|
|
static volatile sig_atomic_t srv_shutdown = 0;
|
|
|
|
static volatile sig_atomic_t handle_sig_child = 0;
|
|
|
|
static volatile sig_atomic_t handle_sig_alarm = 1;
|
|
|
|
static volatile sig_atomic_t handle_sig_hup = 0;
|
|
|
|
static time_t idle_limit = 0;
|
|
|
|
|
|
|
|
#if defined(HAVE_SIGACTION) && defined(SA_SIGINFO)
|
|
|
|
static volatile siginfo_t last_sigterm_info;
|
|
|
|
static volatile siginfo_t last_sighup_info;
|
|
|
|
|
|
|
|
static void sigaction_handler(int sig, siginfo_t *si, void *context) {
|
|
|
|
static const siginfo_t empty_siginfo;
|
|
|
|
UNUSED(context);
|
|
|
|
|
|
|
|
if (!si) *(const siginfo_t **)&si = &empty_siginfo;
|
|
|
|
|
|
|
|
switch (sig) {
|
|
|
|
case SIGTERM:
|
|
|
|
srv_shutdown = 1;
|
|
|
|
last_sigterm_info = *si;
|
|
|
|
break;
|
|
|
|
case SIGUSR1:
|
|
|
|
if (!graceful_shutdown) {
|
|
|
|
graceful_restart = 1;
|
|
|
|
graceful_shutdown = 1;
|
|
|
|
last_sigterm_info = *si;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SIGINT:
|
|
|
|
if (graceful_shutdown) {
|
|
|
|
if (2 == graceful_restart)
|
|
|
|
graceful_restart = 1;
|
|
|
|
else
|
|
|
|
srv_shutdown = 1;
|
|
|
|
} else {
|
|
|
|
graceful_shutdown = 1;
|
|
|
|
}
|
|
|
|
last_sigterm_info = *si;
|
|
|
|
|
|
|
|
break;
|
|
|
|
case SIGALRM:
|
|
|
|
handle_sig_alarm = 1;
|
|
|
|
break;
|
|
|
|
case SIGHUP:
|
|
|
|
handle_sig_hup = 1;
|
|
|
|
last_sighup_info = *si;
|
|
|
|
break;
|
|
|
|
case SIGCHLD:
|
|
|
|
handle_sig_child = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#elif defined(HAVE_SIGNAL) || defined(HAVE_SIGACTION)
|
|
|
|
static void signal_handler(int sig) {
|
|
|
|
switch (sig) {
|
|
|
|
case SIGTERM: srv_shutdown = 1; break;
|
|
|
|
case SIGUSR1:
|
|
|
|
if (!graceful_shutdown) {
|
|
|
|
graceful_restart = 1;
|
|
|
|
graceful_shutdown = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SIGINT:
|
|
|
|
if (graceful_shutdown) {
|
|
|
|
if (2 == graceful_restart)
|
|
|
|
graceful_restart = 1;
|
|
|
|
else
|
|
|
|
srv_shutdown = 1;
|
|
|
|
} else {
|
|
|
|
graceful_shutdown = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SIGALRM: handle_sig_alarm = 1; break;
|
|
|
|
case SIGHUP: handle_sig_hup = 1; break;
|
|
|
|
case SIGCHLD: handle_sig_child = 1; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_FORK
|
|
|
|
static int daemonize(void) {
|
|
|
|
int pipefd[2];
|
|
|
|
pid_t pid;
|
|
|
|
#ifdef SIGTTOU
|
|
|
|
signal(SIGTTOU, SIG_IGN);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGTTIN
|
|
|
|
signal(SIGTTIN, SIG_IGN);
|
|
|
|
#endif
|
|
|
|
#ifdef SIGTSTP
|
|
|
|
signal(SIGTSTP, SIG_IGN);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pipe(pipefd) < 0) exit(-1);
|
|
|
|
|
|
|
|
if (0 > (pid = fork())) exit(-1);
|
|
|
|
|
|
|
|
if (0 < pid) {
|
|
|
|
char buf;
|
|
|
|
ssize_t bytes;
|
|
|
|
|
|
|
|
close(pipefd[1]);
|
|
|
|
/* parent waits for grandchild to be ready */
|
|
|
|
do {
|
|
|
|
bytes = read(pipefd[0], &buf, sizeof(buf));
|
|
|
|
} while (bytes < 0 && EINTR == errno);
|
|
|
|
close(pipefd[0]);
|
|
|
|
|
|
|
|
if (bytes <= 0) {
|
|
|
|
/* closed fd (without writing) == failure in grandchild */
|
|
|
|
fputs("daemonized server failed to start; check error log for details\n", stderr);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(pipefd[0]);
|
|
|
|
|
|
|
|
if (-1 == setsid()) exit(0);
|
|
|
|
|
|
|
|
signal(SIGHUP, SIG_IGN);
|
|
|
|
|
|
|
|
if (0 != fork()) exit(0);
|
|
|
|
|
|
|
|
if (0 != chdir("/")) exit(0);
|
|
|
|
|
|
|
|
fdevent_setfd_cloexec(pipefd[1]);
|
|
|
|
return pipefd[1];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static server *server_init(void) {
|
|
|
|
server *srv = calloc(1, sizeof(*srv));
|
|
|
|
force_assert(srv);
|
|
|
|
#define CLEAN(x) \
|
|
|
|
srv->x = buffer_init();
|
|
|
|
|
|
|
|
CLEAN(tmp_buf);
|
|
|
|
#undef CLEAN
|
|
|
|
srv->joblist = &srv->joblist_A;
|
|
|
|
|
|
|
|
strftime_cache_reset();
|
|
|
|
|
|
|
|
li_rand_reseed();
|
|
|
|
|
|
|
|
srv->startup_ts = log_epoch_secs = time(NULL);
|
|
|
|
|
|
|
|
srv->errh = log_error_st_init();
|
|
|
|
|
|
|
|
config_init(srv);
|
|
|
|
|
|
|
|
srv->request_env = plugins_call_handle_request_env;
|
|
|
|
|
|
|
|
srv->loadavg[0] = 0.0;
|
|
|
|
srv->loadavg[1] = 0.0;
|
|
|
|
srv->loadavg[2] = 0.0;
|
|
|
|
srv->stdin_fd = -1;
|
|
|
|
|
|
|
|
return srv;
|
|
|
|
}
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static void server_free(server *srv) {
|
|
|
|
if (oneshot_fd > 0) {
|
|
|
|
if (oneshot_fdn) {
|
|
|
|
fdevent_fdnode_event_del(srv->ev, oneshot_fdn);
|
|
|
|
fdevent_unregister(srv->ev, oneshot_fd);
|
|
|
|
oneshot_fdn = NULL;
|
|
|
|
}
|
|
|
|
close(oneshot_fd);
|
|
|
|
}
|
|
|
|
if (oneshot_fdout >= 0) {
|
|
|
|
close(oneshot_fdout);
|
|
|
|
}
|
|
|
|
if (srv->stdin_fd >= 0) {
|
|
|
|
close(srv->stdin_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CLEAN(x) \
|
|
|
|
buffer_free(srv->x);
|
|
|
|
|
|
|
|
CLEAN(tmp_buf);
|
|
|
|
|
|
|
|
#undef CLEAN
|
|
|
|
|
|
|
|
fdevent_free(srv->ev);
|
|
|
|
|
|
|
|
config_free(srv);
|
|
|
|
|
|
|
|
free(srv->joblist_A.ptr);
|
|
|
|
free(srv->joblist_B.ptr);
|
|
|
|
free(srv->fdwaitqueue.ptr);
|
|
|
|
|
|
|
|
stat_cache_free();
|
|
|
|
|
|
|
|
li_rand_cleanup();
|
|
|
|
chunkqueue_chunk_pool_free();
|
|
|
|
|
|
|
|
log_error_st_free(srv->errh);
|
|
|
|
free(srv);
|
|
|
|
}
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static void remove_pid_file(server *srv) {
|
|
|
|
if (pid_fd <= -2) return;
|
|
|
|
if (!buffer_string_is_empty(srv->srvconf.pid_file) && 0 <= pid_fd) {
|
|
|
|
if (0 != ftruncate(pid_fd, 0)) {
|
|
|
|
log_perror(srv->errh, __FILE__, __LINE__,
|
|
|
|
"ftruncate failed for: %s", srv->srvconf.pid_file->ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (0 <= pid_fd) {
|
|
|
|
close(pid_fd);
|
|
|
|
pid_fd = -1;
|
|
|
|
}
|
|
|
|
if (!buffer_string_is_empty(srv->srvconf.pid_file) &&
|
|
|
|
buffer_string_is_empty(srv->srvconf.changeroot)) {
|
|
|
|
if (0 != unlink(srv->srvconf.pid_file->ptr)) {
|
|
|
|
if (errno != EACCES && errno != EPERM) {
|
|
|
|
log_perror(srv->errh, __FILE__, __LINE__,
|
|
|
|
"unlink failed for: %s", srv->srvconf.pid_file->ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static server_socket * server_oneshot_getsock(server *srv, sock_addr *cnt_addr) {
|
|
|
|
server_socket *srv_socket, *srv_socket_wild = NULL;
|
|
|
|
for (uint32_t i = 0; i < srv->srv_sockets.used; ++i) {
|
|
|
|
srv_socket = srv->srv_sockets.ptr[i];
|
|
|
|
if (!sock_addr_is_port_eq(&srv_socket->addr,cnt_addr)) continue;
|
|
|
|
if (sock_addr_is_addr_eq(&srv_socket->addr,cnt_addr)) return srv_socket;
|
|
|
|
|
|
|
|
if (NULL != srv_socket_wild) continue;
|
|
|
|
if (sock_addr_is_addr_wildcard(&srv_socket->addr)) {
|
|
|
|
srv_socket_wild = srv_socket;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != srv_socket_wild) {
|
|
|
|
return srv_socket_wild;
|
|
|
|
} else if (srv->srv_sockets.used) {
|
|
|
|
return srv->srv_sockets.ptr[0];
|
|
|
|
} else {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__, "no sockets configured");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int server_oneshot_read_cq(connection *con, chunkqueue *cq, off_t max_bytes) {
|
|
|
|
/* temporary set con->fd to oneshot_fd (fd input) rather than outshot_fdout
|
|
|
|
* (lighttpd generally assumes operation on sockets, so this is a kludge) */
|
|
|
|
int fd = con->fd;
|
|
|
|
con->fd = oneshot_fd;
|
|
|
|
int rc = oneshot_read_cq(con, cq, max_bytes);
|
|
|
|
con->fd = fd;
|
|
|
|
|
|
|
|
/* note: optimistic reads (elsewhere) may or may not be enough to re-enable
|
|
|
|
* read interest after FDEVENT_IN interest was paused for other reasons */
|
|
|
|
|
|
|
|
const int events = fdevent_fdnode_interest(oneshot_fdn);
|
|
|
|
int n = con->is_readable ? 0 : FDEVENT_IN;
|
|
|
|
if (events & FDEVENT_RDHUP)
|
|
|
|
n |= FDEVENT_RDHUP;
|
|
|
|
fdevent_fdnode_event_set(con->srv->ev, oneshot_fdn, n);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static handler_t server_oneshot_handle_fdevent(void *context, int revents) {
|
|
|
|
connection *con = context;
|
|
|
|
|
|
|
|
/* note: not sync'd with con->fdn or connection_set_fdevent_interest() */
|
|
|
|
int rdhup = 0;
|
|
|
|
int n = fdevent_fdnode_interest(oneshot_fdn);
|
|
|
|
if (revents & FDEVENT_IN)
|
|
|
|
n &= ~FDEVENT_IN;
|
|
|
|
request_st * const r = &con->request;
|
|
|
|
if (r->state != CON_STATE_ERROR && (revents & (FDEVENT_HUP|FDEVENT_RDHUP))){
|
|
|
|
revents &= ~(FDEVENT_HUP|FDEVENT_RDHUP);
|
|
|
|
/* copied and modified from connection_handle_fdevent()
|
|
|
|
* fdevent_is_tcp_half_closed() will fail on pipe
|
|
|
|
* and, besides, read end of pipes should treat POLLHUP as POLLRDHUP */
|
|
|
|
n &= ~(FDEVENT_IN|FDEVENT_RDHUP);
|
|
|
|
rdhup = 1;
|
|
|
|
}
|
|
|
|
fdevent_fdnode_event_set(con->srv->ev, oneshot_fdn, n);
|
|
|
|
|
|
|
|
fdnode * const fdn = con->fdn; /* fdn->ctx == con */
|
|
|
|
handler_t rc = ((fdevent_handler)NULL != fdn->handler)
|
|
|
|
? (*fdn->handler)(con, revents)
|
|
|
|
: HANDLER_FINISHED;
|
|
|
|
|
|
|
|
if (rdhup) {
|
|
|
|
r->conf.stream_request_body &=
|
|
|
|
~(FDEVENT_STREAM_REQUEST_BUFMIN|FDEVENT_STREAM_REQUEST_POLLIN);
|
|
|
|
r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLRDHUP;
|
|
|
|
r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_TCP_FIN;
|
|
|
|
con->is_readable = 1; /*(can read 0 for end-of-stream)*/
|
|
|
|
if (chunkqueue_is_empty(con->read_queue)) r->keep_alive = 0;
|
|
|
|
if (r->reqbody_length < -1) /*(transparent proxy mode; no more data)*/
|
|
|
|
r->reqbody_length = r->reqbody_queue.bytes_in;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static int server_oneshot_init_pipe(server *srv, int fdin, int fdout) {
|
|
|
|
/* Note: attempt to work with netcat pipes though other code expects socket.
|
|
|
|
* netcat has different fds (pipes) for stdin and stdout. To support
|
|
|
|
* netcat, need to avoid S_ISSOCK(), getsockname(), and getpeername(),
|
|
|
|
* reconstructing addresses from environment variables:
|
|
|
|
* NCAT_LOCAL_ADDR NCAT_LOCAL_PORT
|
|
|
|
* NCAT_REMOTE_ADDR NCAT_REMOTE_PORT
|
|
|
|
* NCAT_PROTO (TCP, UDP, SCTP)
|
|
|
|
*/
|
|
|
|
connection *con;
|
|
|
|
server_socket *srv_socket;
|
|
|
|
sock_addr cnt_addr;
|
|
|
|
|
|
|
|
/* detect if called from netcat or else fabricate localhost addrs */
|
|
|
|
const char * const ncat =
|
|
|
|
getenv("NCAT_LOCAL_ADDR");
|
|
|
|
const char * const ncat_local_addr =
|
|
|
|
ncat ? ncat : "127.0.0.1"; /*(fabricated)*/
|
|
|
|
const char * const ncat_local_port =
|
|
|
|
ncat ? getenv("NCAT_LOCAL_PORT") : "80"; /*(fabricated)*/
|
|
|
|
const char * const ncat_remote_addr =
|
|
|
|
ncat ? getenv("NCAT_REMOTE_ADDR") : "127.0.0.1"; /*(fabricated)*/
|
|
|
|
const char * const ncat_remote_port =
|
|
|
|
ncat ? getenv("NCAT_REMOTE_PORT") : "48080"; /*(fabricated)*/
|
|
|
|
if (NULL == ncat_local_addr || NULL == ncat_local_port) return 0;
|
|
|
|
if (NULL == ncat_remote_addr || NULL == ncat_remote_port) return 0;
|
|
|
|
|
|
|
|
const int family = ncat && strchr(ncat_local_addr,':') ? AF_INET6 : AF_INET;
|
|
|
|
unsigned short port;
|
|
|
|
|
|
|
|
port = (unsigned short)strtol(ncat_local_port, NULL, 10);
|
|
|
|
if (1 != sock_addr_inet_pton(&cnt_addr, ncat_local_addr, family, port)) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__, "invalid local addr");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
srv_socket = server_oneshot_getsock(srv, &cnt_addr);
|
|
|
|
if (NULL == srv_socket) return 0;
|
|
|
|
|
|
|
|
port = (unsigned short)strtol(ncat_remote_port, NULL, 10);
|
|
|
|
if (1 != sock_addr_inet_pton(&cnt_addr, ncat_remote_addr, family, port)) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__, "invalid remote addr");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*(must set flags; fd did not pass through fdevent accept() logic)*/
|
|
|
|
if (-1 == fdevent_fcntl_set_nb_cloexec(fdin)) {
|
|
|
|
log_perror(srv->errh, __FILE__, __LINE__, "fcntl()");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (-1 == fdevent_fcntl_set_nb_cloexec(fdout)) {
|
|
|
|
log_perror(srv->errh, __FILE__, __LINE__, "fcntl()");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
con = connection_accepted(srv, srv_socket, &cnt_addr, fdout);
|
|
|
|
if (NULL == con) return 0;
|
|
|
|
|
|
|
|
/* note: existing routines assume socket, not pipe
|
|
|
|
* connections.c:connection_read_cq()
|
|
|
|
* uses recv() ifdef __WIN32
|
|
|
|
* passes S_IFSOCK to fdevent_ioctl_fionread()
|
|
|
|
* (The routine could be copied and modified, if required)
|
|
|
|
* This is unlikely to work if TLS is used over pipe since the SSL_CTX
|
|
|
|
* is associated with the other end of the pipe. However, if using
|
|
|
|
* pipes, using TLS is unexpected behavior.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*assert(oneshot_fd == fdin);*/
|
|
|
|
oneshot_read_cq = con->network_read;
|
|
|
|
con->network_read = server_oneshot_read_cq;
|
|
|
|
oneshot_fdn =
|
|
|
|
fdevent_register(srv->ev, fdin, server_oneshot_handle_fdevent, con);
|
|
|
|
fdevent_fdnode_event_set(srv->ev, oneshot_fdn, FDEVENT_RDHUP);
|
|
|
|
|
|
|
|
connection_state_machine(con);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static int server_oneshot_init(server *srv, int fd) {
|
|
|
|
connection *con;
|
|
|
|
server_socket *srv_socket;
|
|
|
|
sock_addr cnt_addr;
|
|
|
|
socklen_t cnt_len;
|
|
|
|
|
|
|
|
cnt_len = sizeof(cnt_addr);
|
|
|
|
if (0 != getsockname(fd, (struct sockaddr *)&cnt_addr, &cnt_len)) {
|
|
|
|
log_perror(srv->errh, __FILE__, __LINE__, "getsockname()");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
srv_socket = server_oneshot_getsock(srv, &cnt_addr);
|
|
|
|
if (NULL == srv_socket) return 0;
|
|
|
|
|
|
|
|
#ifdef __clang_analyzer__
|
|
|
|
memset(&cnt_addr, 0, sizeof(cnt_addr));
|
|
|
|
#endif
|
|
|
|
cnt_len = sizeof(cnt_addr);
|
|
|
|
if (0 != getpeername(fd, (struct sockaddr *)&cnt_addr, &cnt_len)) {
|
|
|
|
log_perror(srv->errh, __FILE__, __LINE__, "getpeername()");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*(must set flags; fd did not pass through fdevent accept() logic)*/
|
|
|
|
if (-1 == fdevent_fcntl_set_nb_cloexec(fd)) {
|
|
|
|
log_perror(srv->errh, __FILE__, __LINE__, "fcntl()");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sock_addr_get_family(&cnt_addr) != AF_UNIX) {
|
|
|
|
network_accept_tcp_nagle_disable(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
con = connection_accepted(srv, srv_socket, &cnt_addr, fd);
|
|
|
|
if (NULL == con) return 0;
|
|
|
|
|
|
|
|
connection_state_machine(con);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static void show_version (void) {
|
|
|
|
char *b = PACKAGE_DESC TEXT_SSL \
|
|
|
|
" - a light and fast webserver\n"
|
|
|
|
#ifdef NONREPRODUCIBLE_BUILD
|
|
|
|
"Build-Date: " __DATE__ " " __TIME__ "\n";
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
write_all(STDOUT_FILENO, b, strlen(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static void show_features (void) {
|
|
|
|
static const char features[] =
|
|
|
|
"\nFeatures:\n\n"
|
|
|
|
#ifdef HAVE_IPV6
|
|
|
|
"\t+ IPv6 support\n"
|
|
|
|
#else
|
|
|
|
"\t- IPv6 support\n"
|
|
|
|
#endif
|
|
|
|
#if defined HAVE_ZLIB_H && defined HAVE_LIBZ
|
|
|
|
"\t+ zlib support\n"
|
|
|
|
#else
|
|
|
|
"\t- zlib support\n"
|
|
|
|
#endif
|
|
|
|
#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2
|
|
|
|
"\t+ bzip2 support\n"
|
|
|
|
#else
|
|
|
|
"\t- bzip2 support\n"
|
|
|
|
#endif
|
|
|
|
#if defined HAVE_BROTLI_ENCODE_H && defined HAVE_BROTLI
|
|
|
|
"\t+ brotli support\n"
|
|
|
|
#else
|
|
|
|
"\t- brotli support\n"
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_CRYPT) || defined(HAVE_CRYPT_R) || defined(HAVE_LIBCRYPT)
|
|
|
|
"\t+ crypt support\n"
|
|
|
|
#else
|
|
|
|
"\t- crypt support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef USE_OPENSSL_CRYPTO
|
|
|
|
"\t+ OpenSSL support\n"
|
|
|
|
#else
|
|
|
|
"\t- OpenSSL support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef USE_MBEDTLS_CRYPTO
|
|
|
|
"\t+ mbedTLS support\n"
|
|
|
|
#else
|
|
|
|
"\t- mbedTLS support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef USE_NSS_CRYPTO
|
|
|
|
"\t+ NSS crypto support\n"
|
|
|
|
#else
|
|
|
|
"\t- NSS crypto support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef USE_GNUTLS_CRYPTO
|
|
|
|
"\t+ GnuTLS support\n"
|
|
|
|
#else
|
|
|
|
"\t- GnuTLS support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef USE_WOLFSSL_CRYPTO
|
|
|
|
"\t+ WolfSSL support\n"
|
|
|
|
#else
|
|
|
|
"\t- WolfSSL support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef USE_NETTLE_CRYPTO
|
|
|
|
"\t+ Nettle support\n"
|
|
|
|
#else
|
|
|
|
"\t- Nettle support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBPCRE
|
|
|
|
"\t+ PCRE support\n"
|
|
|
|
#else
|
|
|
|
"\t- PCRE support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_MYSQL
|
|
|
|
"\t+ MySQL support\n"
|
|
|
|
#else
|
|
|
|
"\t- MySQL support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_PGSQL
|
|
|
|
"\t+ PgSQL support\n"
|
|
|
|
#else
|
|
|
|
"\t- PgSQL support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_DBI
|
|
|
|
"\t+ DBI support\n"
|
|
|
|
#else
|
|
|
|
"\t- DBI support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_KRB5
|
|
|
|
"\t+ Kerberos support\n"
|
|
|
|
#else
|
|
|
|
"\t- Kerberos support\n"
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)
|
|
|
|
"\t+ LDAP support\n"
|
|
|
|
#else
|
|
|
|
"\t- LDAP support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
"\t+ PAM support\n"
|
|
|
|
#else
|
|
|
|
"\t- PAM support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef USE_MEMCACHED
|
|
|
|
"\t+ memcached support\n"
|
|
|
|
#else
|
|
|
|
"\t- memcached support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_FAM_H
|
|
|
|
"\t+ FAM support\n"
|
|
|
|
#else
|
|
|
|
"\t- FAM support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LUA_H
|
|
|
|
"\t+ LUA support\n"
|
|
|
|
#else
|
|
|
|
"\t- LUA support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBXML_H
|
|
|
|
"\t+ xml support\n"
|
|
|
|
#else
|
|
|
|
"\t- xml support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SQLITE3_H
|
|
|
|
"\t+ SQLite support\n"
|
|
|
|
#else
|
|
|
|
"\t- SQLite support\n"
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_GDBM_H
|
|
|
|
"\t+ GDBM support\n"
|
|
|
|
#else
|
|
|
|
"\t- GDBM support\n"
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
show_version();
|
|
|
|
printf("%s%s%s\n", fdevent_show_event_handlers(), network_write_show_handlers(), features);
|
|
|
|
}
|
|
|
|
|
|
|
|
__attribute_cold__
|
|
|
|
static void show_help (void) {
|
|
|
|
char *b = PACKAGE_DESC TEXT_SSL
|
|
|
|
#ifdef NONREPRODUCIBLE_BUILD
|
|
|
|
" ("__DATE__ " " __TIME__ ")"
|
|
|
|
#endif
|
|
|
|
" - a light and fast webserver\n" \
|
|
|
|
"usage:\n" \
|
|
|
|
" -f <name> filename of the config-file\n" \
|
|
|
|
" -m <name> module directory (default: "LIBRARY_DIR")\n" \
|
|
|
|
" -i <secs> graceful shutdown after <secs> of inactivity\n" \
|
|
|
|
" -1 process single (one) request on stdin socket, then exit\n" \
|
|
|
|
" -p print the parsed config-file in internal form, and exit\n" \
|
|
|