Initial commit

This commit is contained in:
Hakase
2018-02-14 15:05:29 +09:00
commit 20f797d2f6
8093 changed files with 1360150 additions and 0 deletions
+560
View File
@@ -0,0 +1,560 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#if (NGX_TEST_BUILD_DEVPOLL)
/* Solaris declarations */
#ifndef POLLREMOVE
#define POLLREMOVE 0x0800
#endif
#define DP_POLL 0xD001
#define DP_ISPOLLED 0xD002
struct dvpoll {
struct pollfd *dp_fds;
int dp_nfds;
int dp_timeout;
};
#endif
typedef struct {
ngx_uint_t changes;
ngx_uint_t events;
} ngx_devpoll_conf_t;
static ngx_int_t ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
static void ngx_devpoll_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_devpoll_process_events(ngx_cycle_t *cycle,
ngx_msec_t timer, ngx_uint_t flags);
static void *ngx_devpoll_create_conf(ngx_cycle_t *cycle);
static char *ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf);
static int dp = -1;
static struct pollfd *change_list, *event_list;
static ngx_uint_t nchanges, max_changes, nevents;
static ngx_event_t **change_index;
static ngx_str_t devpoll_name = ngx_string("/dev/poll");
static ngx_command_t ngx_devpoll_commands[] = {
{ ngx_string("devpoll_changes"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_devpoll_conf_t, changes),
NULL },
{ ngx_string("devpoll_events"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_devpoll_conf_t, events),
NULL },
ngx_null_command
};
static ngx_event_module_t ngx_devpoll_module_ctx = {
&devpoll_name,
ngx_devpoll_create_conf, /* create configuration */
ngx_devpoll_init_conf, /* init configuration */
{
ngx_devpoll_add_event, /* add an event */
ngx_devpoll_del_event, /* delete an event */
ngx_devpoll_add_event, /* enable an event */
ngx_devpoll_del_event, /* disable an event */
NULL, /* add an connection */
NULL, /* delete an connection */
NULL, /* trigger a notify */
ngx_devpoll_process_events, /* process the events */
ngx_devpoll_init, /* init the events */
ngx_devpoll_done, /* done the events */
}
};
ngx_module_t ngx_devpoll_module = {
NGX_MODULE_V1,
&ngx_devpoll_module_ctx, /* module context */
ngx_devpoll_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
size_t n;
ngx_devpoll_conf_t *dpcf;
dpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_devpoll_module);
if (dp == -1) {
dp = open("/dev/poll", O_RDWR);
if (dp == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"open(/dev/poll) failed");
return NGX_ERROR;
}
}
if (max_changes < dpcf->changes) {
if (nchanges) {
n = nchanges * sizeof(struct pollfd);
if (write(dp, change_list, n) != (ssize_t) n) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"write(/dev/poll) failed");
return NGX_ERROR;
}
nchanges = 0;
}
if (change_list) {
ngx_free(change_list);
}
change_list = ngx_alloc(sizeof(struct pollfd) * dpcf->changes,
cycle->log);
if (change_list == NULL) {
return NGX_ERROR;
}
if (change_index) {
ngx_free(change_index);
}
change_index = ngx_alloc(sizeof(ngx_event_t *) * dpcf->changes,
cycle->log);
if (change_index == NULL) {
return NGX_ERROR;
}
}
max_changes = dpcf->changes;
if (nevents < dpcf->events) {
if (event_list) {
ngx_free(event_list);
}
event_list = ngx_alloc(sizeof(struct pollfd) * dpcf->events,
cycle->log);
if (event_list == NULL) {
return NGX_ERROR;
}
}
nevents = dpcf->events;
ngx_io = ngx_os_io;
ngx_event_actions = ngx_devpoll_module_ctx.actions;
ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;
return NGX_OK;
}
static void
ngx_devpoll_done(ngx_cycle_t *cycle)
{
if (close(dp) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close(/dev/poll) failed");
}
dp = -1;
ngx_free(change_list);
ngx_free(event_list);
ngx_free(change_index);
change_list = NULL;
event_list = NULL;
change_index = NULL;
max_changes = 0;
nchanges = 0;
nevents = 0;
}
static ngx_int_t
ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
#if (NGX_DEBUG)
ngx_connection_t *c;
#endif
#if (NGX_READ_EVENT != POLLIN)
event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;
#endif
#if (NGX_DEBUG)
c = ev->data;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"devpoll add event: fd:%d ev:%04Xi", c->fd, event);
#endif
ev->active = 1;
return ngx_devpoll_set_event(ev, event, 0);
}
static ngx_int_t
ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_event_t *e;
ngx_connection_t *c;
c = ev->data;
#if (NGX_READ_EVENT != POLLIN)
event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;
#endif
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"devpoll del event: fd:%d ev:%04Xi", c->fd, event);
if (ngx_devpoll_set_event(ev, POLLREMOVE, flags) == NGX_ERROR) {
return NGX_ERROR;
}
ev->active = 0;
if (flags & NGX_CLOSE_EVENT) {
e = (event == POLLIN) ? c->write : c->read;
if (e) {
e->active = 0;
}
return NGX_OK;
}
/* restore the pair event if it exists */
if (event == POLLIN) {
e = c->write;
event = POLLOUT;
} else {
e = c->read;
event = POLLIN;
}
if (e && e->active) {
return ngx_devpoll_set_event(e, event, 0);
}
return NGX_OK;
}
static ngx_int_t
ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
size_t n;
ngx_connection_t *c;
c = ev->data;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"devpoll fd:%d ev:%04Xi fl:%04Xi", c->fd, event, flags);
if (nchanges >= max_changes) {
ngx_log_error(NGX_LOG_WARN, ev->log, 0,
"/dev/pool change list is filled up");
n = nchanges * sizeof(struct pollfd);
if (write(dp, change_list, n) != (ssize_t) n) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"write(/dev/poll) failed");
return NGX_ERROR;
}
nchanges = 0;
}
change_list[nchanges].fd = c->fd;
change_list[nchanges].events = (short) event;
change_list[nchanges].revents = 0;
change_index[nchanges] = ev;
ev->index = nchanges;
nchanges++;
if (flags & NGX_CLOSE_EVENT) {
n = nchanges * sizeof(struct pollfd);
if (write(dp, change_list, n) != (ssize_t) n) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"write(/dev/poll) failed");
return NGX_ERROR;
}
nchanges = 0;
}
return NGX_OK;
}
static ngx_int_t
ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags)
{
int events, revents, rc;
size_t n;
ngx_fd_t fd;
ngx_err_t err;
ngx_int_t i;
ngx_uint_t level, instance;
ngx_event_t *rev, *wev;
ngx_queue_t *queue;
ngx_connection_t *c;
struct pollfd pfd;
struct dvpoll dvp;
/* NGX_TIMER_INFINITE == INFTIM */
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"devpoll timer: %M", timer);
if (nchanges) {
n = nchanges * sizeof(struct pollfd);
if (write(dp, change_list, n) != (ssize_t) n) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"write(/dev/poll) failed");
return NGX_ERROR;
}
nchanges = 0;
}
dvp.dp_fds = event_list;
dvp.dp_nfds = (int) nevents;
dvp.dp_timeout = timer;
events = ioctl(dp, DP_POLL, &dvp);
err = (events == -1) ? ngx_errno : 0;
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
ngx_time_update();
}
if (err) {
if (err == NGX_EINTR) {
if (ngx_event_timer_alarm) {
ngx_event_timer_alarm = 0;
return NGX_OK;
}
level = NGX_LOG_INFO;
} else {
level = NGX_LOG_ALERT;
}
ngx_log_error(level, cycle->log, err, "ioctl(DP_POLL) failed");
return NGX_ERROR;
}
if (events == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"ioctl(DP_POLL) returned no events without timeout");
return NGX_ERROR;
}
for (i = 0; i < events; i++) {
fd = event_list[i].fd;
revents = event_list[i].revents;
c = ngx_cycle->files[fd];
if (c == NULL || c->fd == -1) {
pfd.fd = fd;
pfd.events = 0;
pfd.revents = 0;
rc = ioctl(dp, DP_ISPOLLED, &pfd);
switch (rc) {
case -1:
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"ioctl(DP_ISPOLLED) failed for socket %d, event %04Xd",
fd, revents);
break;
case 0:
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"phantom event %04Xd for closed and removed socket %d",
revents, fd);
break;
default:
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"unexpected event %04Xd for closed and removed socket %d, "
"ioctl(DP_ISPOLLED) returned rc:%d, fd:%d, event %04Xd",
revents, fd, rc, pfd.fd, pfd.revents);
pfd.fd = fd;
pfd.events = POLLREMOVE;
pfd.revents = 0;
if (write(dp, &pfd, sizeof(struct pollfd))
!= (ssize_t) sizeof(struct pollfd))
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"write(/dev/poll) for %d failed", fd);
}
if (close(fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close(%d) failed", fd);
}
break;
}
continue;
}
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"devpoll: fd:%d, ev:%04Xd, rev:%04Xd",
fd, event_list[i].events, revents);
if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"ioctl(DP_POLL) error fd:%d ev:%04Xd rev:%04Xd",
fd, event_list[i].events, revents);
}
if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"strange ioctl(DP_POLL) events "
"fd:%d ev:%04Xd rev:%04Xd",
fd, event_list[i].events, revents);
}
if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
/*
* if the error events were returned, add POLLIN and POLLOUT
* to handle the events at least in one active handler
*/
revents |= POLLIN|POLLOUT;
}
rev = c->read;
if ((revents & POLLIN) && rev->active) {
rev->ready = 1;
if (flags & NGX_POST_EVENTS) {
queue = rev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(rev, queue);
} else {
instance = rev->instance;
rev->handler(rev);
if (c->fd == -1 || rev->instance != instance) {
continue;
}
}
}
wev = c->write;
if ((revents & POLLOUT) && wev->active) {
wev->ready = 1;
if (flags & NGX_POST_EVENTS) {
ngx_post_event(wev, &ngx_posted_events);
} else {
wev->handler(wev);
}
}
}
return NGX_OK;
}
static void *
ngx_devpoll_create_conf(ngx_cycle_t *cycle)
{
ngx_devpoll_conf_t *dpcf;
dpcf = ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t));
if (dpcf == NULL) {
return NULL;
}
dpcf->changes = NGX_CONF_UNSET;
dpcf->events = NGX_CONF_UNSET;
return dpcf;
}
static char *
ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_devpoll_conf_t *dpcf = conf;
ngx_conf_init_uint_value(dpcf->changes, 32);
ngx_conf_init_uint_value(dpcf->events, 32);
return NGX_CONF_OK;
}
+1052
View File
@@ -0,0 +1,1052 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#if (NGX_TEST_BUILD_EPOLL)
/* epoll declarations */
#define EPOLLIN 0x001
#define EPOLLPRI 0x002
#define EPOLLOUT 0x004
#define EPOLLERR 0x008
#define EPOLLHUP 0x010
#define EPOLLRDNORM 0x040
#define EPOLLRDBAND 0x080
#define EPOLLWRNORM 0x100
#define EPOLLWRBAND 0x200
#define EPOLLMSG 0x400
#define EPOLLRDHUP 0x2000
#define EPOLLEXCLUSIVE 0x10000000
#define EPOLLONESHOT 0x40000000
#define EPOLLET 0x80000000
#define EPOLL_CTL_ADD 1
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events;
epoll_data_t data;
};
int epoll_create(int size);
int epoll_create(int size)
{
return -1;
}
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
{
return -1;
}
int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout);
int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout)
{
return -1;
}
#if (NGX_HAVE_EVENTFD)
#define SYS_eventfd 323
#endif
#if (NGX_HAVE_FILE_AIO)
#define SYS_io_setup 245
#define SYS_io_destroy 246
#define SYS_io_getevents 247
typedef u_int aio_context_t;
struct io_event {
uint64_t data; /* the data field from the iocb */
uint64_t obj; /* what iocb this event came from */
int64_t res; /* result code for this event */
int64_t res2; /* secondary result */
};
#endif
#endif /* NGX_TEST_BUILD_EPOLL */
typedef struct {
ngx_uint_t events;
ngx_uint_t aio_requests;
} ngx_epoll_conf_t;
static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
#if (NGX_HAVE_EVENTFD)
static ngx_int_t ngx_epoll_notify_init(ngx_log_t *log);
static void ngx_epoll_notify_handler(ngx_event_t *ev);
#endif
#if (NGX_HAVE_EPOLLRDHUP)
static void ngx_epoll_test_rdhup(ngx_cycle_t *cycle);
#endif
static void ngx_epoll_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_epoll_add_connection(ngx_connection_t *c);
static ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c,
ngx_uint_t flags);
#if (NGX_HAVE_EVENTFD)
static ngx_int_t ngx_epoll_notify(ngx_event_handler_pt handler);
#endif
static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
#if (NGX_HAVE_FILE_AIO)
static void ngx_epoll_eventfd_handler(ngx_event_t *ev);
#endif
static void *ngx_epoll_create_conf(ngx_cycle_t *cycle);
static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf);
static int ep = -1;
static struct epoll_event *event_list;
static ngx_uint_t nevents;
#if (NGX_HAVE_EVENTFD)
static int notify_fd = -1;
static ngx_event_t notify_event;
static ngx_connection_t notify_conn;
#endif
#if (NGX_HAVE_FILE_AIO)
int ngx_eventfd = -1;
aio_context_t ngx_aio_ctx = 0;
static ngx_event_t ngx_eventfd_event;
static ngx_connection_t ngx_eventfd_conn;
#endif
#if (NGX_HAVE_EPOLLRDHUP)
ngx_uint_t ngx_use_epoll_rdhup;
#endif
static ngx_str_t epoll_name = ngx_string("epoll");
static ngx_command_t ngx_epoll_commands[] = {
{ ngx_string("epoll_events"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_epoll_conf_t, events),
NULL },
{ ngx_string("worker_aio_requests"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_epoll_conf_t, aio_requests),
NULL },
ngx_null_command
};
static ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
{
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
#if (NGX_HAVE_EVENTFD)
ngx_epoll_notify, /* trigger a notify */
#else
NULL, /* trigger a notify */
#endif
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};
ngx_module_t ngx_epoll_module = {
NGX_MODULE_V1,
&ngx_epoll_module_ctx, /* module context */
ngx_epoll_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
#if (NGX_HAVE_FILE_AIO)
/*
* We call io_setup(), io_destroy() io_submit(), and io_getevents() directly
* as syscalls instead of libaio usage, because the library header file
* supports eventfd() since 0.3.107 version only.
*/
static int
io_setup(u_int nr_reqs, aio_context_t *ctx)
{
return syscall(SYS_io_setup, nr_reqs, ctx);
}
static int
io_destroy(aio_context_t ctx)
{
return syscall(SYS_io_destroy, ctx);
}
static int
io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events,
struct timespec *tmo)
{
return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo);
}
static void
ngx_epoll_aio_init(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf)
{
int n;
struct epoll_event ee;
#if (NGX_HAVE_SYS_EVENTFD_H)
ngx_eventfd = eventfd(0, 0);
#else
ngx_eventfd = syscall(SYS_eventfd, 0);
#endif
if (ngx_eventfd == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"eventfd() failed");
ngx_file_aio = 0;
return;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"eventfd: %d", ngx_eventfd);
n = 1;
if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"ioctl(eventfd, FIONBIO) failed");
goto failed;
}
if (io_setup(epcf->aio_requests, &ngx_aio_ctx) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"io_setup() failed");
goto failed;
}
ngx_eventfd_event.data = &ngx_eventfd_conn;
ngx_eventfd_event.handler = ngx_epoll_eventfd_handler;
ngx_eventfd_event.log = cycle->log;
ngx_eventfd_event.active = 1;
ngx_eventfd_conn.fd = ngx_eventfd;
ngx_eventfd_conn.read = &ngx_eventfd_event;
ngx_eventfd_conn.log = cycle->log;
ee.events = EPOLLIN|EPOLLET;
ee.data.ptr = &ngx_eventfd_conn;
if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) != -1) {
return;
}
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");
if (io_destroy(ngx_aio_ctx) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"io_destroy() failed");
}
failed:
if (close(ngx_eventfd) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"eventfd close() failed");
}
ngx_eventfd = -1;
ngx_aio_ctx = 0;
ngx_file_aio = 0;
}
#endif
static ngx_int_t
ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
ngx_epoll_conf_t *epcf;
epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);
if (ep == -1) {
ep = epoll_create(cycle->connection_n / 2);
if (ep == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"epoll_create() failed");
return NGX_ERROR;
}
#if (NGX_HAVE_EVENTFD)
if (ngx_epoll_notify_init(cycle->log) != NGX_OK) {
ngx_epoll_module_ctx.actions.notify = NULL;
}
#endif
#if (NGX_HAVE_FILE_AIO)
ngx_epoll_aio_init(cycle, epcf);
#endif
#if (NGX_HAVE_EPOLLRDHUP)
ngx_epoll_test_rdhup(cycle);
#endif
}
if (nevents < epcf->events) {
if (event_list) {
ngx_free(event_list);
}
event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,
cycle->log);
if (event_list == NULL) {
return NGX_ERROR;
}
}
nevents = epcf->events;
ngx_io = ngx_os_io;
ngx_event_actions = ngx_epoll_module_ctx.actions;
#if (NGX_HAVE_CLEAR_EVENT)
ngx_event_flags = NGX_USE_CLEAR_EVENT
#else
ngx_event_flags = NGX_USE_LEVEL_EVENT
#endif
|NGX_USE_GREEDY_EVENT
|NGX_USE_EPOLL_EVENT;
return NGX_OK;
}
#if (NGX_HAVE_EVENTFD)
static ngx_int_t
ngx_epoll_notify_init(ngx_log_t *log)
{
struct epoll_event ee;
#if (NGX_HAVE_SYS_EVENTFD_H)
notify_fd = eventfd(0, 0);
#else
notify_fd = syscall(SYS_eventfd, 0);
#endif
if (notify_fd == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "eventfd() failed");
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,
"notify eventfd: %d", notify_fd);
notify_event.handler = ngx_epoll_notify_handler;
notify_event.log = log;
notify_event.active = 1;
notify_conn.fd = notify_fd;
notify_conn.read = &notify_event;
notify_conn.log = log;
ee.events = EPOLLIN|EPOLLET;
ee.data.ptr = &notify_conn;
if (epoll_ctl(ep, EPOLL_CTL_ADD, notify_fd, &ee) == -1) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");
if (close(notify_fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
"eventfd close() failed");
}
return NGX_ERROR;
}
return NGX_OK;
}
static void
ngx_epoll_notify_handler(ngx_event_t *ev)
{
ssize_t n;
uint64_t count;
ngx_err_t err;
ngx_event_handler_pt handler;
if (++ev->index == NGX_MAX_UINT32_VALUE) {
ev->index = 0;
n = read(notify_fd, &count, sizeof(uint64_t));
err = ngx_errno;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"read() eventfd %d: %z count:%uL", notify_fd, n, count);
if ((size_t) n != sizeof(uint64_t)) {
ngx_log_error(NGX_LOG_ALERT, ev->log, err,
"read() eventfd %d failed", notify_fd);
}
}
handler = ev->data;
handler(ev);
}
#endif
#if (NGX_HAVE_EPOLLRDHUP)
static void
ngx_epoll_test_rdhup(ngx_cycle_t *cycle)
{
int s[2], events;
struct epoll_event ee;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"socketpair() failed");
return;
}
ee.events = EPOLLET|EPOLLIN|EPOLLRDHUP;
if (epoll_ctl(ep, EPOLL_CTL_ADD, s[0], &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"epoll_ctl() failed");
goto failed;
}
if (close(s[1]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() failed");
s[1] = -1;
goto failed;
}
s[1] = -1;
events = epoll_wait(ep, &ee, 1, 5000);
if (events == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"epoll_wait() failed");
goto failed;
}
if (events) {
ngx_use_epoll_rdhup = ee.events & EPOLLRDHUP;
} else {
ngx_log_error(NGX_LOG_ALERT, cycle->log, NGX_ETIMEDOUT,
"epoll_wait() timed out");
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"testing the EPOLLRDHUP flag: %s",
ngx_use_epoll_rdhup ? "success" : "fail");
failed:
if (s[1] != -1 && close(s[1]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() failed");
}
if (close(s[0]) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() failed");
}
}
#endif
static void
ngx_epoll_done(ngx_cycle_t *cycle)
{
if (close(ep) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"epoll close() failed");
}
ep = -1;
#if (NGX_HAVE_EVENTFD)
if (close(notify_fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"eventfd close() failed");
}
notify_fd = -1;
#endif
#if (NGX_HAVE_FILE_AIO)
if (ngx_eventfd != -1) {
if (io_destroy(ngx_aio_ctx) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"io_destroy() failed");
}
if (close(ngx_eventfd) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"eventfd close() failed");
}
ngx_eventfd = -1;
}
ngx_aio_ctx = 0;
#endif
ngx_free(event_list);
event_list = NULL;
nevents = 0;
}
static ngx_int_t
ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
int op;
uint32_t events, prev;
ngx_event_t *e;
ngx_connection_t *c;
struct epoll_event ee;
c = ev->data;
events = (uint32_t) event;
if (event == NGX_READ_EVENT) {
e = c->write;
prev = EPOLLOUT;
#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
events = EPOLLIN|EPOLLRDHUP;
#endif
} else {
e = c->read;
prev = EPOLLIN|EPOLLRDHUP;
#if (NGX_WRITE_EVENT != EPOLLOUT)
events = EPOLLOUT;
#endif
}
if (e->active) {
op = EPOLL_CTL_MOD;
events |= prev;
} else {
op = EPOLL_CTL_ADD;
}
#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP)
if (flags & NGX_EXCLUSIVE_EVENT) {
events &= ~EPOLLRDHUP;
}
#endif
ee.events = events | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"epoll add event: fd:%d op:%d ev:%08XD",
c->fd, op, ee.events);
if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
}
ev->active = 1;
#if 0
ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
#endif
return NGX_OK;
}
static ngx_int_t
ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
int op;
uint32_t prev;
ngx_event_t *e;
ngx_connection_t *c;
struct epoll_event ee;
/*
* when the file descriptor is closed, the epoll automatically deletes
* it from its queue, so we do not need to delete explicitly the event
* before the closing the file descriptor
*/
if (flags & NGX_CLOSE_EVENT) {
ev->active = 0;
return NGX_OK;
}
c = ev->data;
if (event == NGX_READ_EVENT) {
e = c->write;
prev = EPOLLOUT;
} else {
e = c->read;
prev = EPOLLIN|EPOLLRDHUP;
}
if (e->active) {
op = EPOLL_CTL_MOD;
ee.events = prev | (uint32_t) flags;
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
} else {
op = EPOLL_CTL_DEL;
ee.events = 0;
ee.data.ptr = NULL;
}
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"epoll del event: fd:%d op:%d ev:%08XD",
c->fd, op, ee.events);
if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
}
ev->active = 0;
return NGX_OK;
}
static ngx_int_t
ngx_epoll_add_connection(ngx_connection_t *c)
{
struct epoll_event ee;
ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;
ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"epoll add connection: fd:%d ev:%08XD", c->fd, ee.events);
if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"epoll_ctl(EPOLL_CTL_ADD, %d) failed", c->fd);
return NGX_ERROR;
}
c->read->active = 1;
c->write->active = 1;
return NGX_OK;
}
static ngx_int_t
ngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags)
{
int op;
struct epoll_event ee;
/*
* when the file descriptor is closed the epoll automatically deletes
* it from its queue so we do not need to delete explicitly the event
* before the closing the file descriptor
*/
if (flags & NGX_CLOSE_EVENT) {
c->read->active = 0;
c->write->active = 0;
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"epoll del connection: fd:%d", c->fd);
op = EPOLL_CTL_DEL;
ee.events = 0;
ee.data.ptr = NULL;
if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"epoll_ctl(%d, %d) failed", op, c->fd);
return NGX_ERROR;
}
c->read->active = 0;
c->write->active = 0;
return NGX_OK;
}
#if (NGX_HAVE_EVENTFD)
static ngx_int_t
ngx_epoll_notify(ngx_event_handler_pt handler)
{
static uint64_t inc = 1;
notify_event.data = handler;
if ((size_t) write(notify_fd, &inc, sizeof(uint64_t)) != sizeof(uint64_t)) {
ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,
"write() to eventfd %d failed", notify_fd);
return NGX_ERROR;
}
return NGX_OK;
}
#endif
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
int events;
uint32_t revents;
ngx_int_t instance, i;
ngx_uint_t level;
ngx_err_t err;
ngx_event_t *rev, *wev;
ngx_queue_t *queue;
ngx_connection_t *c;
/* NGX_TIMER_INFINITE == INFTIM */
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll timer: %M", timer);
events = epoll_wait(ep, event_list, (int) nevents, timer);
err = (events == -1) ? ngx_errno : 0;
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
ngx_time_update();
}
if (err) {
if (err == NGX_EINTR) {
if (ngx_event_timer_alarm) {
ngx_event_timer_alarm = 0;
return NGX_OK;
}
level = NGX_LOG_INFO;
} else {
level = NGX_LOG_ALERT;
}
ngx_log_error(level, cycle->log, err, "epoll_wait() failed");
return NGX_ERROR;
}
if (events == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"epoll_wait() returned no events without timeout");
return NGX_ERROR;
}
for (i = 0; i < events; i++) {
c = event_list[i].data.ptr;
instance = (uintptr_t) c & 1;
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
rev = c->read;
if (c->fd == -1 || rev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
revents = event_list[i].events;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: fd:%d ev:%04XD d:%p",
c->fd, revents, event_list[i].data.ptr);
if (revents & (EPOLLERR|EPOLLHUP)) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll_wait() error on fd:%d ev:%04XD",
c->fd, revents);
/*
* if the error events were returned, add EPOLLIN and EPOLLOUT
* to handle the events at least in one active handler
*/
revents |= EPOLLIN|EPOLLOUT;
}
#if 0
if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"strange epoll_wait() events fd:%d ev:%04XD",
c->fd, revents);
}
#endif
if ((revents & EPOLLIN) && rev->active) {
#if (NGX_HAVE_EPOLLRDHUP)
if (revents & EPOLLRDHUP) {
rev->pending_eof = 1;
}
rev->available = 1;
#endif
rev->ready = 1;
if (flags & NGX_POST_EVENTS) {
queue = rev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(rev, queue);
} else {
rev->handler(rev);
}
}
wev = c->write;
if ((revents & EPOLLOUT) && wev->active) {
if (c->fd == -1 || wev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"epoll: stale event %p", c);
continue;
}
wev->ready = 1;
#if (NGX_THREADS)
wev->complete = 1;
#endif
if (flags & NGX_POST_EVENTS) {
ngx_post_event(wev, &ngx_posted_events);
} else {
wev->handler(wev);
}
}
}
return NGX_OK;
}
#if (NGX_HAVE_FILE_AIO)
static void
ngx_epoll_eventfd_handler(ngx_event_t *ev)
{
int n, events;
long i;
uint64_t ready;
ngx_err_t err;
ngx_event_t *e;
ngx_event_aio_t *aio;
struct io_event event[64];
struct timespec ts;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd handler");
n = read(ngx_eventfd, &ready, 8);
err = ngx_errno;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd: %d", n);
if (n != 8) {
if (n == -1) {
if (err == NGX_EAGAIN) {
return;
}
ngx_log_error(NGX_LOG_ALERT, ev->log, err, "read(eventfd) failed");
return;
}
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"read(eventfd) returned only %d bytes", n);
return;
}
ts.tv_sec = 0;
ts.tv_nsec = 0;
while (ready) {
events = io_getevents(ngx_aio_ctx, 1, 64, event, &ts);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"io_getevents: %d", events);
if (events > 0) {
ready -= events;
for (i = 0; i < events; i++) {
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"io_event: %XL %XL %L %L",
event[i].data, event[i].obj,
event[i].res, event[i].res2);
e = (ngx_event_t *) (uintptr_t) event[i].data;
e->complete = 1;
e->active = 0;
e->ready = 1;
aio = e->data;
aio->res = event[i].res;
ngx_post_event(e, &ngx_posted_events);
}
continue;
}
if (events == 0) {
return;
}
/* events == -1 */
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"io_getevents() failed");
return;
}
}
#endif
static void *
ngx_epoll_create_conf(ngx_cycle_t *cycle)
{
ngx_epoll_conf_t *epcf;
epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t));
if (epcf == NULL) {
return NULL;
}
epcf->events = NGX_CONF_UNSET;
epcf->aio_requests = NGX_CONF_UNSET;
return epcf;
}
static char *
ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_epoll_conf_t *epcf = conf;
ngx_conf_init_uint_value(epcf->events, 512);
ngx_conf_init_uint_value(epcf->aio_requests, 32);
return NGX_CONF_OK;
}
+651
View File
@@ -0,0 +1,651 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#if (NGX_TEST_BUILD_EVENTPORT)
#define ushort_t u_short
#define uint_t u_int
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
typedef int clockid_t;
typedef void * timer_t;
#elif (NGX_DARWIN)
typedef void * timer_t;
#endif
/* Solaris declarations */
#define PORT_SOURCE_AIO 1
#define PORT_SOURCE_TIMER 2
#define PORT_SOURCE_USER 3
#define PORT_SOURCE_FD 4
#define PORT_SOURCE_ALERT 5
#define PORT_SOURCE_MQ 6
#ifndef ETIME
#define ETIME 64
#endif
#define SIGEV_PORT 4
typedef struct {
int portev_events; /* event data is source specific */
ushort_t portev_source; /* event source */
ushort_t portev_pad; /* port internal use */
uintptr_t portev_object; /* source specific object */
void *portev_user; /* user cookie */
} port_event_t;
typedef struct port_notify {
int portnfy_port; /* bind request(s) to port */
void *portnfy_user; /* user defined */
} port_notify_t;
#if (__FreeBSD__ && __FreeBSD_version < 700005) || (NGX_DARWIN)
typedef struct itimerspec { /* definition per POSIX.4 */
struct timespec it_interval;/* timer period */
struct timespec it_value; /* timer expiration */
} itimerspec_t;
#endif
int port_create(void);
int port_create(void)
{
return -1;
}
int port_associate(int port, int source, uintptr_t object, int events,
void *user);
int port_associate(int port, int source, uintptr_t object, int events,
void *user)
{
return -1;
}
int port_dissociate(int port, int source, uintptr_t object);
int port_dissociate(int port, int source, uintptr_t object)
{
return -1;
}
int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,
struct timespec *timeout);
int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,
struct timespec *timeout)
{
return -1;
}
int port_send(int port, int events, void *user);
int port_send(int port, int events, void *user)
{
return -1;
}
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);
int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
{
return -1;
}
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
struct itimerspec *ovalue);
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
struct itimerspec *ovalue)
{
return -1;
}
int timer_delete(timer_t timerid);
int timer_delete(timer_t timerid)
{
return -1;
}
#endif
typedef struct {
ngx_uint_t events;
} ngx_eventport_conf_t;
static ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer);
static void ngx_eventport_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_eventport_notify(ngx_event_handler_pt handler);
static ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle,
ngx_msec_t timer, ngx_uint_t flags);
static void *ngx_eventport_create_conf(ngx_cycle_t *cycle);
static char *ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf);
static int ep = -1;
static port_event_t *event_list;
static ngx_uint_t nevents;
static timer_t event_timer = (timer_t) -1;
static ngx_event_t notify_event;
static ngx_str_t eventport_name = ngx_string("eventport");
static ngx_command_t ngx_eventport_commands[] = {
{ ngx_string("eventport_events"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_eventport_conf_t, events),
NULL },
ngx_null_command
};
static ngx_event_module_t ngx_eventport_module_ctx = {
&eventport_name,
ngx_eventport_create_conf, /* create configuration */
ngx_eventport_init_conf, /* init configuration */
{
ngx_eventport_add_event, /* add an event */
ngx_eventport_del_event, /* delete an event */
ngx_eventport_add_event, /* enable an event */
ngx_eventport_del_event, /* disable an event */
NULL, /* add an connection */
NULL, /* delete an connection */
ngx_eventport_notify, /* trigger a notify */
ngx_eventport_process_events, /* process the events */
ngx_eventport_init, /* init the events */
ngx_eventport_done, /* done the events */
}
};
ngx_module_t ngx_eventport_module = {
NGX_MODULE_V1,
&ngx_eventport_module_ctx, /* module context */
ngx_eventport_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
port_notify_t pn;
struct itimerspec its;
struct sigevent sev;
ngx_eventport_conf_t *epcf;
epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_eventport_module);
if (ep == -1) {
ep = port_create();
if (ep == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"port_create() failed");
return NGX_ERROR;
}
notify_event.active = 1;
notify_event.log = cycle->log;
}
if (nevents < epcf->events) {
if (event_list) {
ngx_free(event_list);
}
event_list = ngx_alloc(sizeof(port_event_t) * epcf->events,
cycle->log);
if (event_list == NULL) {
return NGX_ERROR;
}
}
ngx_event_flags = NGX_USE_EVENTPORT_EVENT;
if (timer) {
ngx_memzero(&pn, sizeof(port_notify_t));
pn.portnfy_port = ep;
ngx_memzero(&sev, sizeof(struct sigevent));
sev.sigev_notify = SIGEV_PORT;
#if !(NGX_TEST_BUILD_EVENTPORT)
sev.sigev_value.sival_ptr = &pn;
#endif
if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"timer_create() failed");
return NGX_ERROR;
}
its.it_interval.tv_sec = timer / 1000;
its.it_interval.tv_nsec = (timer % 1000) * 1000000;
its.it_value.tv_sec = timer / 1000;
its.it_value.tv_nsec = (timer % 1000) * 1000000;
if (timer_settime(event_timer, 0, &its, NULL) == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"timer_settime() failed");
return NGX_ERROR;
}
ngx_event_flags |= NGX_USE_TIMER_EVENT;
}
nevents = epcf->events;
ngx_io = ngx_os_io;
ngx_event_actions = ngx_eventport_module_ctx.actions;
return NGX_OK;
}
static void
ngx_eventport_done(ngx_cycle_t *cycle)
{
if (event_timer != (timer_t) -1) {
if (timer_delete(event_timer) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"timer_delete() failed");
}
event_timer = (timer_t) -1;
}
if (close(ep) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() event port failed");
}
ep = -1;
ngx_free(event_list);
event_list = NULL;
nevents = 0;
}
static ngx_int_t
ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_int_t events, prev;
ngx_event_t *e;
ngx_connection_t *c;
c = ev->data;
events = event;
if (event == NGX_READ_EVENT) {
e = c->write;
prev = POLLOUT;
#if (NGX_READ_EVENT != POLLIN)
events = POLLIN;
#endif
} else {
e = c->read;
prev = POLLIN;
#if (NGX_WRITE_EVENT != POLLOUT)
events = POLLOUT;
#endif
}
if (e->oneshot) {
events |= prev;
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"eventport add event: fd:%d ev:%04Xi", c->fd, events);
if (port_associate(ep, PORT_SOURCE_FD, c->fd, events,
(void *) ((uintptr_t) ev | ev->instance))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"port_associate() failed");
return NGX_ERROR;
}
ev->active = 1;
ev->oneshot = 1;
return NGX_OK;
}
static ngx_int_t
ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_event_t *e;
ngx_connection_t *c;
/*
* when the file descriptor is closed, the event port automatically
* dissociates it from the port, so we do not need to dissociate explicitly
* the event before the closing the file descriptor
*/
if (flags & NGX_CLOSE_EVENT) {
ev->active = 0;
ev->oneshot = 0;
return NGX_OK;
}
c = ev->data;
if (event == NGX_READ_EVENT) {
e = c->write;
event = POLLOUT;
} else {
e = c->read;
event = POLLIN;
}
if (e->oneshot) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"eventport change event: fd:%d ev:%04Xi", c->fd, event);
if (port_associate(ep, PORT_SOURCE_FD, c->fd, event,
(void *) ((uintptr_t) ev | ev->instance))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"port_associate() failed");
return NGX_ERROR;
}
} else {
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"eventport del event: fd:%d", c->fd);
if (port_dissociate(ep, PORT_SOURCE_FD, c->fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"port_dissociate() failed");
return NGX_ERROR;
}
}
ev->active = 0;
ev->oneshot = 0;
return NGX_OK;
}
static ngx_int_t
ngx_eventport_notify(ngx_event_handler_pt handler)
{
notify_event.handler = handler;
if (port_send(ep, 0, &notify_event) != 0) {
ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,
"port_send() failed");
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags)
{
int n, revents;
u_int events;
ngx_err_t err;
ngx_int_t instance;
ngx_uint_t i, level;
ngx_event_t *ev, *rev, *wev;
ngx_queue_t *queue;
ngx_connection_t *c;
struct timespec ts, *tp;
if (timer == NGX_TIMER_INFINITE) {
tp = NULL;
} else {
ts.tv_sec = timer / 1000;
ts.tv_nsec = (timer % 1000) * 1000000;
tp = &ts;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"eventport timer: %M", timer);
events = 1;
n = port_getn(ep, event_list, (u_int) nevents, &events, tp);
err = ngx_errno;
if (flags & NGX_UPDATE_TIME) {
ngx_time_update();
}
if (n == -1) {
if (err == ETIME) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"port_getn() returned no events without timeout");
return NGX_ERROR;
}
level = (err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT;
ngx_log_error(level, cycle->log, err, "port_getn() failed");
return NGX_ERROR;
}
if (events == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"port_getn() returned no events without timeout");
return NGX_ERROR;
}
for (i = 0; i < events; i++) {
if (event_list[i].portev_source == PORT_SOURCE_TIMER) {
ngx_time_update();
continue;
}
ev = event_list[i].portev_user;
switch (event_list[i].portev_source) {
case PORT_SOURCE_FD:
instance = (uintptr_t) ev & 1;
ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);
if (ev->closed || ev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"eventport: stale event %p", ev);
continue;
}
revents = event_list[i].portev_events;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"eventport: fd:%d, ev:%04Xd",
(int) event_list[i].portev_object, revents);
if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"port_getn() error fd:%d ev:%04Xd",
(int) event_list[i].portev_object, revents);
}
if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"strange port_getn() events fd:%d ev:%04Xd",
(int) event_list[i].portev_object, revents);
}
if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
/*
* if the error events were returned, add POLLIN and POLLOUT
* to handle the events at least in one active handler
*/
revents |= POLLIN|POLLOUT;
}
c = ev->data;
rev = c->read;
wev = c->write;
rev->active = 0;
wev->active = 0;
if (revents & POLLIN) {
rev->ready = 1;
if (flags & NGX_POST_EVENTS) {
queue = rev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(rev, queue);
} else {
rev->handler(rev);
if (ev->closed || ev->instance != instance) {
continue;
}
}
if (rev->accept) {
if (ngx_use_accept_mutex) {
ngx_accept_events = 1;
continue;
}
if (port_associate(ep, PORT_SOURCE_FD, c->fd, POLLIN,
(void *) ((uintptr_t) ev | ev->instance))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"port_associate() failed");
return NGX_ERROR;
}
}
}
if (revents & POLLOUT) {
wev->ready = 1;
if (flags & NGX_POST_EVENTS) {
ngx_post_event(wev, &ngx_posted_events);
} else {
wev->handler(wev);
}
}
continue;
case PORT_SOURCE_USER:
ev->handler(ev);
continue;
default:
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"unexpected eventport object %d",
(int) event_list[i].portev_object);
continue;
}
}
return NGX_OK;
}
static void *
ngx_eventport_create_conf(ngx_cycle_t *cycle)
{
ngx_eventport_conf_t *epcf;
epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t));
if (epcf == NULL) {
return NULL;
}
epcf->events = NGX_CONF_UNSET;
return epcf;
}
static char *
ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_eventport_conf_t *epcf = conf;
ngx_conf_init_uint_value(epcf->events, 32);
return NGX_CONF_OK;
}
+380
View File
@@ -0,0 +1,380 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_iocp_module.h>
static ngx_int_t ngx_iocp_init(ngx_cycle_t *cycle, ngx_msec_t timer);
static ngx_thread_value_t __stdcall ngx_iocp_timer(void *data);
static void ngx_iocp_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_iocp_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t key);
static ngx_int_t ngx_iocp_del_connection(ngx_connection_t *c, ngx_uint_t flags);
static ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
static void *ngx_iocp_create_conf(ngx_cycle_t *cycle);
static char *ngx_iocp_init_conf(ngx_cycle_t *cycle, void *conf);
static ngx_str_t iocp_name = ngx_string("iocp");
static ngx_command_t ngx_iocp_commands[] = {
{ ngx_string("iocp_threads"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_iocp_conf_t, threads),
NULL },
{ ngx_string("post_acceptex"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_iocp_conf_t, post_acceptex),
NULL },
{ ngx_string("acceptex_read"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_iocp_conf_t, acceptex_read),
NULL },
ngx_null_command
};
static ngx_event_module_t ngx_iocp_module_ctx = {
&iocp_name,
ngx_iocp_create_conf, /* create configuration */
ngx_iocp_init_conf, /* init configuration */
{
ngx_iocp_add_event, /* add an event */
NULL, /* delete an event */
NULL, /* enable an event */
NULL, /* disable an event */
NULL, /* add an connection */
ngx_iocp_del_connection, /* delete an connection */
NULL, /* trigger a notify */
ngx_iocp_process_events, /* process the events */
ngx_iocp_init, /* init the events */
ngx_iocp_done /* done the events */
}
};
ngx_module_t ngx_iocp_module = {
NGX_MODULE_V1,
&ngx_iocp_module_ctx, /* module context */
ngx_iocp_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
ngx_os_io_t ngx_iocp_io = {
ngx_overlapped_wsarecv,
NULL,
ngx_udp_overlapped_wsarecv,
NULL,
NULL,
NULL,
ngx_overlapped_wsasend_chain,
0
};
static HANDLE iocp;
static ngx_tid_t timer_thread;
static ngx_msec_t msec;
static ngx_int_t
ngx_iocp_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
ngx_iocp_conf_t *cf;
cf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
if (iocp == NULL) {
iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
cf->threads);
}
if (iocp == NULL) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"CreateIoCompletionPort() failed");
return NGX_ERROR;
}
ngx_io = ngx_iocp_io;
ngx_event_actions = ngx_iocp_module_ctx.actions;
ngx_event_flags = NGX_USE_IOCP_EVENT;
if (timer == 0) {
return NGX_OK;
}
/*
* The waitable timer could not be used, because
* GetQueuedCompletionStatus() does not set a thread to alertable state
*/
if (timer_thread == NULL) {
msec = timer;
if (ngx_create_thread(&timer_thread, ngx_iocp_timer, &msec, cycle->log)
!= 0)
{
return NGX_ERROR;
}
}
ngx_event_flags |= NGX_USE_TIMER_EVENT;
return NGX_OK;
}
static ngx_thread_value_t __stdcall
ngx_iocp_timer(void *data)
{
ngx_msec_t timer = *(ngx_msec_t *) data;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
"THREAD %p %p", &msec, data);
for ( ;; ) {
Sleep(timer);
ngx_time_update();
#if 1
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer");
#endif
}
#if defined(__WATCOMC__) || defined(__GNUC__)
return 0;
#endif
}
static void
ngx_iocp_done(ngx_cycle_t *cycle)
{
if (CloseHandle(iocp) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"iocp CloseHandle() failed");
}
iocp = NULL;
}
static ngx_int_t
ngx_iocp_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t key)
{
ngx_connection_t *c;
c = (ngx_connection_t *) ev->data;
c->read->active = 1;
c->write->active = 1;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"iocp add: fd:%d k:%ui ov:%p", c->fd, key, &ev->ovlp);
if (CreateIoCompletionPort((HANDLE) c->fd, iocp, key, 0) == NULL) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"CreateIoCompletionPort() failed");
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_iocp_del_connection(ngx_connection_t *c, ngx_uint_t flags)
{
#if 0
if (flags & NGX_CLOSE_EVENT) {
return NGX_OK;
}
if (CancelIo((HANDLE) c->fd) == 0) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, "CancelIo() failed");
return NGX_ERROR;
}
#endif
return NGX_OK;
}
static
ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags)
{
int rc;
u_int key;
u_long bytes;
ngx_err_t err;
ngx_msec_t delta;
ngx_event_t *ev;
ngx_event_ovlp_t *ovlp;
if (timer == NGX_TIMER_INFINITE) {
timer = INFINITE;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "iocp timer: %M", timer);
rc = GetQueuedCompletionStatus(iocp, &bytes, (PULONG_PTR) &key,
(LPOVERLAPPED *) &ovlp, (u_long) timer);
if (rc == 0) {
err = ngx_errno;
} else {
err = 0;
}
delta = ngx_current_msec;
if (flags & NGX_UPDATE_TIME) {
ngx_time_update();
}
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"iocp: %d b:%d k:%d ov:%p", rc, bytes, key, ovlp);
if (timer != INFINITE) {
delta = ngx_current_msec - delta;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"iocp timer: %M, delta: %M", timer, delta);
}
if (err) {
if (ovlp == NULL) {
if (err != WAIT_TIMEOUT) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
"GetQueuedCompletionStatus() failed");
return NGX_ERROR;
}
return NGX_OK;
}
ovlp->error = err;
}
if (ovlp == NULL) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"GetQueuedCompletionStatus() returned no operation");
return NGX_ERROR;
}
ev = ovlp->event;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err, "iocp event:%p", ev);
if (err == ERROR_NETNAME_DELETED /* the socket was closed */
|| err == ERROR_OPERATION_ABORTED /* the operation was canceled */)
{
/*
* the WSA_OPERATION_ABORTED completion notification
* for a file descriptor that was closed
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,
"iocp: aborted event %p", ev);
return NGX_OK;
}
if (err) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
"GetQueuedCompletionStatus() returned operation error");
}
switch (key) {
case NGX_IOCP_ACCEPT:
if (bytes) {
ev->ready = 1;
}
break;
case NGX_IOCP_IO:
ev->complete = 1;
ev->ready = 1;
break;
case NGX_IOCP_CONNECT:
ev->ready = 1;
}
ev->available = bytes;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"iocp event handler: %p", ev->handler);
ev->handler(ev);
return NGX_OK;
}
static void *
ngx_iocp_create_conf(ngx_cycle_t *cycle)
{
ngx_iocp_conf_t *cf;
cf = ngx_palloc(cycle->pool, sizeof(ngx_iocp_conf_t));
if (cf == NULL) {
return NGX_CONF_ERROR;
}
cf->threads = NGX_CONF_UNSET;
cf->post_acceptex = NGX_CONF_UNSET;
cf->acceptex_read = NGX_CONF_UNSET;
return cf;
}
static char *
ngx_iocp_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_iocp_conf_t *cf = conf;
ngx_conf_init_value(cf->threads, 0);
ngx_conf_init_value(cf->post_acceptex, 10);
ngx_conf_init_value(cf->acceptex_read, 1);
return NGX_CONF_OK;
}
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_IOCP_MODULE_H_INCLUDED_
#define _NGX_IOCP_MODULE_H_INCLUDED_
typedef struct {
int threads;
int post_acceptex;
int acceptex_read;
} ngx_iocp_conf_t;
extern ngx_module_t ngx_iocp_module;
#endif /* _NGX_IOCP_MODULE_H_INCLUDED_ */
+722
View File
@@ -0,0 +1,722 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
typedef struct {
ngx_uint_t changes;
ngx_uint_t events;
} ngx_kqueue_conf_t;
static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer);
#ifdef EVFILT_USER
static ngx_int_t ngx_kqueue_notify_init(ngx_log_t *log);
#endif
static void ngx_kqueue_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter,
ngx_uint_t flags);
#ifdef EVFILT_USER
static ngx_int_t ngx_kqueue_notify(ngx_event_handler_pt handler);
#endif
static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log,
struct kevent *kev);
static void *ngx_kqueue_create_conf(ngx_cycle_t *cycle);
static char *ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf);
int ngx_kqueue = -1;
static struct kevent *change_list;
static struct kevent *event_list;
static ngx_uint_t max_changes, nchanges, nevents;
#ifdef EVFILT_USER
static ngx_event_t notify_event;
static struct kevent notify_kev;
#endif
static ngx_str_t kqueue_name = ngx_string("kqueue");
static ngx_command_t ngx_kqueue_commands[] = {
{ ngx_string("kqueue_changes"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_kqueue_conf_t, changes),
NULL },
{ ngx_string("kqueue_events"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_kqueue_conf_t, events),
NULL },
ngx_null_command
};
static ngx_event_module_t ngx_kqueue_module_ctx = {
&kqueue_name,
ngx_kqueue_create_conf, /* create configuration */
ngx_kqueue_init_conf, /* init configuration */
{
ngx_kqueue_add_event, /* add an event */
ngx_kqueue_del_event, /* delete an event */
ngx_kqueue_add_event, /* enable an event */
ngx_kqueue_del_event, /* disable an event */
NULL, /* add an connection */
NULL, /* delete an connection */
#ifdef EVFILT_USER
ngx_kqueue_notify, /* trigger a notify */
#else
NULL, /* trigger a notify */
#endif
ngx_kqueue_process_events, /* process the events */
ngx_kqueue_init, /* init the events */
ngx_kqueue_done /* done the events */
}
};
ngx_module_t ngx_kqueue_module = {
NGX_MODULE_V1,
&ngx_kqueue_module_ctx, /* module context */
ngx_kqueue_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
ngx_kqueue_conf_t *kcf;
struct timespec ts;
#if (NGX_HAVE_TIMER_EVENT)
struct kevent kev;
#endif
kcf = ngx_event_get_conf(cycle->conf_ctx, ngx_kqueue_module);
if (ngx_kqueue == -1) {
ngx_kqueue = kqueue();
if (ngx_kqueue == -1) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
"kqueue() failed");
return NGX_ERROR;
}
#ifdef EVFILT_USER
if (ngx_kqueue_notify_init(cycle->log) != NGX_OK) {
return NGX_ERROR;
}
#endif
}
if (max_changes < kcf->changes) {
if (nchanges) {
ts.tv_sec = 0;
ts.tv_nsec = 0;
if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)
== -1)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"kevent() failed");
return NGX_ERROR;
}
nchanges = 0;
}
if (change_list) {
ngx_free(change_list);
}
change_list = ngx_alloc(kcf->changes * sizeof(struct kevent),
cycle->log);
if (change_list == NULL) {
return NGX_ERROR;
}
}
max_changes = kcf->changes;
if (nevents < kcf->events) {
if (event_list) {
ngx_free(event_list);
}
event_list = ngx_alloc(kcf->events * sizeof(struct kevent), cycle->log);
if (event_list == NULL) {
return NGX_ERROR;
}
}
ngx_event_flags = NGX_USE_ONESHOT_EVENT
|NGX_USE_KQUEUE_EVENT
|NGX_USE_VNODE_EVENT;
#if (NGX_HAVE_TIMER_EVENT)
if (timer) {
kev.ident = 0;
kev.filter = EVFILT_TIMER;
kev.flags = EV_ADD|EV_ENABLE;
kev.fflags = 0;
kev.data = timer;
kev.udata = 0;
ts.tv_sec = 0;
ts.tv_nsec = 0;
if (kevent(ngx_kqueue, &kev, 1, NULL, 0, &ts) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"kevent(EVFILT_TIMER) failed");
return NGX_ERROR;
}
ngx_event_flags |= NGX_USE_TIMER_EVENT;
}
#endif
#if (NGX_HAVE_CLEAR_EVENT)
ngx_event_flags |= NGX_USE_CLEAR_EVENT;
#else
ngx_event_flags |= NGX_USE_LEVEL_EVENT;
#endif
#if (NGX_HAVE_LOWAT_EVENT)
ngx_event_flags |= NGX_USE_LOWAT_EVENT;
#endif
nevents = kcf->events;
ngx_io = ngx_os_io;
ngx_event_actions = ngx_kqueue_module_ctx.actions;
return NGX_OK;
}
#ifdef EVFILT_USER
static ngx_int_t
ngx_kqueue_notify_init(ngx_log_t *log)
{
notify_kev.ident = 0;
notify_kev.filter = EVFILT_USER;
notify_kev.data = 0;
notify_kev.flags = EV_ADD|EV_CLEAR;
notify_kev.fflags = 0;
notify_kev.udata = 0;
if (kevent(ngx_kqueue, &notify_kev, 1, NULL, 0, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
"kevent(EVFILT_USER, EV_ADD) failed");
return NGX_ERROR;
}
notify_event.active = 1;
notify_event.log = log;
notify_kev.flags = 0;
notify_kev.fflags = NOTE_TRIGGER;
notify_kev.udata = NGX_KQUEUE_UDATA_T ((uintptr_t) &notify_event);
return NGX_OK;
}
#endif
static void
ngx_kqueue_done(ngx_cycle_t *cycle)
{
if (close(ngx_kqueue) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"kqueue close() failed");
}
ngx_kqueue = -1;
ngx_free(change_list);
ngx_free(event_list);
change_list = NULL;
event_list = NULL;
max_changes = 0;
nchanges = 0;
nevents = 0;
}
static ngx_int_t
ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_int_t rc;
#if 0
ngx_event_t *e;
ngx_connection_t *c;
#endif
ev->active = 1;
ev->disabled = 0;
ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
#if 0
if (ev->index < nchanges
&& ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)
== (uintptr_t) ev)
{
if (change_list[ev->index].flags == EV_DISABLE) {
/*
* if the EV_DISABLE is still not passed to a kernel
* we will not pass it
*/
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"kevent activated: %d: ft:%i",
ngx_event_ident(ev->data), event);
if (ev->index < --nchanges) {
e = (ngx_event_t *)
((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);
change_list[ev->index] = change_list[nchanges];
e->index = ev->index;
}
return NGX_OK;
}
c = ev->data;
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"previous event on #%d were not passed in kernel", c->fd);
return NGX_ERROR;
}
#endif
rc = ngx_kqueue_set_event(ev, event, EV_ADD|EV_ENABLE|flags);
return rc;
}
static ngx_int_t
ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_int_t rc;
ngx_event_t *e;
ev->active = 0;
ev->disabled = 0;
if (ev->index < nchanges
&& ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)
== (uintptr_t) ev)
{
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"kevent deleted: %d: ft:%i",
ngx_event_ident(ev->data), event);
/* if the event is still not passed to a kernel we will not pass it */
nchanges--;
if (ev->index < nchanges) {
e = (ngx_event_t *)
((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);
change_list[ev->index] = change_list[nchanges];
e->index = ev->index;
}
return NGX_OK;
}
/*
* when the file descriptor is closed the kqueue automatically deletes
* its filters so we do not need to delete explicitly the event
* before the closing the file descriptor.
*/
if (flags & NGX_CLOSE_EVENT) {
return NGX_OK;
}
if (flags & NGX_DISABLE_EVENT) {
ev->disabled = 1;
} else {
flags |= EV_DELETE;
}
rc = ngx_kqueue_set_event(ev, event, flags);
return rc;
}
static ngx_int_t
ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, ngx_uint_t flags)
{
struct kevent *kev;
struct timespec ts;
ngx_connection_t *c;
c = ev->data;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"kevent set event: %d: ft:%i fl:%04Xi",
c->fd, filter, flags);
if (nchanges >= max_changes) {
ngx_log_error(NGX_LOG_WARN, ev->log, 0,
"kqueue change list is filled up");
ts.tv_sec = 0;
ts.tv_nsec = 0;
if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)
== -1)
{
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed");
return NGX_ERROR;
}
nchanges = 0;
}
kev = &change_list[nchanges];
kev->ident = c->fd;
kev->filter = (short) filter;
kev->flags = (u_short) flags;
kev->udata = NGX_KQUEUE_UDATA_T ((uintptr_t) ev | ev->instance);
if (filter == EVFILT_VNODE) {
kev->fflags = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND
|NOTE_ATTRIB|NOTE_RENAME
#if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \
|| __FreeBSD_version >= 500018
|NOTE_REVOKE
#endif
;
kev->data = 0;
} else {
#if (NGX_HAVE_LOWAT_EVENT)
if (flags & NGX_LOWAT_EVENT) {
kev->fflags = NOTE_LOWAT;
kev->data = ev->available;
} else {
kev->fflags = 0;
kev->data = 0;
}
#else
kev->fflags = 0;
kev->data = 0;
#endif
}
ev->index = nchanges;
nchanges++;
if (flags & NGX_FLUSH_EVENT) {
ts.tv_sec = 0;
ts.tv_nsec = 0;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent flush");
if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)
== -1)
{
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed");
return NGX_ERROR;
}
nchanges = 0;
}
return NGX_OK;
}
#ifdef EVFILT_USER
static ngx_int_t
ngx_kqueue_notify(ngx_event_handler_pt handler)
{
notify_event.handler = handler;
if (kevent(ngx_kqueue, &notify_kev, 1, NULL, 0, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,
"kevent(EVFILT_USER, NOTE_TRIGGER) failed");
return NGX_ERROR;
}
return NGX_OK;
}
#endif
static ngx_int_t
ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags)
{
int events, n;
ngx_int_t i, instance;
ngx_uint_t level;
ngx_err_t err;
ngx_event_t *ev;
ngx_queue_t *queue;
struct timespec ts, *tp;
n = (int) nchanges;
nchanges = 0;
if (timer == NGX_TIMER_INFINITE) {
tp = NULL;
} else {
ts.tv_sec = timer / 1000;
ts.tv_nsec = (timer % 1000) * 1000000;
/*
* 64-bit Darwin kernel has the bug: kernel level ts.tv_nsec is
* the int32_t while user level ts.tv_nsec is the long (64-bit),
* so on the big endian PowerPC all nanoseconds are lost.
*/
#if (NGX_DARWIN_KEVENT_BUG)
ts.tv_nsec <<= 32;
#endif
tp = &ts;
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"kevent timer: %M, changes: %d", timer, n);
events = kevent(ngx_kqueue, change_list, n, event_list, (int) nevents, tp);
err = (events == -1) ? ngx_errno : 0;
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
ngx_time_update();
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"kevent events: %d", events);
if (err) {
if (err == NGX_EINTR) {
if (ngx_event_timer_alarm) {
ngx_event_timer_alarm = 0;
return NGX_OK;
}
level = NGX_LOG_INFO;
} else {
level = NGX_LOG_ALERT;
}
ngx_log_error(level, cycle->log, err, "kevent() failed");
return NGX_ERROR;
}
if (events == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"kevent() returned no events without timeout");
return NGX_ERROR;
}
for (i = 0; i < events; i++) {
ngx_kqueue_dump_event(cycle->log, &event_list[i]);
if (event_list[i].flags & EV_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, event_list[i].data,
"kevent() error on %d filter:%d flags:%04Xd",
(int) event_list[i].ident, event_list[i].filter,
event_list[i].flags);
continue;
}
#if (NGX_HAVE_TIMER_EVENT)
if (event_list[i].filter == EVFILT_TIMER) {
ngx_time_update();
continue;
}
#endif
ev = (ngx_event_t *) event_list[i].udata;
switch (event_list[i].filter) {
case EVFILT_READ:
case EVFILT_WRITE:
instance = (uintptr_t) ev & 1;
ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);
if (ev->closed || ev->instance != instance) {
/*
* the stale event from a file descriptor
* that was just closed in this iteration
*/
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"kevent: stale event %p", ev);
continue;
}
if (ev->log && (ev->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
ngx_kqueue_dump_event(ev->log, &event_list[i]);
}
if (ev->oneshot) {
ev->active = 0;
}
ev->available = event_list[i].data;
if (event_list[i].flags & EV_EOF) {
ev->pending_eof = 1;
ev->kq_errno = event_list[i].fflags;
}
ev->ready = 1;
break;
case EVFILT_VNODE:
ev->kq_vnode = 1;
break;
case EVFILT_AIO:
ev->complete = 1;
ev->ready = 1;
break;
#ifdef EVFILT_USER
case EVFILT_USER:
break;
#endif
default:
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"unexpected kevent() filter %d",
event_list[i].filter);
continue;
}
if (flags & NGX_POST_EVENTS) {
queue = ev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(ev, queue);
continue;
}
ev->handler(ev);
}
return NGX_OK;
}
static ngx_inline void
ngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev)
{
if (kev->ident > 0x8000000 && kev->ident != (unsigned) -1) {
ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0,
"kevent: %p: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p",
(void *) kev->ident, kev->filter,
kev->flags, kev->fflags,
(int) kev->data, kev->udata);
} else {
ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0,
"kevent: %d: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p",
(int) kev->ident, kev->filter,
kev->flags, kev->fflags,
(int) kev->data, kev->udata);
}
}
static void *
ngx_kqueue_create_conf(ngx_cycle_t *cycle)
{
ngx_kqueue_conf_t *kcf;
kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t));
if (kcf == NULL) {
return NULL;
}
kcf->changes = NGX_CONF_UNSET;
kcf->events = NGX_CONF_UNSET;
return kcf;
}
static char *
ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_kqueue_conf_t *kcf = conf;
ngx_conf_init_uint_value(kcf->changes, 512);
ngx_conf_init_uint_value(kcf->events, 512);
return NGX_CONF_OK;
}
+415
View File
@@ -0,0 +1,415 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
static void ngx_poll_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf);
static struct pollfd *event_list;
static ngx_uint_t nevents;
static ngx_str_t poll_name = ngx_string("poll");
static ngx_event_module_t ngx_poll_module_ctx = {
&poll_name,
NULL, /* create configuration */
ngx_poll_init_conf, /* init configuration */
{
ngx_poll_add_event, /* add an event */
ngx_poll_del_event, /* delete an event */
ngx_poll_add_event, /* enable an event */
ngx_poll_del_event, /* disable an event */
NULL, /* add an connection */
NULL, /* delete an connection */
NULL, /* trigger a notify */
ngx_poll_process_events, /* process the events */
ngx_poll_init, /* init the events */
ngx_poll_done /* done the events */
}
};
ngx_module_t ngx_poll_module = {
NGX_MODULE_V1,
&ngx_poll_module_ctx, /* module context */
NULL, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
struct pollfd *list;
if (event_list == NULL) {
nevents = 0;
}
if (ngx_process >= NGX_PROCESS_WORKER
|| cycle->old_cycle == NULL
|| cycle->old_cycle->connection_n < cycle->connection_n)
{
list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n,
cycle->log);
if (list == NULL) {
return NGX_ERROR;
}
if (event_list) {
ngx_memcpy(list, event_list, sizeof(ngx_event_t *) * nevents);
ngx_free(event_list);
}
event_list = list;
}
ngx_io = ngx_os_io;
ngx_event_actions = ngx_poll_module_ctx.actions;
ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;
return NGX_OK;
}
static void
ngx_poll_done(ngx_cycle_t *cycle)
{
ngx_free(event_list);
event_list = NULL;
}
static ngx_int_t
ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_event_t *e;
ngx_connection_t *c;
c = ev->data;
ev->active = 1;
if (ev->index != NGX_INVALID_INDEX) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"poll event fd:%d ev:%i is already set", c->fd, event);
return NGX_OK;
}
if (event == NGX_READ_EVENT) {
e = c->write;
#if (NGX_READ_EVENT != POLLIN)
event = POLLIN;
#endif
} else {
e = c->read;
#if (NGX_WRITE_EVENT != POLLOUT)
event = POLLOUT;
#endif
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"poll add event: fd:%d ev:%i", c->fd, event);
if (e == NULL || e->index == NGX_INVALID_INDEX) {
event_list[nevents].fd = c->fd;
event_list[nevents].events = (short) event;
event_list[nevents].revents = 0;
ev->index = nevents;
nevents++;
} else {
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"poll add index: %i", e->index);
event_list[e->index].events |= (short) event;
ev->index = e->index;
}
return NGX_OK;
}
static ngx_int_t
ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_event_t *e;
ngx_connection_t *c;
c = ev->data;
ev->active = 0;
if (ev->index == NGX_INVALID_INDEX) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"poll event fd:%d ev:%i is already deleted",
c->fd, event);
return NGX_OK;
}
if (event == NGX_READ_EVENT) {
e = c->write;
#if (NGX_READ_EVENT != POLLIN)
event = POLLIN;
#endif
} else {
e = c->read;
#if (NGX_WRITE_EVENT != POLLOUT)
event = POLLOUT;
#endif
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"poll del event: fd:%d ev:%i", c->fd, event);
if (e == NULL || e->index == NGX_INVALID_INDEX) {
nevents--;
if (ev->index < nevents) {
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"index: copy event %ui to %i", nevents, ev->index);
event_list[ev->index] = event_list[nevents];
c = ngx_cycle->files[event_list[nevents].fd];
if (c->fd == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"unexpected last event");
} else {
if (c->read->index == nevents) {
c->read->index = ev->index;
}
if (c->write->index == nevents) {
c->write->index = ev->index;
}
}
}
} else {
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"poll del index: %i", e->index);
event_list[e->index].events &= (short) ~event;
}
ev->index = NGX_INVALID_INDEX;
return NGX_OK;
}
static ngx_int_t
ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
int ready, revents;
ngx_err_t err;
ngx_uint_t i, found, level;
ngx_event_t *ev;
ngx_queue_t *queue;
ngx_connection_t *c;
/* NGX_TIMER_INFINITE == INFTIM */
#if (NGX_DEBUG0)
if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
for (i = 0; i < nevents; i++) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"poll: %ui: fd:%d ev:%04Xd",
i, event_list[i].fd, event_list[i].events);
}
}
#endif
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll timer: %M", timer);
ready = poll(event_list, (u_int) nevents, (int) timer);
err = (ready == -1) ? ngx_errno : 0;
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
ngx_time_update();
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"poll ready %d of %ui", ready, nevents);
if (err) {
if (err == NGX_EINTR) {
if (ngx_event_timer_alarm) {
ngx_event_timer_alarm = 0;
return NGX_OK;
}
level = NGX_LOG_INFO;
} else {
level = NGX_LOG_ALERT;
}
ngx_log_error(level, cycle->log, err, "poll() failed");
return NGX_ERROR;
}
if (ready == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"poll() returned no events without timeout");
return NGX_ERROR;
}
for (i = 0; i < nevents && ready; i++) {
revents = event_list[i].revents;
#if 1
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"poll: %ui: fd:%d ev:%04Xd rev:%04Xd",
i, event_list[i].fd, event_list[i].events, revents);
#else
if (revents) {
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"poll: %ui: fd:%d ev:%04Xd rev:%04Xd",
i, event_list[i].fd, event_list[i].events, revents);
}
#endif
if (revents & POLLNVAL) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"poll() error fd:%d ev:%04Xd rev:%04Xd",
event_list[i].fd, event_list[i].events, revents);
}
if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"strange poll() events fd:%d ev:%04Xd rev:%04Xd",
event_list[i].fd, event_list[i].events, revents);
}
if (event_list[i].fd == -1) {
/*
* the disabled event, a workaround for our possible bug,
* see the comment below
*/
continue;
}
c = ngx_cycle->files[event_list[i].fd];
if (c->fd == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event");
/*
* it is certainly our fault and it should be investigated,
* in the meantime we disable this event to avoid a CPU spinning
*/
if (i == nevents - 1) {
nevents--;
} else {
event_list[i].fd = -1;
}
continue;
}
if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
/*
* if the error events were returned, add POLLIN and POLLOUT
* to handle the events at least in one active handler
*/
revents |= POLLIN|POLLOUT;
}
found = 0;
if ((revents & POLLIN) && c->read->active) {
found = 1;
ev = c->read;
ev->ready = 1;
queue = ev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(ev, queue);
}
if ((revents & POLLOUT) && c->write->active) {
found = 1;
ev = c->write;
ev->ready = 1;
ngx_post_event(ev, &ngx_posted_events);
}
if (found) {
ready--;
continue;
}
}
if (ready != 0) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll ready != events");
}
return NGX_OK;
}
static char *
ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_event_conf_t *ecf;
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
if (ecf->use != ngx_poll_module.ctx_index) {
return NGX_CONF_OK;
}
return NGX_CONF_OK;
}
+423
View File
@@ -0,0 +1,423 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);
static void ngx_select_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
static fd_set master_read_fd_set;
static fd_set master_write_fd_set;
static fd_set work_read_fd_set;
static fd_set work_write_fd_set;
static ngx_int_t max_fd;
static ngx_uint_t nevents;
static ngx_event_t **event_index;
static ngx_str_t select_name = ngx_string("select");
static ngx_event_module_t ngx_select_module_ctx = {
&select_name,
NULL, /* create configuration */
ngx_select_init_conf, /* init configuration */
{
ngx_select_add_event, /* add an event */
ngx_select_del_event, /* delete an event */
ngx_select_add_event, /* enable an event */
ngx_select_del_event, /* disable an event */
NULL, /* add an connection */
NULL, /* delete an connection */
NULL, /* trigger a notify */
ngx_select_process_events, /* process the events */
ngx_select_init, /* init the events */
ngx_select_done /* done the events */
}
};
ngx_module_t ngx_select_module = {
NGX_MODULE_V1,
&ngx_select_module_ctx, /* module context */
NULL, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
ngx_event_t **index;
if (event_index == NULL) {
FD_ZERO(&master_read_fd_set);
FD_ZERO(&master_write_fd_set);
nevents = 0;
}
if (ngx_process >= NGX_PROCESS_WORKER
|| cycle->old_cycle == NULL
|| cycle->old_cycle->connection_n < cycle->connection_n)
{
index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,
cycle->log);
if (index == NULL) {
return NGX_ERROR;
}
if (event_index) {
ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);
ngx_free(event_index);
}
event_index = index;
}
ngx_io = ngx_os_io;
ngx_event_actions = ngx_select_module_ctx.actions;
ngx_event_flags = NGX_USE_LEVEL_EVENT;
max_fd = -1;
return NGX_OK;
}
static void
ngx_select_done(ngx_cycle_t *cycle)
{
ngx_free(event_index);
event_index = NULL;
}
static ngx_int_t
ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_connection_t *c;
c = ev->data;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"select add event fd:%d ev:%i", c->fd, event);
if (ev->index != NGX_INVALID_INDEX) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"select event fd:%d ev:%i is already set", c->fd, event);
return NGX_OK;
}
if ((event == NGX_READ_EVENT && ev->write)
|| (event == NGX_WRITE_EVENT && !ev->write))
{
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"invalid select %s event fd:%d ev:%i",
ev->write ? "write" : "read", c->fd, event);
return NGX_ERROR;
}
if (event == NGX_READ_EVENT) {
FD_SET(c->fd, &master_read_fd_set);
} else if (event == NGX_WRITE_EVENT) {
FD_SET(c->fd, &master_write_fd_set);
}
if (max_fd != -1 && max_fd < c->fd) {
max_fd = c->fd;
}
ev->active = 1;
event_index[nevents] = ev;
ev->index = nevents;
nevents++;
return NGX_OK;
}
static ngx_int_t
ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_event_t *e;
ngx_connection_t *c;
c = ev->data;
ev->active = 0;
if (ev->index == NGX_INVALID_INDEX) {
return NGX_OK;
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"select del event fd:%d ev:%i", c->fd, event);
if (event == NGX_READ_EVENT) {
FD_CLR(c->fd, &master_read_fd_set);
} else if (event == NGX_WRITE_EVENT) {
FD_CLR(c->fd, &master_write_fd_set);
}
if (max_fd == c->fd) {
max_fd = -1;
}
if (ev->index < --nevents) {
e = event_index[nevents];
event_index[ev->index] = e;
e->index = ev->index;
}
ev->index = NGX_INVALID_INDEX;
return NGX_OK;
}
static ngx_int_t
ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags)
{
int ready, nready;
ngx_err_t err;
ngx_uint_t i, found;
ngx_event_t *ev;
ngx_queue_t *queue;
struct timeval tv, *tp;
ngx_connection_t *c;
if (max_fd == -1) {
for (i = 0; i < nevents; i++) {
c = event_index[i]->data;
if (max_fd < c->fd) {
max_fd = c->fd;
}
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"change max_fd: %i", max_fd);
}
#if (NGX_DEBUG)
if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
for (i = 0; i < nevents; i++) {
ev = event_index[i];
c = ev->data;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select event: fd:%d wr:%d", c->fd, ev->write);
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"max_fd: %i", max_fd);
}
#endif
if (timer == NGX_TIMER_INFINITE) {
tp = NULL;
} else {
tv.tv_sec = (long) (timer / 1000);
tv.tv_usec = (long) ((timer % 1000) * 1000);
tp = &tv;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select timer: %M", timer);
work_read_fd_set = master_read_fd_set;
work_write_fd_set = master_write_fd_set;
ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);
err = (ready == -1) ? ngx_errno : 0;
if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
ngx_time_update();
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select ready %d", ready);
if (err) {
ngx_uint_t level;
if (err == NGX_EINTR) {
if (ngx_event_timer_alarm) {
ngx_event_timer_alarm = 0;
return NGX_OK;
}
level = NGX_LOG_INFO;
} else {
level = NGX_LOG_ALERT;
}
ngx_log_error(level, cycle->log, err, "select() failed");
if (err == NGX_EBADF) {
ngx_select_repair_fd_sets(cycle);
}
return NGX_ERROR;
}
if (ready == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"select() returned no events without timeout");
return NGX_ERROR;
}
nready = 0;
for (i = 0; i < nevents; i++) {
ev = event_index[i];
c = ev->data;
found = 0;
if (ev->write) {
if (FD_ISSET(c->fd, &work_write_fd_set)) {
found = 1;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select write %d", c->fd);
}
} else {
if (FD_ISSET(c->fd, &work_read_fd_set)) {
found = 1;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select read %d", c->fd);
}
}
if (found) {
ev->ready = 1;
queue = ev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(ev, queue);
nready++;
}
}
if (ready != nready) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"select ready != events: %d:%d", ready, nready);
ngx_select_repair_fd_sets(cycle);
}
return NGX_OK;
}
static void
ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
{
int n;
socklen_t len;
ngx_err_t err;
ngx_socket_t s;
for (s = 0; s <= max_fd; s++) {
if (FD_ISSET(s, &master_read_fd_set) == 0) {
continue;
}
len = sizeof(int);
if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
err = ngx_socket_errno;
ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
"invalid descriptor #%d in read fd_set", s);
FD_CLR(s, &master_read_fd_set);
}
}
for (s = 0; s <= max_fd; s++) {
if (FD_ISSET(s, &master_write_fd_set) == 0) {
continue;
}
len = sizeof(int);
if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
err = ngx_socket_errno;
ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
"invalid descriptor #%d in write fd_set", s);
FD_CLR(s, &master_write_fd_set);
}
}
max_fd = -1;
}
static char *
ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_event_conf_t *ecf;
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
if (ecf->use != ngx_select_module.ctx_index) {
return NGX_CONF_OK;
}
/* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */
if (cycle->connection_n > FD_SETSIZE) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"the maximum number of files "
"supported by select() is %ud", FD_SETSIZE);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
+398
View File
@@ -0,0 +1,398 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);
static void ngx_select_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,
ngx_uint_t flags);
static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
static fd_set master_read_fd_set;
static fd_set master_write_fd_set;
static fd_set work_read_fd_set;
static fd_set work_write_fd_set;
static ngx_uint_t max_read;
static ngx_uint_t max_write;
static ngx_uint_t nevents;
static ngx_event_t **event_index;
static ngx_str_t select_name = ngx_string("select");
static ngx_event_module_t ngx_select_module_ctx = {
&select_name,
NULL, /* create configuration */
ngx_select_init_conf, /* init configuration */
{
ngx_select_add_event, /* add an event */
ngx_select_del_event, /* delete an event */
ngx_select_add_event, /* enable an event */
ngx_select_del_event, /* disable an event */
NULL, /* add an connection */
NULL, /* delete an connection */
NULL, /* trigger a notify */
ngx_select_process_events, /* process the events */
ngx_select_init, /* init the events */
ngx_select_done /* done the events */
}
};
ngx_module_t ngx_select_module = {
NGX_MODULE_V1,
&ngx_select_module_ctx, /* module context */
NULL, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_int_t
ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)
{
ngx_event_t **index;
if (event_index == NULL) {
FD_ZERO(&master_read_fd_set);
FD_ZERO(&master_write_fd_set);
nevents = 0;
}
if (ngx_process >= NGX_PROCESS_WORKER
|| cycle->old_cycle == NULL
|| cycle->old_cycle->connection_n < cycle->connection_n)
{
index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,
cycle->log);
if (index == NULL) {
return NGX_ERROR;
}
if (event_index) {
ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);
ngx_free(event_index);
}
event_index = index;
}
ngx_io = ngx_os_io;
ngx_event_actions = ngx_select_module_ctx.actions;
ngx_event_flags = NGX_USE_LEVEL_EVENT;
max_read = 0;
max_write = 0;
return NGX_OK;
}
static void
ngx_select_done(ngx_cycle_t *cycle)
{
ngx_free(event_index);
event_index = NULL;
}
static ngx_int_t
ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_connection_t *c;
c = ev->data;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"select add event fd:%d ev:%i", c->fd, event);
if (ev->index != NGX_INVALID_INDEX) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"select event fd:%d ev:%i is already set", c->fd, event);
return NGX_OK;
}
if ((event == NGX_READ_EVENT && ev->write)
|| (event == NGX_WRITE_EVENT && !ev->write))
{
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"invalid select %s event fd:%d ev:%i",
ev->write ? "write" : "read", c->fd, event);
return NGX_ERROR;
}
if ((event == NGX_READ_EVENT && max_read >= FD_SETSIZE)
|| (event == NGX_WRITE_EVENT && max_write >= FD_SETSIZE))
{
ngx_log_error(NGX_LOG_ERR, ev->log, 0,
"maximum number of descriptors "
"supported by select() is %d", FD_SETSIZE);
return NGX_ERROR;
}
if (event == NGX_READ_EVENT) {
FD_SET(c->fd, &master_read_fd_set);
max_read++;
} else if (event == NGX_WRITE_EVENT) {
FD_SET(c->fd, &master_write_fd_set);
max_write++;
}
ev->active = 1;
event_index[nevents] = ev;
ev->index = nevents;
nevents++;
return NGX_OK;
}
static ngx_int_t
ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
ngx_event_t *e;
ngx_connection_t *c;
c = ev->data;
ev->active = 0;
if (ev->index == NGX_INVALID_INDEX) {
return NGX_OK;
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"select del event fd:%d ev:%i", c->fd, event);
if (event == NGX_READ_EVENT) {
FD_CLR(c->fd, &master_read_fd_set);
max_read--;
} else if (event == NGX_WRITE_EVENT) {
FD_CLR(c->fd, &master_write_fd_set);
max_write--;
}
if (ev->index < --nevents) {
e = event_index[nevents];
event_index[ev->index] = e;
e->index = ev->index;
}
ev->index = NGX_INVALID_INDEX;
return NGX_OK;
}
static ngx_int_t
ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags)
{
int ready, nready;
ngx_err_t err;
ngx_uint_t i, found;
ngx_event_t *ev;
ngx_queue_t *queue;
struct timeval tv, *tp;
ngx_connection_t *c;
#if (NGX_DEBUG)
if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
for (i = 0; i < nevents; i++) {
ev = event_index[i];
c = ev->data;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select event: fd:%d wr:%d", c->fd, ev->write);
}
}
#endif
if (timer == NGX_TIMER_INFINITE) {
tp = NULL;
} else {
tv.tv_sec = (long) (timer / 1000);
tv.tv_usec = (long) ((timer % 1000) * 1000);
tp = &tv;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select timer: %M", timer);
work_read_fd_set = master_read_fd_set;
work_write_fd_set = master_write_fd_set;
if (max_read || max_write) {
ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);
} else {
/*
* Winsock select() requires that at least one descriptor set must be
* be non-null, and any non-null descriptor set must contain at least
* one handle to a socket. Otherwise select() returns WSAEINVAL.
*/
ngx_msleep(timer);
ready = 0;
}
err = (ready == -1) ? ngx_socket_errno : 0;
if (flags & NGX_UPDATE_TIME) {
ngx_time_update();
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select ready %d", ready);
if (err) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed");
if (err == WSAENOTSOCK) {
ngx_select_repair_fd_sets(cycle);
}
return NGX_ERROR;
}
if (ready == 0) {
if (timer != NGX_TIMER_INFINITE) {
return NGX_OK;
}
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"select() returned no events without timeout");
return NGX_ERROR;
}
nready = 0;
for (i = 0; i < nevents; i++) {
ev = event_index[i];
c = ev->data;
found = 0;
if (ev->write) {
if (FD_ISSET(c->fd, &work_write_fd_set)) {
found = 1;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select write %d", c->fd);
}
} else {
if (FD_ISSET(c->fd, &work_read_fd_set)) {
found = 1;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"select read %d", c->fd);
}
}
if (found) {
ev->ready = 1;
queue = ev->accept ? &ngx_posted_accept_events
: &ngx_posted_events;
ngx_post_event(ev, queue);
nready++;
}
}
if (ready != nready) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"select ready != events: %d:%d", ready, nready);
ngx_select_repair_fd_sets(cycle);
}
return NGX_OK;
}
static void
ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
{
int n;
u_int i;
socklen_t len;
ngx_err_t err;
ngx_socket_t s;
for (i = 0; i < master_read_fd_set.fd_count; i++) {
s = master_read_fd_set.fd_array[i];
len = sizeof(int);
if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
err = ngx_socket_errno;
ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
"invalid descriptor #%d in read fd_set", s);
FD_CLR(s, &master_read_fd_set);
}
}
for (i = 0; i < master_write_fd_set.fd_count; i++) {
s = master_write_fd_set.fd_array[i];
len = sizeof(int);
if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
err = ngx_socket_errno;
ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
"invalid descriptor #%d in write fd_set", s);
FD_CLR(s, &master_write_fd_set);
}
}
}
static char *
ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_event_conf_t *ecf;
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
if (ecf->use != ngx_select_module.ctx_index) {
return NGX_CONF_OK;
}
return NGX_CONF_OK;
}
+1291
View File
@@ -0,0 +1,1291 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#define DEFAULT_CONNECTIONS 512
extern ngx_module_t ngx_kqueue_module;
extern ngx_module_t ngx_eventport_module;
extern ngx_module_t ngx_devpoll_module;
extern ngx_module_t ngx_epoll_module;
extern ngx_module_t ngx_select_module;
static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf);
static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle);
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle);
static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static void *ngx_event_core_create_conf(ngx_cycle_t *cycle);
static char *ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf);
static ngx_uint_t ngx_timer_resolution;
sig_atomic_t ngx_event_timer_alarm;
static ngx_uint_t ngx_event_max_module;
ngx_uint_t ngx_event_flags;
ngx_event_actions_t ngx_event_actions;
static ngx_atomic_t connection_counter = 1;
ngx_atomic_t *ngx_connection_counter = &connection_counter;
ngx_atomic_t *ngx_accept_mutex_ptr;
ngx_shmtx_t ngx_accept_mutex;
ngx_uint_t ngx_use_accept_mutex;
ngx_uint_t ngx_accept_events;
ngx_uint_t ngx_accept_mutex_held;
ngx_msec_t ngx_accept_mutex_delay;
ngx_int_t ngx_accept_disabled;
#if (NGX_STAT_STUB)
static ngx_atomic_t ngx_stat_accepted0;
ngx_atomic_t *ngx_stat_accepted = &ngx_stat_accepted0;
static ngx_atomic_t ngx_stat_handled0;
ngx_atomic_t *ngx_stat_handled = &ngx_stat_handled0;
static ngx_atomic_t ngx_stat_requests0;
ngx_atomic_t *ngx_stat_requests = &ngx_stat_requests0;
static ngx_atomic_t ngx_stat_active0;
ngx_atomic_t *ngx_stat_active = &ngx_stat_active0;
static ngx_atomic_t ngx_stat_reading0;
ngx_atomic_t *ngx_stat_reading = &ngx_stat_reading0;
static ngx_atomic_t ngx_stat_writing0;
ngx_atomic_t *ngx_stat_writing = &ngx_stat_writing0;
static ngx_atomic_t ngx_stat_waiting0;
ngx_atomic_t *ngx_stat_waiting = &ngx_stat_waiting0;
#endif
static ngx_command_t ngx_events_commands[] = {
{ ngx_string("events"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_events_block,
0,
0,
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_events_module_ctx = {
ngx_string("events"),
NULL,
ngx_event_init_conf
};
ngx_module_t ngx_events_module = {
NGX_MODULE_V1,
&ngx_events_module_ctx, /* module context */
ngx_events_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static ngx_str_t event_core_name = ngx_string("event_core");
static ngx_command_t ngx_event_core_commands[] = {
{ ngx_string("worker_connections"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_connections,
0,
0,
NULL },
{ ngx_string("use"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_use,
0,
0,
NULL },
{ ngx_string("multi_accept"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, multi_accept),
NULL },
{ ngx_string("accept_mutex"),
NGX_EVENT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex),
NULL },
{ ngx_string("accept_mutex_delay"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
0,
offsetof(ngx_event_conf_t, accept_mutex_delay),
NULL },
{ ngx_string("debug_connection"),
NGX_EVENT_CONF|NGX_CONF_TAKE1,
ngx_event_debug_connection,
0,
0,
NULL },
ngx_null_command
};
static ngx_event_module_t ngx_event_core_module_ctx = {
&event_core_name,
ngx_event_core_create_conf, /* create configuration */
ngx_event_core_init_conf, /* init configuration */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
ngx_module_t ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
ngx_event_module_init, /* init module */
ngx_event_process_init, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta;
if (ngx_timer_resolution) {
timer = NGX_TIMER_INFINITE;
flags = 0;
} else {
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME;
#if (NGX_WIN32)
/* handle signals from master in case of network inactivity */
if (timer == NGX_TIMER_INFINITE || timer > 500) {
timer = 500;
}
#endif
}
if (ngx_use_accept_mutex) {
if (ngx_accept_disabled > 0) {
ngx_accept_disabled--;
} else {
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
}
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
} else {
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
}
delta = ngx_current_msec;
(void) ngx_process_events(cycle, timer, flags);
delta = ngx_current_msec - delta;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"timer delta: %M", delta);
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
}
if (delta) {
ngx_event_expire_timers();
}
ngx_event_process_posted(cycle, &ngx_posted_events);
}
ngx_int_t
ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
{
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
/* kqueue, epoll */
if (!rev->active && !rev->ready) {
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
}
return NGX_OK;
} else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
/* select, poll, /dev/poll */
if (!rev->active && !rev->ready) {
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
return NGX_OK;
}
if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {
if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)
== NGX_ERROR)
{
return NGX_ERROR;
}
return NGX_OK;
}
} else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
/* event ports */
if (!rev->active && !rev->ready) {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
return NGX_OK;
}
if (rev->oneshot && !rev->ready) {
if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
return NGX_OK;
}
}
/* iocp */
return NGX_OK;
}
ngx_int_t
ngx_handle_write_event(ngx_event_t *wev, size_t lowat)
{
ngx_connection_t *c;
if (lowat) {
c = wev->data;
if (ngx_send_lowat(c, lowat) == NGX_ERROR) {
return NGX_ERROR;
}
}
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
/* kqueue, epoll */
if (!wev->active && !wev->ready) {
if (ngx_add_event(wev, NGX_WRITE_EVENT,
NGX_CLEAR_EVENT | (lowat ? NGX_LOWAT_EVENT : 0))
== NGX_ERROR)
{
return NGX_ERROR;
}
}
return NGX_OK;
} else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
/* select, poll, /dev/poll */
if (!wev->active && !wev->ready) {
if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
return NGX_OK;
}
if (wev->active && wev->ready) {
if (ngx_del_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
return NGX_OK;
}
} else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
/* event ports */
if (!wev->active && !wev->ready) {
if (ngx_add_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
return NGX_OK;
}
if (wev->oneshot && wev->ready) {
if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
return NGX_OK;
}
}
/* iocp */
return NGX_OK;
}
static char *
ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
{
if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
"no \"events\" section in configuration");
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_event_module_init(ngx_cycle_t *cycle)
{
void ***cf;
u_char *shared;
size_t size, cl;
ngx_shm_t shm;
ngx_time_t *tp;
ngx_core_conf_t *ccf;
ngx_event_conf_t *ecf;
cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
ecf = (*cf)[ngx_event_core_module.ctx_index];
if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
"using the \"%s\" event method", ecf->name);
}
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ngx_timer_resolution = ccf->timer_resolution;
#if !(NGX_WIN32)
{
ngx_int_t limit;
struct rlimit rlmt;
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"getrlimit(RLIMIT_NOFILE) failed, ignored");
} else {
if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
&& (ccf->rlimit_nofile == NGX_CONF_UNSET
|| ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))
{
limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
(ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"%ui worker_connections exceed "
"open file resource limit: %i",
ecf->connections, limit);
}
}
}
#endif /* !(NGX_WIN32) */
if (ccf->master == 0) {
return NGX_OK;
}
if (ngx_accept_mutex_ptr) {
return NGX_OK;
}
/* cl should be equal to or greater than cache line size */
cl = 128;
size = cl /* ngx_accept_mutex */
+ cl /* ngx_connection_counter */
+ cl; /* ngx_temp_number */
#if (NGX_STAT_STUB)
size += cl /* ngx_stat_accepted */
+ cl /* ngx_stat_handled */
+ cl /* ngx_stat_requests */
+ cl /* ngx_stat_active */
+ cl /* ngx_stat_reading */
+ cl /* ngx_stat_writing */
+ cl; /* ngx_stat_waiting */
#endif
shm.size = size;
ngx_str_set(&shm.name, "nginx_shared_zone");
shm.log = cycle->log;
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
}
shared = shm.addr;
ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
ngx_accept_mutex.spin = (ngx_uint_t) -1;
if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,
cycle->lock_file.data)
!= NGX_OK)
{
return NGX_ERROR;
}
ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);
(void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"counter: %p, %uA",
ngx_connection_counter, *ngx_connection_counter);
ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);
tp = ngx_timeofday();
ngx_random_number = (tp->msec << 16) + ngx_pid;
#if (NGX_STAT_STUB)
ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
#endif
return NGX_OK;
}
#if !(NGX_WIN32)
static void
ngx_timer_signal_handler(int signo)
{
ngx_event_timer_alarm = 1;
#if 1
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer signal");
#endif
}
#endif
static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
ngx_uint_t m, i;
ngx_event_t *rev, *wev;
ngx_listening_t *ls;
ngx_connection_t *c, *next, *old;
ngx_core_conf_t *ccf;
ngx_event_conf_t *ecf;
ngx_event_module_t *module;
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
ngx_use_accept_mutex = 1;
ngx_accept_mutex_held = 0;
ngx_accept_mutex_delay = ecf->accept_mutex_delay;
} else {
ngx_use_accept_mutex = 0;
}
#if (NGX_WIN32)
/*
* disable accept mutex on win32 as it may cause deadlock if
* grabbed by a process which can't accept connections
*/
ngx_use_accept_mutex = 0;
#endif
ngx_queue_init(&ngx_posted_accept_events);
ngx_queue_init(&ngx_posted_events);
if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
return NGX_ERROR;
}
for (m = 0; cycle->modules[m]; m++) {
if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
if (cycle->modules[m]->ctx_index != ecf->use) {
continue;
}
module = cycle->modules[m]->ctx;
if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
/* fatal */
exit(2);
}
break;
}
#if !(NGX_WIN32)
if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
struct sigaction sa;
struct itimerval itv;
ngx_memzero(&sa, sizeof(struct sigaction));
sa.sa_handler = ngx_timer_signal_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGALRM, &sa, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigaction(SIGALRM) failed");
return NGX_ERROR;
}
itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
itv.it_value.tv_sec = ngx_timer_resolution / 1000;
itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;
if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setitimer() failed");
}
}
if (ngx_event_flags & NGX_USE_FD_EVENT) {
struct rlimit rlmt;
if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"getrlimit(RLIMIT_NOFILE) failed");
return NGX_ERROR;
}
cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;
cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,
cycle->log);
if (cycle->files == NULL) {
return NGX_ERROR;
}
}
#else
if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"the \"timer_resolution\" directive is not supported "
"with the configured event method, ignored");
ngx_timer_resolution = 0;
}
#endif
cycle->connections =
ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
if (cycle->connections == NULL) {
return NGX_ERROR;
}
c = cycle->connections;
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->read_events == NULL) {
return NGX_ERROR;
}
rev = cycle->read_events;
for (i = 0; i < cycle->connection_n; i++) {
rev[i].closed = 1;
rev[i].instance = 1;
}
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->write_events == NULL) {
return NGX_ERROR;
}
wev = cycle->write_events;
for (i = 0; i < cycle->connection_n; i++) {
wev[i].closed = 1;
}
i = cycle->connection_n;
next = NULL;
do {
i--;
c[i].data = next;
c[i].read = &cycle->read_events[i];
c[i].write = &cycle->write_events[i];
c[i].fd = (ngx_socket_t) -1;
next = &c[i];
} while (i);
cycle->free_connections = next;
cycle->free_connection_n = cycle->connection_n;
/* for each listening socket */
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
#if (NGX_HAVE_REUSEPORT)
if (ls[i].reuseport && ls[i].worker != ngx_worker) {
continue;
}
#endif
c = ngx_get_connection(ls[i].fd, cycle->log);
if (c == NULL) {
return NGX_ERROR;
}
c->type = ls[i].type;
c->log = &ls[i].log;
c->listening = &ls[i];
ls[i].connection = c;
rev = c->read;
rev->log = c->log;
rev->accept = 1;
#if (NGX_HAVE_DEFERRED_ACCEPT)
rev->deferred_accept = ls[i].deferred_accept;
#endif
if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
if (ls[i].previous) {
/*
* delete the old accept events that were bound to
* the old cycle read events array
*/
old = ls[i].previous->connection;
if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
old->fd = (ngx_socket_t) -1;
}
}
#if (NGX_WIN32)
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
ngx_iocp_conf_t *iocpcf;
rev->handler = ngx_event_acceptex;
if (ngx_use_accept_mutex) {
continue;
}
if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
return NGX_ERROR;
}
ls[i].log.handler = ngx_acceptex_log_error;
iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
== NGX_ERROR)
{
return NGX_ERROR;
}
} else {
rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
: ngx_event_recvmsg;
if (ngx_use_accept_mutex) {
continue;
}
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
}
#else
rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
: ngx_event_recvmsg;
#if (NGX_HAVE_REUSEPORT)
if (ls[i].reuseport) {
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
continue;
}
#endif
if (ngx_use_accept_mutex) {
continue;
}
#if (NGX_HAVE_EPOLLEXCLUSIVE)
if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
&& ccf->worker_processes > 1)
{
if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
continue;
}
#endif
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
#endif
}
return NGX_OK;
}
ngx_int_t
ngx_send_lowat(ngx_connection_t *c, size_t lowat)
{
int sndlowat;
#if (NGX_HAVE_LOWAT_EVENT)
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
c->write->available = lowat;
return NGX_OK;
}
#endif
if (lowat == 0 || c->sndlowat) {
return NGX_OK;
}
sndlowat = (int) lowat;
if (setsockopt(c->fd, SOL_SOCKET, SO_SNDLOWAT,
(const void *) &sndlowat, sizeof(int))
== -1)
{
ngx_connection_error(c, ngx_socket_errno,
"setsockopt(SO_SNDLOWAT) failed");
return NGX_ERROR;
}
c->sndlowat = 1;
return NGX_OK;
}
static char *
ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
void ***ctx;
ngx_uint_t i;
ngx_conf_t pcf;
ngx_event_module_t *m;
if (*(void **) conf) {
return "is duplicate";
}
/* count the number of the event modules and set up their indices */
ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);
ctx = ngx_pcalloc(cf->pool, sizeof(void *));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
*ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
if (*ctx == NULL) {
return NGX_CONF_ERROR;
}
*(void **) conf = ctx;
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = cf->cycle->modules[i]->ctx;
if (m->create_conf) {
(*ctx)[cf->cycle->modules[i]->ctx_index] =
m->create_conf(cf->cycle);
if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
}
pcf = *cf;
cf->ctx = ctx;
cf->module_type = NGX_EVENT_MODULE;
cf->cmd_type = NGX_EVENT_CONF;
rv = ngx_conf_parse(cf, NULL);
*cf = pcf;
if (rv != NGX_CONF_OK) {
return rv;
}
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
m = cf->cycle->modules[i]->ctx;
if (m->init_conf) {
rv = m->init_conf(cf->cycle,
(*ctx)[cf->cycle->modules[i]->ctx_index]);
if (rv != NGX_CONF_OK) {
return rv;
}
}
}
return NGX_CONF_OK;
}
static char *
ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_event_conf_t *ecf = conf;
ngx_str_t *value;
if (ecf->connections != NGX_CONF_UNSET_UINT) {
return "is duplicate";
}
value = cf->args->elts;
ecf->connections = ngx_atoi(value[1].data, value[1].len);
if (ecf->connections == (ngx_uint_t) NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid number \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
cf->cycle->connection_n = ecf->connections;
return NGX_CONF_OK;
}
static char *
ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_event_conf_t *ecf = conf;
ngx_int_t m;
ngx_str_t *value;
ngx_event_conf_t *old_ecf;
ngx_event_module_t *module;
if (ecf->use != NGX_CONF_UNSET_UINT) {
return "is duplicate";
}
value = cf->args->elts;
if (cf->cycle->old_cycle->conf_ctx) {
old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx,
ngx_event_core_module);
} else {
old_ecf = NULL;
}
for (m = 0; cf->cycle->modules[m]; m++) {
if (cf->cycle->modules[m]->type != NGX_EVENT_MODULE) {
continue;
}
module = cf->cycle->modules[m]->ctx;
if (module->name->len == value[1].len) {
if (ngx_strcmp(module->name->data, value[1].data) == 0) {
ecf->use = cf->cycle->modules[m]->ctx_index;
ecf->name = module->name->data;
if (ngx_process == NGX_PROCESS_SINGLE
&& old_ecf
&& old_ecf->use != ecf->use)
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"when the server runs without a master process "
"the \"%V\" event type must be the same as "
"in previous configuration - \"%s\" "
"and it cannot be changed on the fly, "
"to change it you need to stop server "
"and start it again",
&value[1], old_ecf->name);
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
}
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid event type \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
static char *
ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
#if (NGX_DEBUG)
ngx_event_conf_t *ecf = conf;
ngx_int_t rc;
ngx_str_t *value;
ngx_url_t u;
ngx_cidr_t c, *cidr;
ngx_uint_t i;
struct sockaddr_in *sin;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
#endif
value = cf->args->elts;
#if (NGX_HAVE_UNIX_DOMAIN)
if (ngx_strcmp(value[1].data, "unix:") == 0) {
cidr = ngx_array_push(&ecf->debug_connection);
if (cidr == NULL) {
return NGX_CONF_ERROR;
}
cidr->family = AF_UNIX;
return NGX_CONF_OK;
}
#endif
rc = ngx_ptocidr(&value[1], &c);
if (rc != NGX_ERROR) {
if (rc == NGX_DONE) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"low address bits of %V are meaningless",
&value[1]);
}
cidr = ngx_array_push(&ecf->debug_connection);
if (cidr == NULL) {
return NGX_CONF_ERROR;
}
*cidr = c;
return NGX_CONF_OK;
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.host = value[1];
if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
if (u.err) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"%s in debug_connection \"%V\"",
u.err, &u.host);
}
return NGX_CONF_ERROR;
}
cidr = ngx_array_push_n(&ecf->debug_connection, u.naddrs);
if (cidr == NULL) {
return NGX_CONF_ERROR;
}
ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));
for (i = 0; i < u.naddrs; i++) {
cidr[i].family = u.addrs[i].sockaddr->sa_family;
switch (cidr[i].family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
cidr[i].u.in6.addr = sin6->sin6_addr;
ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
cidr[i].u.in.addr = sin->sin_addr.s_addr;
cidr[i].u.in.mask = 0xffffffff;
break;
}
}
#else
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"\"debug_connection\" is ignored, you need to rebuild "
"nginx using --with-debug option to enable it");
#endif
return NGX_CONF_OK;
}
static void *
ngx_event_core_create_conf(ngx_cycle_t *cycle)
{
ngx_event_conf_t *ecf;
ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
if (ecf == NULL) {
return NULL;
}
ecf->connections = NGX_CONF_UNSET_UINT;
ecf->use = NGX_CONF_UNSET_UINT;
ecf->multi_accept = NGX_CONF_UNSET;
ecf->accept_mutex = NGX_CONF_UNSET;
ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
ecf->name = (void *) NGX_CONF_UNSET;
#if (NGX_DEBUG)
if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
sizeof(ngx_cidr_t)) == NGX_ERROR)
{
return NULL;
}
#endif
return ecf;
}
static char *
ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_event_conf_t *ecf = conf;
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
int fd;
#endif
ngx_int_t i;
ngx_module_t *module;
ngx_event_module_t *event_module;
module = NULL;
#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
fd = epoll_create(100);
if (fd != -1) {
(void) close(fd);
module = &ngx_epoll_module;
} else if (ngx_errno != NGX_ENOSYS) {
module = &ngx_epoll_module;
}
#endif
#if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL)
module = &ngx_devpoll_module;
#endif
#if (NGX_HAVE_KQUEUE)
module = &ngx_kqueue_module;
#endif
#if (NGX_HAVE_SELECT)
if (module == NULL) {
module = &ngx_select_module;
}
#endif
if (module == NULL) {
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_EVENT_MODULE) {
continue;
}
event_module = cycle->modules[i]->ctx;
if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)
{
continue;
}
module = cycle->modules[i];
break;
}
}
if (module == NULL) {
ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
return NGX_CONF_ERROR;
}
ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
cycle->connection_n = ecf->connections;
ngx_conf_init_uint_value(ecf->use, module->ctx_index);
event_module = module->ctx;
ngx_conf_init_ptr_value(ecf->name, event_module->name->data);
ngx_conf_init_value(ecf->multi_accept, 0);
ngx_conf_init_value(ecf->accept_mutex, 0);
ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);
return NGX_CONF_OK;
}
+539
View File
@@ -0,0 +1,539 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_H_INCLUDED_
#define _NGX_EVENT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#define NGX_INVALID_INDEX 0xd0d0d0d0
#if (NGX_HAVE_IOCP)
typedef struct {
WSAOVERLAPPED ovlp;
ngx_event_t *event;
int error;
} ngx_event_ovlp_t;
#endif
struct ngx_event_s {
void *data;
unsigned write:1;
unsigned accept:1;
/* used to detect the stale events in kqueue and epoll */
unsigned instance:1;
/*
* the event was passed or would be passed to a kernel;
* in aio mode - operation was posted.
*/
unsigned active:1;
unsigned disabled:1;
/* the ready event; in aio mode 0 means that no operation can be posted */
unsigned ready:1;
unsigned oneshot:1;
/* aio operation is complete */
unsigned complete:1;
unsigned eof:1;
unsigned error:1;
unsigned timedout:1;
unsigned timer_set:1;
unsigned delayed:1;
unsigned deferred_accept:1;
/* the pending eof reported by kqueue, epoll or in aio chain operation */
unsigned pending_eof:1;
unsigned posted:1;
unsigned closed:1;
/* to test on worker exit */
unsigned channel:1;
unsigned resolver:1;
unsigned cancelable:1;
#if (NGX_HAVE_KQUEUE)
unsigned kq_vnode:1;
/* the pending errno reported by kqueue */
int kq_errno;
#endif
/*
* kqueue only:
* accept: number of sockets that wait to be accepted
* read: bytes to read when event is ready
* or lowat when event is set with NGX_LOWAT_EVENT flag
* write: available space in buffer when event is ready
* or lowat when event is set with NGX_LOWAT_EVENT flag
*
* epoll with EPOLLRDHUP:
* accept: 1 if accept many, 0 otherwise
* read: 1 if there can be data to read, 0 otherwise
*
* iocp: TODO
*
* otherwise:
* accept: 1 if accept many, 0 otherwise
*/
#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)
int available;
#else
unsigned available:1;
#endif
ngx_event_handler_pt handler;
#if (NGX_HAVE_IOCP)
ngx_event_ovlp_t ovlp;
#endif
ngx_uint_t index;
ngx_log_t *log;
ngx_rbtree_node_t timer;
/* the posted queue */
ngx_queue_t queue;
#if 0
/* the threads support */
/*
* the event thread context, we store it here
* if $(CC) does not understand __thread declaration
* and pthread_getspecific() is too costly
*/
void *thr_ctx;
#if (NGX_EVENT_T_PADDING)
/* event should not cross cache line in SMP */
uint32_t padding[NGX_EVENT_T_PADDING];
#endif
#endif
};
#if (NGX_HAVE_FILE_AIO)
struct ngx_event_aio_s {
void *data;
ngx_event_handler_pt handler;
ngx_file_t *file;
ngx_fd_t fd;
#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
ssize_t (*preload_handler)(ngx_buf_t *file);
#endif
#if (NGX_HAVE_EVENTFD)
int64_t res;
#endif
#if !(NGX_HAVE_EVENTFD) || (NGX_TEST_BUILD_EPOLL)
ngx_err_t err;
size_t nbytes;
#endif
ngx_aiocb_t aiocb;
ngx_event_t event;
};
#endif
typedef struct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*add_conn)(ngx_connection_t *c);
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
ngx_int_t (*notify)(ngx_event_handler_pt handler);
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
extern ngx_event_actions_t ngx_event_actions;
#if (NGX_HAVE_EPOLLRDHUP)
extern ngx_uint_t ngx_use_epoll_rdhup;
#endif
/*
* The event filter requires to read/write the whole data:
* select, poll, /dev/poll, kqueue, epoll.
*/
#define NGX_USE_LEVEL_EVENT 0x00000001
/*
* The event filter is deleted after a notification without an additional
* syscall: kqueue, epoll.
*/
#define NGX_USE_ONESHOT_EVENT 0x00000002
/*
* The event filter notifies only the changes and an initial level:
* kqueue, epoll.
*/
#define NGX_USE_CLEAR_EVENT 0x00000004
/*
* The event filter has kqueue features: the eof flag, errno,
* available data, etc.
*/
#define NGX_USE_KQUEUE_EVENT 0x00000008
/*
* The event filter supports low water mark: kqueue's NOTE_LOWAT.
* kqueue in FreeBSD 4.1-4.2 has no NOTE_LOWAT so we need a separate flag.
*/
#define NGX_USE_LOWAT_EVENT 0x00000010
/*
* The event filter requires to do i/o operation until EAGAIN: epoll.
*/
#define NGX_USE_GREEDY_EVENT 0x00000020
/*
* The event filter is epoll.
*/
#define NGX_USE_EPOLL_EVENT 0x00000040
/*
* Obsolete.
*/
#define NGX_USE_RTSIG_EVENT 0x00000080
/*
* Obsolete.
*/
#define NGX_USE_AIO_EVENT 0x00000100
/*
* Need to add socket or handle only once: i/o completion port.
*/
#define NGX_USE_IOCP_EVENT 0x00000200
/*
* The event filter has no opaque data and requires file descriptors table:
* poll, /dev/poll.
*/
#define NGX_USE_FD_EVENT 0x00000400
/*
* The event module handles periodic or absolute timer event by itself:
* kqueue in FreeBSD 4.4, NetBSD 2.0, and MacOSX 10.4, Solaris 10's event ports.
*/
#define NGX_USE_TIMER_EVENT 0x00000800
/*
* All event filters on file descriptor are deleted after a notification:
* Solaris 10's event ports.
*/
#define NGX_USE_EVENTPORT_EVENT 0x00001000
/*
* The event filter support vnode notifications: kqueue.
*/
#define NGX_USE_VNODE_EVENT 0x00002000
/*
* The event filter is deleted just before the closing file.
* Has no meaning for select and poll.
* kqueue, epoll, eventport: allows to avoid explicit delete,
* because filter automatically is deleted
* on file close,
*
* /dev/poll: we need to flush POLLREMOVE event
* before closing file.
*/
#define NGX_CLOSE_EVENT 1
/*
* disable temporarily event filter, this may avoid locks
* in kernel malloc()/free(): kqueue.
*/
#define NGX_DISABLE_EVENT 2
/*
* event must be passed to kernel right now, do not wait until batch processing.
*/
#define NGX_FLUSH_EVENT 4
/* these flags have a meaning only for kqueue */
#define NGX_LOWAT_EVENT 0
#define NGX_VNODE_EVENT 0
#if (NGX_HAVE_EPOLL) && !(NGX_HAVE_EPOLLRDHUP)
#define EPOLLRDHUP 0
#endif
#if (NGX_HAVE_KQUEUE)
#define NGX_READ_EVENT EVFILT_READ
#define NGX_WRITE_EVENT EVFILT_WRITE
#undef NGX_VNODE_EVENT
#define NGX_VNODE_EVENT EVFILT_VNODE
/*
* NGX_CLOSE_EVENT, NGX_LOWAT_EVENT, and NGX_FLUSH_EVENT are the module flags
* and they must not go into a kernel so we need to choose the value
* that must not interfere with any existent and future kqueue flags.
* kqueue has such values - EV_FLAG1, EV_EOF, and EV_ERROR:
* they are reserved and cleared on a kernel entrance.
*/
#undef NGX_CLOSE_EVENT
#define NGX_CLOSE_EVENT EV_EOF
#undef NGX_LOWAT_EVENT
#define NGX_LOWAT_EVENT EV_FLAG1
#undef NGX_FLUSH_EVENT
#define NGX_FLUSH_EVENT EV_ERROR
#define NGX_LEVEL_EVENT 0
#define NGX_ONESHOT_EVENT EV_ONESHOT
#define NGX_CLEAR_EVENT EV_CLEAR
#undef NGX_DISABLE_EVENT
#define NGX_DISABLE_EVENT EV_DISABLE
#elif (NGX_HAVE_DEVPOLL && !(NGX_TEST_BUILD_DEVPOLL)) \
|| (NGX_HAVE_EVENTPORT && !(NGX_TEST_BUILD_EVENTPORT))
#define NGX_READ_EVENT POLLIN
#define NGX_WRITE_EVENT POLLOUT
#define NGX_LEVEL_EVENT 0
#define NGX_ONESHOT_EVENT 1
#elif (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
#define NGX_READ_EVENT (EPOLLIN|EPOLLRDHUP)
#define NGX_WRITE_EVENT EPOLLOUT
#define NGX_LEVEL_EVENT 0
#define NGX_CLEAR_EVENT EPOLLET
#define NGX_ONESHOT_EVENT 0x70000000
#if 0
#define NGX_ONESHOT_EVENT EPOLLONESHOT
#endif
#if (NGX_HAVE_EPOLLEXCLUSIVE)
#define NGX_EXCLUSIVE_EVENT EPOLLEXCLUSIVE
#endif
#elif (NGX_HAVE_POLL)
#define NGX_READ_EVENT POLLIN
#define NGX_WRITE_EVENT POLLOUT
#define NGX_LEVEL_EVENT 0
#define NGX_ONESHOT_EVENT 1
#else /* select */
#define NGX_READ_EVENT 0
#define NGX_WRITE_EVENT 1
#define NGX_LEVEL_EVENT 0
#define NGX_ONESHOT_EVENT 1
#endif /* NGX_HAVE_KQUEUE */
#if (NGX_HAVE_IOCP)
#define NGX_IOCP_ACCEPT 0
#define NGX_IOCP_IO 1
#define NGX_IOCP_CONNECT 2
#endif
#if (NGX_TEST_BUILD_EPOLL)
#define NGX_EXCLUSIVE_EVENT 0
#endif
#ifndef NGX_CLEAR_EVENT
#define NGX_CLEAR_EVENT 0 /* dummy declaration */
#endif
#define ngx_process_events ngx_event_actions.process_events
#define ngx_done_events ngx_event_actions.done
#define ngx_add_event ngx_event_actions.add
#define ngx_del_event ngx_event_actions.del
#define ngx_add_conn ngx_event_actions.add_conn
#define ngx_del_conn ngx_event_actions.del_conn
#define ngx_notify ngx_event_actions.notify
#define ngx_add_timer ngx_event_add_timer
#define ngx_del_timer ngx_event_del_timer
extern ngx_os_io_t ngx_io;
#define ngx_recv ngx_io.recv
#define ngx_recv_chain ngx_io.recv_chain
#define ngx_udp_recv ngx_io.udp_recv
#define ngx_send ngx_io.send
#define ngx_send_chain ngx_io.send_chain
#define ngx_udp_send ngx_io.udp_send
#define ngx_udp_send_chain ngx_io.udp_send_chain
#define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */
#define NGX_EVENT_CONF 0x02000000
typedef struct {
ngx_uint_t connections;
ngx_uint_t use;
ngx_flag_t multi_accept;
ngx_flag_t accept_mutex;
ngx_msec_t accept_mutex_delay;
u_char *name;
#if (NGX_DEBUG)
ngx_array_t debug_connection;
#endif
} ngx_event_conf_t;
typedef struct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;
extern ngx_atomic_t *ngx_connection_counter;
extern ngx_atomic_t *ngx_accept_mutex_ptr;
extern ngx_shmtx_t ngx_accept_mutex;
extern ngx_uint_t ngx_use_accept_mutex;
extern ngx_uint_t ngx_accept_events;
extern ngx_uint_t ngx_accept_mutex_held;
extern ngx_msec_t ngx_accept_mutex_delay;
extern ngx_int_t ngx_accept_disabled;
#if (NGX_STAT_STUB)
extern ngx_atomic_t *ngx_stat_accepted;
extern ngx_atomic_t *ngx_stat_handled;
extern ngx_atomic_t *ngx_stat_requests;
extern ngx_atomic_t *ngx_stat_active;
extern ngx_atomic_t *ngx_stat_reading;
extern ngx_atomic_t *ngx_stat_writing;
extern ngx_atomic_t *ngx_stat_waiting;
#endif
#define NGX_UPDATE_TIME 1
#define NGX_POST_EVENTS 2
extern sig_atomic_t ngx_event_timer_alarm;
extern ngx_uint_t ngx_event_flags;
extern ngx_module_t ngx_events_module;
extern ngx_module_t ngx_event_core_module;
#define ngx_event_get_conf(conf_ctx, module) \
(*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index];
void ngx_event_accept(ngx_event_t *ev);
void ngx_event_recvmsg(ngx_event_t *ev);
ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle);
u_char *ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len);
void ngx_process_events_and_timers(ngx_cycle_t *cycle);
ngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags);
ngx_int_t ngx_handle_write_event(ngx_event_t *wev, size_t lowat);
#if (NGX_WIN32)
void ngx_event_acceptex(ngx_event_t *ev);
ngx_int_t ngx_event_post_acceptex(ngx_listening_t *ls, ngx_uint_t n);
u_char *ngx_acceptex_log_error(ngx_log_t *log, u_char *buf, size_t len);
#endif
ngx_int_t ngx_send_lowat(ngx_connection_t *c, size_t lowat);
/* used in ngx_log_debugX() */
#define ngx_event_ident(p) ((ngx_connection_t *) (p))->fd
#include <ngx_event_timer.h>
#include <ngx_event_posted.h>
#if (NGX_WIN32)
#include <ngx_iocp_module.h>
#endif
#endif /* _NGX_EVENT_H_INCLUDED_ */
+1029
View File
@@ -0,0 +1,1029 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all);
static void ngx_close_accepted_connection(ngx_connection_t *c);
#if (NGX_DEBUG)
static void ngx_debug_accepted_connection(ngx_event_conf_t *ecf,
ngx_connection_t *c);
#endif
void
ngx_event_accept(ngx_event_t *ev)
{
socklen_t socklen;
ngx_err_t err;
ngx_log_t *log;
ngx_uint_t level;
ngx_socket_t s;
ngx_event_t *rev, *wev;
ngx_sockaddr_t sa;
ngx_listening_t *ls;
ngx_connection_t *c, *lc;
ngx_event_conf_t *ecf;
#if (NGX_HAVE_ACCEPT4)
static ngx_uint_t use_accept4 = 1;
#endif
if (ev->timedout) {
if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
return;
}
ev->timedout = 0;
}
ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
ev->available = ecf->multi_accept;
}
lc = ev->data;
ls = lc->listening;
ev->ready = 0;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"accept on %V, ready: %d", &ls->addr_text, ev->available);
do {
socklen = sizeof(ngx_sockaddr_t);
#if (NGX_HAVE_ACCEPT4)
if (use_accept4) {
s = accept4(lc->fd, &sa.sockaddr, &socklen, SOCK_NONBLOCK);
} else {
s = accept(lc->fd, &sa.sockaddr, &socklen);
}
#else
s = accept(lc->fd, &sa.sockaddr, &socklen);
#endif
if (s == (ngx_socket_t) -1) {
err = ngx_socket_errno;
if (err == NGX_EAGAIN) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
"accept() not ready");
return;
}
level = NGX_LOG_ALERT;
if (err == NGX_ECONNABORTED) {
level = NGX_LOG_ERR;
} else if (err == NGX_EMFILE || err == NGX_ENFILE) {
level = NGX_LOG_CRIT;
}
#if (NGX_HAVE_ACCEPT4)
ngx_log_error(level, ev->log, err,
use_accept4 ? "accept4() failed" : "accept() failed");
if (use_accept4 && err == NGX_ENOSYS) {
use_accept4 = 0;
ngx_inherited_nonblocking = 0;
continue;
}
#else
ngx_log_error(level, ev->log, err, "accept() failed");
#endif
if (err == NGX_ECONNABORTED) {
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
ev->available--;
}
if (ev->available) {
continue;
}
}
if (err == NGX_EMFILE || err == NGX_ENFILE) {
if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle, 1)
!= NGX_OK)
{
return;
}
if (ngx_use_accept_mutex) {
if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
ngx_accept_mutex_held = 0;
}
ngx_accept_disabled = 1;
} else {
ngx_add_timer(ev, ecf->accept_mutex_delay);
}
}
return;
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
#endif
ngx_accept_disabled = ngx_cycle->connection_n / 8
- ngx_cycle->free_connection_n;
c = ngx_get_connection(s, ev->log);
if (c == NULL) {
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_close_socket_n " failed");
}
return;
}
c->type = SOCK_STREAM;
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, 1);
#endif
c->pool = ngx_create_pool(ls->pool_size, ev->log);
if (c->pool == NULL) {
ngx_close_accepted_connection(c);
return;
}
if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
socklen = sizeof(ngx_sockaddr_t);
}
c->sockaddr = ngx_palloc(c->pool, socklen);
if (c->sockaddr == NULL) {
ngx_close_accepted_connection(c);
return;
}
ngx_memcpy(c->sockaddr, &sa, socklen);
log = ngx_palloc(c->pool, sizeof(ngx_log_t));
if (log == NULL) {
ngx_close_accepted_connection(c);
return;
}
/* set a blocking mode for iocp and non-blocking mode for others */
if (ngx_inherited_nonblocking) {
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
if (ngx_blocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_blocking_n " failed");
ngx_close_accepted_connection(c);
return;
}
}
} else {
if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx_nonblocking_n " failed");
ngx_close_accepted_connection(c);
return;
}
}
}
*log = ls->log;
c->recv = ngx_recv;
c->send = ngx_send;
c->recv_chain = ngx_recv_chain;
c->send_chain = ngx_send_chain;
c->log = log;
c->pool->log = log;
c->socklen = socklen;
c->listening = ls;
c->local_sockaddr = ls->sockaddr;
c->local_socklen = ls->socklen;
#if (NGX_HAVE_UNIX_DOMAIN)
if (c->sockaddr->sa_family == AF_UNIX) {
c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
#if (NGX_SOLARIS)
/* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
c->sendfile = 0;
#endif
}
#endif
rev = c->read;
wev = c->write;
wev->ready = 1;
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
rev->ready = 1;
}
if (ev->deferred_accept) {
rev->ready = 1;
#if (NGX_HAVE_KQUEUE || NGX_HAVE_EPOLLRDHUP)
rev->available = 1;
#endif
}
rev->log = log;
wev->log = log;
/*
* TODO: MT: - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*
* TODO: MP: - allocated in a shared memory
* - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*/
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
#endif
if (ls->addr_ntop) {
c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
if (c->addr_text.data == NULL) {
ngx_close_accepted_connection(c);
return;
}
c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
c->addr_text.data,
ls->addr_text_max_len, 0);
if (c->addr_text.len == 0) {
ngx_close_accepted_connection(c);
return;
}
}
#if (NGX_DEBUG)
{
ngx_str_t addr;
u_char text[NGX_SOCKADDR_STRLEN];
ngx_debug_accepted_connection(ecf, c);
if (log->log_level & NGX_LOG_DEBUG_EVENT) {
addr.data = text;
addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
NGX_SOCKADDR_STRLEN, 1);
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0,
"*%uA accept: %V fd:%d", c->number, &addr, s);
}
}
#endif
if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
if (ngx_add_conn(c) == NGX_ERROR) {
ngx_close_accepted_connection(c);
return;
}
}
log->data = NULL;
log->handler = NULL;
ls->handler(c);
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
ev->available--;
}
} while (ev->available);
}
#if !(NGX_WIN32)
void
ngx_event_recvmsg(ngx_event_t *ev)
{
ssize_t n;
ngx_log_t *log;
ngx_err_t err;
ngx_event_t *rev, *wev;
struct iovec iov[1];
struct msghdr msg;
ngx_sockaddr_t sa;
ngx_listening_t *ls;
ngx_event_conf_t *ecf;
ngx_connection_t *c, *lc;
static u_char buffer[65535];
#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
#if (NGX_HAVE_IP_RECVDSTADDR)
u_char msg_control[CMSG_SPACE(sizeof(struct in_addr))];
#elif (NGX_HAVE_IP_PKTINFO)
u_char msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#endif
#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
u_char msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#endif
if (ev->timedout) {
if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
return;
}
ev->timedout = 0;
}
ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
ev->available = ecf->multi_accept;
}
lc = ev->data;
ls = lc->listening;
ev->ready = 0;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"recvmsg on %V, ready: %d", &ls->addr_text, ev->available);
do {
ngx_memzero(&msg, sizeof(struct msghdr));
iov[0].iov_base = (void *) buffer;
iov[0].iov_len = sizeof(buffer);
msg.msg_name = &sa;
msg.msg_namelen = sizeof(ngx_sockaddr_t);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
if (ls->wildcard) {
#if (NGX_HAVE_IP_RECVDSTADDR || NGX_HAVE_IP_PKTINFO)
if (ls->sockaddr->sa_family == AF_INET) {
msg.msg_control = &msg_control;
msg.msg_controllen = sizeof(msg_control);
}
#endif
#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
if (ls->sockaddr->sa_family == AF_INET6) {
msg.msg_control = &msg_control6;
msg.msg_controllen = sizeof(msg_control6);
}
#endif
}
#endif
n = recvmsg(lc->fd, &msg, 0);
if (n == -1) {
err = ngx_socket_errno;
if (err == NGX_EAGAIN) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
"recvmsg() not ready");
return;
}
ngx_log_error(NGX_LOG_ALERT, ev->log, err, "recvmsg() failed");
return;
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
#endif
#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
"recvmsg() truncated data");
continue;
}
#endif
ngx_accept_disabled = ngx_cycle->connection_n / 8
- ngx_cycle->free_connection_n;
c = ngx_get_connection(lc->fd, ev->log);
if (c == NULL) {
return;
}
c->shared = 1;
c->type = SOCK_DGRAM;
c->socklen = msg.msg_namelen;
if (c->socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
c->socklen = sizeof(ngx_sockaddr_t);
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, 1);
#endif
c->pool = ngx_create_pool(ls->pool_size, ev->log);
if (c->pool == NULL) {
ngx_close_accepted_connection(c);
return;
}
c->sockaddr = ngx_palloc(c->pool, c->socklen);
if (c->sockaddr == NULL) {
ngx_close_accepted_connection(c);
return;
}
ngx_memcpy(c->sockaddr, msg.msg_name, c->socklen);
log = ngx_palloc(c->pool, sizeof(ngx_log_t));
if (log == NULL) {
ngx_close_accepted_connection(c);
return;
}
*log = ls->log;
c->send = ngx_udp_send;
c->send_chain = ngx_udp_send_chain;
c->log = log;
c->pool->log = log;
c->listening = ls;
c->local_sockaddr = ls->sockaddr;
c->local_socklen = ls->socklen;
#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
if (ls->wildcard) {
struct cmsghdr *cmsg;
struct sockaddr *sockaddr;
sockaddr = ngx_palloc(c->pool, c->local_socklen);
if (sockaddr == NULL) {
ngx_close_accepted_connection(c);
return;
}
ngx_memcpy(sockaddr, c->local_sockaddr, c->local_socklen);
c->local_sockaddr = sockaddr;
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
#if (NGX_HAVE_IP_RECVDSTADDR)
if (cmsg->cmsg_level == IPPROTO_IP
&& cmsg->cmsg_type == IP_RECVDSTADDR
&& sockaddr->sa_family == AF_INET)
{
struct in_addr *addr;
struct sockaddr_in *sin;
addr = (struct in_addr *) CMSG_DATA(cmsg);
sin = (struct sockaddr_in *) sockaddr;
sin->sin_addr = *addr;
break;
}
#elif (NGX_HAVE_IP_PKTINFO)
if (cmsg->cmsg_level == IPPROTO_IP
&& cmsg->cmsg_type == IP_PKTINFO
&& sockaddr->sa_family == AF_INET)
{
struct in_pktinfo *pkt;
struct sockaddr_in *sin;
pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
sin = (struct sockaddr_in *) sockaddr;
sin->sin_addr = pkt->ipi_addr;
break;
}
#endif
#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)
if (cmsg->cmsg_level == IPPROTO_IPV6
&& cmsg->cmsg_type == IPV6_PKTINFO
&& sockaddr->sa_family == AF_INET6)
{
struct in6_pktinfo *pkt6;
struct sockaddr_in6 *sin6;
pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
sin6 = (struct sockaddr_in6 *) sockaddr;
sin6->sin6_addr = pkt6->ipi6_addr;
break;
}
#endif
}
}
#endif
c->buffer = ngx_create_temp_buf(c->pool, n);
if (c->buffer == NULL) {
ngx_close_accepted_connection(c);
return;
}
c->buffer->last = ngx_cpymem(c->buffer->last, buffer, n);
rev = c->read;
wev = c->write;
wev->ready = 1;
rev->log = log;
wev->log = log;
/*
* TODO: MT: - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*
* TODO: MP: - allocated in a shared memory
* - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*/
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
#endif
if (ls->addr_ntop) {
c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
if (c->addr_text.data == NULL) {
ngx_close_accepted_connection(c);
return;
}
c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
c->addr_text.data,
ls->addr_text_max_len, 0);
if (c->addr_text.len == 0) {
ngx_close_accepted_connection(c);
return;
}
}
#if (NGX_DEBUG)
{
ngx_str_t addr;
u_char text[NGX_SOCKADDR_STRLEN];
ngx_debug_accepted_connection(ecf, c);
if (log->log_level & NGX_LOG_DEBUG_EVENT) {
addr.data = text;
addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
NGX_SOCKADDR_STRLEN, 1);
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,
"*%uA recvmsg: %V fd:%d n:%z",
c->number, &addr, c->fd, n);
}
}
#endif
log->data = NULL;
log->handler = NULL;
ls->handler(c);
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
ev->available -= n;
}
} while (ev->available);
}
#else
void ngx_event_recvmsg(ngx_event_t *ev)
{
ssize_t n;
ngx_log_t *log;
ngx_err_t err;
ngx_event_t *rev, *wev;
WSABUF wsabuf[1];
u_long bytes, flags;
ngx_listening_t *ls;
ngx_event_conf_t *ecf;
ngx_connection_t *c, *lc;
u_char sa[NGX_SOCKADDRLEN];
socklen_t socklen = NGX_SOCKADDRLEN;
static u_char buffer[65535];
if (ev->timedout) {
if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
return;
}
ev->timedout = 0;
}
ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
ev->available = ecf->multi_accept;
lc = ev->data;
ls = lc->listening;
ev->ready = 0;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"WSARecvFrom on %V, ready: %d", &ls->addr_text, ev->available);
do {
ngx_memzero(&wsabuf, sizeof(WSABUF));
wsabuf[0].buf = (void *) buffer;
wsabuf[0].len = sizeof(buffer);
flags = 0;
bytes = 0;
n = WSARecvFrom(lc->fd, &wsabuf, 1, &bytes, &flags,
(struct sockaddr *) sa,
(LPINT) &socklen, NULL, NULL);
if (n == SOCKET_ERROR) {
err = ngx_socket_errno;
if (err == NGX_EAGAIN) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
"WSARecvFrom() not ready");
return;
}
ngx_log_error(NGX_LOG_ALERT, ev->log, err, "WSARecvFrom() failed");
return;
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
#endif
ngx_accept_disabled = ngx_cycle->connection_n / 8
- ngx_cycle->free_connection_n;
c = ngx_get_connection(lc->fd, ev->log);
if (c == NULL) {
return;
}
c->shared = 1;
c->type = SOCK_DGRAM;
c->socklen = sizeof(sa);
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, 1);
#endif
c->pool = ngx_create_pool(ls->pool_size, ev->log);
if (c->pool == NULL) {
ngx_close_accepted_connection(c);
return;
}
c->sockaddr = ngx_palloc(c->pool, c->socklen);
if (c->sockaddr == NULL) {
ngx_close_accepted_connection(c);
return;
}
ngx_memcpy(c->sockaddr, &sa, c->socklen);
log = ngx_palloc(c->pool, sizeof(ngx_log_t));
if (log == NULL) {
ngx_close_accepted_connection(c);
return;
}
*log = ls->log;
c->send = ngx_udp_send;
c->log = log;
c->pool->log = log;
c->listening = ls;
c->local_sockaddr = ls->sockaddr;
c->local_socklen = ls->socklen;
n = bytes;
c->buffer = ngx_create_temp_buf(c->pool, n);
if (c->buffer == NULL) {
ngx_close_accepted_connection(c);
return;
}
c->buffer->last = ngx_cpymem(c->buffer->last, buffer, n);
rev = c->read;
wev = c->write;
wev->ready = 1;
rev->log = log;
wev->log = log;
/*
* TODO: MT: - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*
* TODO: MP: - allocated in a shared memory
* - ngx_atomic_fetch_add()
* or protection by critical section or light mutex
*/
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
#endif
if (ls->addr_ntop) {
c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
if (c->addr_text.data == NULL) {
ngx_close_accepted_connection(c);
return;
}
c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
c->addr_text.data,
ls->addr_text_max_len, 0);
if (c->addr_text.len == 0) {
ngx_close_accepted_connection(c);
return;
}
}
#if (NGX_DEBUG)
{
ngx_str_t addr;
u_char text[NGX_SOCKADDR_STRLEN];
ngx_debug_accepted_connection(ecf, c);
if (log->log_level & NGX_LOG_DEBUG_EVENT) {
addr.data = text;
addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
NGX_SOCKADDR_STRLEN, 1);
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,
"*%uA WSARecvFrom: %V fd:%d n:%z",
c->number, &addr, c->fd, n);
}
}
#endif
log->data = NULL;
log->handler = NULL;
ls->handler(c);
} while (ev->available);
}
#endif
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"accept mutex locked");
if (ngx_accept_mutex_held && ngx_accept_events == 0) {
return NGX_OK;
}
if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
ngx_shmtx_unlock(&ngx_accept_mutex);
return NGX_ERROR;
}
ngx_accept_events = 0;
ngx_accept_mutex_held = 1;
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"accept mutex lock failed: %ui", ngx_accept_mutex_held);
if (ngx_accept_mutex_held) {
if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {
return NGX_ERROR;
}
ngx_accept_mutex_held = 0;
}
return NGX_OK;
}
static ngx_int_t
ngx_enable_accept_events(ngx_cycle_t *cycle)
{
ngx_uint_t i;
ngx_listening_t *ls;
ngx_connection_t *c;
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
c = ls[i].connection;
if (c == NULL || c->read->active) {
continue;
}
if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all)
{
ngx_uint_t i;
ngx_listening_t *ls;
ngx_connection_t *c;
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
c = ls[i].connection;
if (c == NULL || !c->read->active) {
continue;
}
#if (NGX_HAVE_REUSEPORT)
/*
* do not disable accept on worker's own sockets
* when disabling accept events due to accept mutex
*/
if (ls[i].reuseport && !all) {
continue;
}
#endif
if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
== NGX_ERROR)
{
return NGX_ERROR;
}
}
return NGX_OK;
}
static void
ngx_close_accepted_connection(ngx_connection_t *c)
{
ngx_socket_t fd;
ngx_free_connection(c);
fd = c->fd;
c->fd = (ngx_socket_t) -1;
if (!c->shared && ngx_close_socket(fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
ngx_close_socket_n " failed");
}
if (c->pool) {
ngx_destroy_pool(c->pool);
}
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_active, -1);
#endif
}
u_char *
ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len)
{
return ngx_snprintf(buf, len, " while accepting new connection on %V",
log->data);
}
#if (NGX_DEBUG)
static void
ngx_debug_accepted_connection(ngx_event_conf_t *ecf, ngx_connection_t *c)
{
struct sockaddr_in *sin;
ngx_cidr_t *cidr;
ngx_uint_t i;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
ngx_uint_t n;
#endif
cidr = ecf->debug_connection.elts;
for (i = 0; i < ecf->debug_connection.nelts; i++) {
if (cidr[i].family != (ngx_uint_t) c->sockaddr->sa_family) {
goto next;
}
switch (cidr[i].family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) c->sockaddr;
for (n = 0; n < 16; n++) {
if ((sin6->sin6_addr.s6_addr[n]
& cidr[i].u.in6.mask.s6_addr[n])
!= cidr[i].u.in6.addr.s6_addr[n])
{
goto next;
}
}
break;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
case AF_UNIX:
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) c->sockaddr;
if ((sin->sin_addr.s_addr & cidr[i].u.in.mask)
!= cidr[i].u.in.addr)
{
goto next;
}
break;
}
c->log->log_level = NGX_LOG_DEBUG_CONNECTION|NGX_LOG_DEBUG_ALL;
break;
next:
continue;
}
}
#endif
+227
View File
@@ -0,0 +1,227 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
static void ngx_close_posted_connection(ngx_connection_t *c);
void
ngx_event_acceptex(ngx_event_t *rev)
{
ngx_listening_t *ls;
ngx_connection_t *c;
c = rev->data;
ls = c->listening;
c->log->handler = ngx_accept_log_error;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "AcceptEx: %d", c->fd);
if (rev->ovlp.error) {
ngx_log_error(NGX_LOG_CRIT, c->log, rev->ovlp.error,
"AcceptEx() %V failed", &ls->addr_text);
return;
}
/* SO_UPDATE_ACCEPT_CONTEXT is required for shutdown() to work */
if (setsockopt(c->fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
(char *) &ls->fd, sizeof(ngx_socket_t))
== -1)
{
ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
"setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed for %V",
&c->addr_text);
/* TODO: close socket */
return;
}
ngx_getacceptexsockaddrs(c->buffer->pos,
ls->post_accept_buffer_size,
ls->socklen + 16,
ls->socklen + 16,
&c->local_sockaddr, &c->local_socklen,
&c->sockaddr, &c->socklen);
if (ls->post_accept_buffer_size) {
c->buffer->last += rev->available;
c->buffer->end = c->buffer->start + ls->post_accept_buffer_size;
} else {
c->buffer = NULL;
}
if (ls->addr_ntop) {
c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
if (c->addr_text.data == NULL) {
/* TODO: close socket */
return;
}
c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
c->addr_text.data,
ls->addr_text_max_len, 0);
if (c->addr_text.len == 0) {
/* TODO: close socket */
return;
}
}
ngx_event_post_acceptex(ls, 1);
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
ls->handler(c);
return;
}
ngx_int_t
ngx_event_post_acceptex(ngx_listening_t *ls, ngx_uint_t n)
{
u_long rcvd;
ngx_err_t err;
ngx_log_t *log;
ngx_uint_t i;
ngx_event_t *rev, *wev;
ngx_socket_t s;
ngx_connection_t *c;
for (i = 0; i < n; i++) {
/* TODO: look up reused sockets */
s = ngx_socket(ls->sockaddr->sa_family, ls->type, 0);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &ls->log, 0,
ngx_socket_n " s:%d", s);
if (s == (ngx_socket_t) -1) {
ngx_log_error(NGX_LOG_ALERT, &ls->log, ngx_socket_errno,
ngx_socket_n " failed");
return NGX_ERROR;
}
c = ngx_get_connection(s, &ls->log);
if (c == NULL) {
return NGX_ERROR;
}
c->pool = ngx_create_pool(ls->pool_size, &ls->log);
if (c->pool == NULL) {
ngx_close_posted_connection(c);
return NGX_ERROR;
}
log = ngx_palloc(c->pool, sizeof(ngx_log_t));
if (log == NULL) {
ngx_close_posted_connection(c);
return NGX_ERROR;
}
c->buffer = ngx_create_temp_buf(c->pool, ls->post_accept_buffer_size
+ 2 * (ls->socklen + 16));
if (c->buffer == NULL) {
ngx_close_posted_connection(c);
return NGX_ERROR;
}
c->local_sockaddr = ngx_palloc(c->pool, ls->socklen);
if (c->local_sockaddr == NULL) {
ngx_close_posted_connection(c);
return NGX_ERROR;
}
c->sockaddr = ngx_palloc(c->pool, ls->socklen);
if (c->sockaddr == NULL) {
ngx_close_posted_connection(c);
return NGX_ERROR;
}
*log = ls->log;
c->log = log;
c->recv = ngx_recv;
c->send = ngx_send;
c->recv_chain = ngx_recv_chain;
c->send_chain = ngx_send_chain;
c->listening = ls;
rev = c->read;
wev = c->write;
rev->ovlp.event = rev;
wev->ovlp.event = wev;
rev->handler = ngx_event_acceptex;
rev->ready = 1;
wev->ready = 1;
rev->log = c->log;
wev->log = c->log;
if (ngx_add_event(rev, 0, NGX_IOCP_IO) == NGX_ERROR) {
ngx_close_posted_connection(c);
return NGX_ERROR;
}
if (ngx_acceptex(ls->fd, s, c->buffer->pos, ls->post_accept_buffer_size,
ls->socklen + 16, ls->socklen + 16,
&rcvd, (LPOVERLAPPED) &rev->ovlp)
== 0)
{
err = ngx_socket_errno;
if (err != WSA_IO_PENDING) {
ngx_log_error(NGX_LOG_ALERT, &ls->log, err,
"AcceptEx() %V failed", &ls->addr_text);
ngx_close_posted_connection(c);
return NGX_ERROR;
}
}
}
return NGX_OK;
}
static void
ngx_close_posted_connection(ngx_connection_t *c)
{
ngx_socket_t fd;
ngx_free_connection(c);
fd = c->fd;
c->fd = (ngx_socket_t) -1;
if (ngx_close_socket(fd) == -1) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
ngx_close_socket_n " failed");
}
if (c->pool) {
ngx_destroy_pool(c->pool);
}
}
u_char *
ngx_acceptex_log_error(ngx_log_t *log, u_char *buf, size_t len)
{
return ngx_snprintf(buf, len, " while posting AcceptEx() on %V", log->data);
}
+410
View File
@@ -0,0 +1,410 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_connect.h>
#if (NGX_HAVE_TRANSPARENT_PROXY)
static ngx_int_t ngx_event_connect_set_transparent(ngx_peer_connection_t *pc,
ngx_socket_t s);
#endif
ngx_int_t
ngx_event_connect_peer(ngx_peer_connection_t *pc)
{
int rc, type;
#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
in_port_t port;
#endif
ngx_int_t event;
ngx_err_t err;
ngx_uint_t level;
ngx_socket_t s;
ngx_event_t *rev, *wev;
ngx_connection_t *c;
rc = pc->get(pc, pc->data);
if (rc != NGX_OK) {
return rc;
}
type = (pc->type ? pc->type : SOCK_STREAM);
s = ngx_socket(pc->sockaddr->sa_family, type, 0);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d",
(type == SOCK_STREAM) ? "stream" : "dgram", s);
if (s == (ngx_socket_t) -1) {
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
ngx_socket_n " failed");
return NGX_ERROR;
}
c = ngx_get_connection(s, pc->log);
if (c == NULL) {
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
ngx_close_socket_n "failed");
}
return NGX_ERROR;
}
c->type = type;
if (pc->rcvbuf) {
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
(const void *) &pc->rcvbuf, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
"setsockopt(SO_RCVBUF) failed");
goto failed;
}
}
if (ngx_nonblocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
ngx_nonblocking_n " failed");
goto failed;
}
if (pc->local) {
#if (NGX_HAVE_TRANSPARENT_PROXY)
if (pc->transparent) {
if (ngx_event_connect_set_transparent(pc, s) != NGX_OK) {
goto failed;
}
}
#endif
#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
port = ngx_inet_get_port(pc->local->sockaddr);
#endif
#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT)
if (pc->sockaddr->sa_family != AF_UNIX && port == 0) {
static int bind_address_no_port = 1;
if (bind_address_no_port) {
if (setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT,
(const void *) &bind_address_no_port,
sizeof(int)) == -1)
{
err = ngx_socket_errno;
if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) {
ngx_log_error(NGX_LOG_ALERT, pc->log, err,
"setsockopt(IP_BIND_ADDRESS_NO_PORT) "
"failed, ignored");
} else {
bind_address_no_port = 0;
}
}
}
}
#endif
#if (NGX_LINUX)
if (pc->type == SOCK_DGRAM && port != 0) {
int reuse_addr = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(const void *) &reuse_addr, sizeof(int))
== -1)
{
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
"setsockopt(SO_REUSEADDR) failed");
goto failed;
}
}
#endif
if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {
ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,
"bind(%V) failed", &pc->local->name);
goto failed;
}
}
if (type == SOCK_STREAM) {
c->recv = ngx_recv;
c->send = ngx_send;
c->recv_chain = ngx_recv_chain;
c->send_chain = ngx_send_chain;
c->sendfile = 1;
if (pc->sockaddr->sa_family == AF_UNIX) {
c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
#if (NGX_SOLARIS)
/* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
c->sendfile = 0;
#endif
}
} else { /* type == SOCK_DGRAM */
c->recv = ngx_udp_recv;
c->send = ngx_send;
c->send_chain = ngx_udp_send_chain;
}
c->log_error = pc->log_error;
rev = c->read;
wev = c->write;
rev->log = pc->log;
wev->log = pc->log;
pc->connection = c;
c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
if (ngx_add_conn) {
if (ngx_add_conn(c) == NGX_ERROR) {
goto failed;
}
}
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,
"connect to %V, fd:%d #%uA", pc->name, s, c->number);
rc = connect(s, pc->sockaddr, pc->socklen);
if (rc == -1) {
err = ngx_socket_errno;
if (err != NGX_EINPROGRESS
#if (NGX_WIN32)
/* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
&& err != NGX_EAGAIN
#endif
)
{
if (err == NGX_ECONNREFUSED
#if (NGX_LINUX)
/*
* Linux returns EAGAIN instead of ECONNREFUSED
* for unix sockets if listen queue is full
*/
|| err == NGX_EAGAIN
#endif
|| err == NGX_ECONNRESET
|| err == NGX_ENETDOWN
|| err == NGX_ENETUNREACH
|| err == NGX_EHOSTDOWN
|| err == NGX_EHOSTUNREACH)
{
level = NGX_LOG_ERR;
} else {
level = NGX_LOG_CRIT;
}
ngx_log_error(level, c->log, err, "connect() to %V failed",
pc->name);
ngx_close_connection(c);
pc->connection = NULL;
return NGX_DECLINED;
}
}
if (ngx_add_conn) {
if (rc == -1) {
/* NGX_EINPROGRESS */
return NGX_AGAIN;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
wev->ready = 1;
return NGX_OK;
}
if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,
"connect(): %d", rc);
if (ngx_blocking(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
ngx_blocking_n " failed");
goto failed;
}
/*
* FreeBSD's aio allows to post an operation on non-connected socket.
* NT does not support it.
*
* TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT
*/
rev->ready = 1;
wev->ready = 1;
return NGX_OK;
}
if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
/* kqueue */
event = NGX_CLEAR_EVENT;
} else {
/* select, poll, /dev/poll */
event = NGX_LEVEL_EVENT;
}
if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
goto failed;
}
if (rc == -1) {
/* NGX_EINPROGRESS */
if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
goto failed;
}
return NGX_AGAIN;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
wev->ready = 1;
return NGX_OK;
failed:
ngx_close_connection(c);
pc->connection = NULL;
return NGX_ERROR;
}
#if (NGX_HAVE_TRANSPARENT_PROXY)
static ngx_int_t
ngx_event_connect_set_transparent(ngx_peer_connection_t *pc, ngx_socket_t s)
{
int value;
value = 1;
#if defined(SO_BINDANY)
if (setsockopt(s, SOL_SOCKET, SO_BINDANY,
(const void *) &value, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
"setsockopt(SO_BINDANY) failed");
return NGX_ERROR;
}
#else
switch (pc->local->sockaddr->sa_family) {
case AF_INET:
#if defined(IP_TRANSPARENT)
if (setsockopt(s, IPPROTO_IP, IP_TRANSPARENT,
(const void *) &value, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
"setsockopt(IP_TRANSPARENT) failed");
return NGX_ERROR;
}
#elif defined(IP_BINDANY)
if (setsockopt(s, IPPROTO_IP, IP_BINDANY,
(const void *) &value, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
"setsockopt(IP_BINDANY) failed");
return NGX_ERROR;
}
#endif
break;
#if (NGX_HAVE_INET6)
case AF_INET6:
#if defined(IPV6_TRANSPARENT)
if (setsockopt(s, IPPROTO_IPV6, IPV6_TRANSPARENT,
(const void *) &value, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
"setsockopt(IPV6_TRANSPARENT) failed");
return NGX_ERROR;
}
#elif defined(IPV6_BINDANY)
if (setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY,
(const void *) &value, sizeof(int)) == -1)
{
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
"setsockopt(IPV6_BINDANY) failed");
return NGX_ERROR;
}
#endif
break;
#endif /* NGX_HAVE_INET6 */
}
#endif /* SO_BINDANY */
return NGX_OK;
}
#endif
ngx_int_t
ngx_event_get_peer(ngx_peer_connection_t *pc, void *data)
{
return NGX_OK;
}
+78
View File
@@ -0,0 +1,78 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_CONNECT_H_INCLUDED_
#define _NGX_EVENT_CONNECT_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#define NGX_PEER_KEEPALIVE 1
#define NGX_PEER_NEXT 2
#define NGX_PEER_FAILED 4
typedef struct ngx_peer_connection_s ngx_peer_connection_t;
typedef ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc,
void *data);
typedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, void *data,
ngx_uint_t state);
typedef void (*ngx_event_notify_peer_pt)(ngx_peer_connection_t *pc,
void *data, ngx_uint_t type);
typedef ngx_int_t (*ngx_event_set_peer_session_pt)(ngx_peer_connection_t *pc,
void *data);
typedef void (*ngx_event_save_peer_session_pt)(ngx_peer_connection_t *pc,
void *data);
struct ngx_peer_connection_s {
ngx_connection_t *connection;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t *name;
ngx_uint_t tries;
ngx_msec_t start_time;
ngx_event_get_peer_pt get;
ngx_event_free_peer_pt free;
ngx_event_notify_peer_pt notify;
void *data;
#if (NGX_SSL || NGX_COMPAT)
ngx_event_set_peer_session_pt set_session;
ngx_event_save_peer_session_pt save_session;
#endif
ngx_addr_t *local;
int type;
int rcvbuf;
ngx_log_t *log;
unsigned cached:1;
unsigned transparent:1;
/* ngx_connection_log_error_e */
unsigned log_error:2;
NGX_COMPAT_BEGIN(2)
NGX_COMPAT_END
};
ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc);
ngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data);
#endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */
+206
View File
@@ -0,0 +1,206 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#define NGX_MAX_PENDING_CONN 10
static CRITICAL_SECTION connect_lock;
static int nconnects;
static ngx_connection_t pending_connects[NGX_MAX_PENDING_CONN];
static HANDLE pending_connect_event;
__declspec(thread) int nevents = 0;
__declspec(thread) WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS + 1];
__declspec(thread) ngx_connection_t *conn[WSA_MAXIMUM_WAIT_EVENTS + 1];
int ngx_iocp_wait_connect(ngx_connection_t *c)
{
for ( ;; ) {
EnterCriticalSection(&connect_lock);
if (nconnects < NGX_MAX_PENDING_CONN) {
pending_connects[--nconnects] = c;
LeaveCriticalSection(&connect_lock);
if (SetEvent(pending_connect_event) == 0) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"SetEvent() failed");
return NGX_ERROR;
break;
}
LeaveCriticalSection(&connect_lock);
ngx_log_error(NGX_LOG_NOTICE, c->log, 0,
"max number of pending connect()s is %d",
NGX_MAX_PENDING_CONN);
msleep(100);
}
if (!started) {
if (ngx_iocp_new_thread(1) == NGX_ERROR) {
return NGX_ERROR;
}
started = 1;
}
return NGX_OK;
}
int ngx_iocp_new_thread(int main)
{
u_int id;
if (main) {
pending_connect_event = CreateEvent(NULL, 0, 1, NULL);
if (pending_connect_event == INVALID_HANDLE_VALUE) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"CreateThread() failed");
return NGX_ERROR;
}
}
if (CreateThread(NULL, 0, ngx_iocp_wait_events, main, 0, &id)
== INVALID_HANDLE_VALUE)
{
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"CreateThread() failed");
return NGX_ERROR;
}
SetEvent(event) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
"SetEvent() failed");
return NGX_ERROR;
}
return NGX_OK;
}
int ngx_iocp_new_connect()
{
EnterCriticalSection(&connect_lock);
c = pending_connects[--nconnects];
LeaveCriticalSection(&connect_lock);
conn[nevents] = c;
events[nevents] = WSACreateEvent();
if (events[nevents] == INVALID_HANDLE_VALUE) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
"WSACreateEvent() failed");
return NGX_ERROR;
}
if (WSAEventSelect(c->fd, events[nevents], FD_CONNECT) == -1)
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
"WSAEventSelect() failed");
return NGX_ERROR;
}
nevents++;
return NGX_OK;
}
void ngx_iocp_wait_events(int main)
{
WSANETWORKEVENTS ne;
nevents = 1;
events[0] = pending_connect_event;
conn[0] = NULL;
for ( ;; ) {
offset = (nevents == WSA_MAXIMUM_WAIT_EVENTS + 1) ? 1: 0;
timeout = (nevents == 1 && !first) ? 60000: INFINITE;
n = WSAWaitForMultipleEvents(nevents - offset, events[offset],
0, timeout, 0);
if (n == WAIT_FAILED) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
"WSAWaitForMultipleEvents() failed");
continue;
}
if (n == WAIT_TIMEOUT) {
if (nevents == 2 && !main) {
ExitThread(0);
}
ngx_log_error(NGX_LOG_ALERT, log, 0,
"WSAWaitForMultipleEvents() "
"returned unexpected WAIT_TIMEOUT");
continue;
}
n -= WSA_WAIT_EVENT_0;
if (events[n] == NULL) {
/* the pending_connect_event */
if (nevents == WSA_MAXIMUM_WAIT_EVENTS) {
ngx_iocp_new_thread(0);
} else {
ngx_iocp_new_connect();
}
continue;
}
if (WSAEnumNetworkEvents(c[n].fd, events[n], &ne) == -1) {
ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
"WSAEnumNetworkEvents() failed");
continue;
}
if (ne.lNetworkEvents & FD_CONNECT) {
conn[n].write->ovlp.error = ne.iErrorCode[FD_CONNECT_BIT];
if (PostQueuedCompletionStatus(iocp, 0, NGX_IOCP_CONNECT,
&conn[n].write->ovlp) == 0)
{
ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
"PostQueuedCompletionStatus() failed");
continue;
}
if (n < nevents) {
conn[n] = conn[nevents];
events[n] = events[nevents];
}
nevents--;
continue;
}
if (ne.lNetworkEvents & FD_ACCEPT) {
/* CHECK ERROR ??? */
ngx_event_post_acceptex(conn[n].listening, 1);
continue;
}
ngx_log_error(NGX_LOG_ALERT, c[n].log, 0,
"WSAWaitForMultipleEvents() "
"returned unexpected network event %ul",
ne.lNetworkEvents);
}
}
+4290
View File
@@ -0,0 +1,4290 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
typedef struct {
ngx_uint_t engine; /* unsigned engine:1; */
} ngx_openssl_conf_t;
static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
void *userdata);
static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
int ret);
static void ngx_ssl_passwords_cleanup(void *data);
static void ngx_ssl_handshake_handler(ngx_event_t *ev);
static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
static void ngx_ssl_write_handler(ngx_event_t *wev);
static void ngx_ssl_read_handler(ngx_event_t *rev);
static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
ngx_err_t err, char *text);
static void ngx_ssl_clear_error(ngx_log_t *log);
static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
ngx_str_t *sess_ctx);
ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,
ngx_ssl_session_t *sess);
static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
#if OPENSSL_VERSION_NUMBER >= 0x10100003L
const
#endif
u_char *id, int len, int *copy);
static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
ngx_slab_pool_t *shpool, ngx_uint_t n);
static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
HMAC_CTX *hctx, int enc);
#endif
#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str);
#endif
static time_t ngx_ssl_parse_time(
#if OPENSSL_VERSION_NUMBER > 0x10100000L
const
#endif
ASN1_TIME *asn1time);
static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void ngx_openssl_exit(ngx_cycle_t *cycle);
static ngx_command_t ngx_openssl_commands[] = {
{ ngx_string("ssl_engine"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
ngx_openssl_engine,
0,
0,
NULL },
ngx_null_command
};
static ngx_core_module_t ngx_openssl_module_ctx = {
ngx_string("openssl"),
ngx_openssl_create_conf,
NULL
};
ngx_module_t ngx_openssl_module = {
NGX_MODULE_V1,
&ngx_openssl_module_ctx, /* module context */
ngx_openssl_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
ngx_openssl_exit, /* exit master */
NGX_MODULE_V1_PADDING
};
int ngx_ssl_connection_index;
int ngx_ssl_server_conf_index;
int ngx_ssl_session_cache_index;
int ngx_ssl_session_ticket_keys_index;
int ngx_ssl_certificate_index;
int ngx_ssl_next_certificate_index;
int ngx_ssl_certificate_name_index;
int ngx_ssl_stapling_index;
ngx_int_t
ngx_ssl_init(ngx_log_t *log)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100003L
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_init_ssl() failed");
return NGX_ERROR;
}
/*
* OPENSSL_init_ssl() may leave errors in the error queue
* while returning success
*/
ERR_clear_error();
#else
OPENSSL_config(NULL);
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#endif
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#ifndef SSL_OP_NO_COMPRESSION
{
/*
* Disable gzip compression in OpenSSL prior to 1.0.0 version,
* this saves about 522K per connection.
*/
int n;
STACK_OF(SSL_COMP) *ssl_comp_methods;
ssl_comp_methods = SSL_COMP_get_compression_methods();
n = sk_SSL_COMP_num(ssl_comp_methods);
while (n--) {
(void) sk_SSL_COMP_pop(ssl_comp_methods);
}
}
#endif
#endif
ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
if (ngx_ssl_connection_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_server_conf_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_session_cache_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_session_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
NULL, NULL);
if (ngx_ssl_session_ticket_keys_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_certificate_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
"SSL_CTX_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_next_certificate_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
NULL);
if (ngx_ssl_certificate_name_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);
if (ngx_ssl_stapling_index == -1) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
{
ssl->ctx = SSL_CTX_new(SSLv23_method());
if (ssl->ctx == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_new() failed");
return NGX_ERROR;
}
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, NULL) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
ssl->buffer_size = NGX_SSL_BUFSIZE;
/* client side options */
#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);
#endif
#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_NETSCAPE_CHALLENGE_BUG);
#endif
/* server side options */
#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
#endif
#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
#endif
#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
/* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */
SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING);
#endif
#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
#endif
#ifdef SSL_OP_TLS_D5_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);
#endif
#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);
#endif
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
#endif
SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
#ifdef SSL_CTRL_CLEAR_OPTIONS
/* only in 0.9.8m+ */
SSL_CTX_clear_options(ssl->ctx,
SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
#endif
if (!(protocols & NGX_SSL_SSLv2)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);
}
if (!(protocols & NGX_SSL_SSLv3)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3);
}
if (!(protocols & NGX_SSL_TLSv1)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);
}
#ifdef SSL_OP_NO_TLSv1_1
SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
if (!(protocols & NGX_SSL_TLSv1_1)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
}
#endif
#ifdef SSL_OP_NO_TLSv1_2
SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
if (!(protocols & NGX_SSL_TLSv1_2)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
}
#endif
#ifdef SSL_OP_NO_TLSv1_3
SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_3);
if (!(protocols & NGX_SSL_TLSv1_3)) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_3);
}
#endif
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);
#endif
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
#ifdef SSL_MODE_NO_AUTO_CHAIN
SSL_CTX_set_mode(ssl->ctx, SSL_MODE_NO_AUTO_CHAIN);
#endif
SSL_CTX_set_read_ahead(ssl->ctx, 1);
SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);
return NGX_OK;
}
ngx_int_t
ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs,
ngx_array_t *keys, ngx_array_t *passwords)
{
ngx_str_t *cert, *key;
ngx_uint_t i;
cert = certs->elts;
key = keys->elts;
for (i = 0; i < certs->nelts; i++) {
if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords)
!= NGX_OK)
{
return NGX_ERROR;
}
}
return NGX_OK;
}
ngx_int_t
ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_str_t *key, ngx_array_t *passwords)
{
BIO *bio;
X509 *x509;
u_long n;
ngx_str_t *pwd;
ngx_uint_t tries;
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
return NGX_ERROR;
}
/*
* we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
* allow to access certificate later from SSL_CTX, so we reimplement
* it here
*/
bio = BIO_new_file((char *) cert->data, "r");
if (bio == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"BIO_new_file(\"%s\") failed", cert->data);
return NGX_ERROR;
}
x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
if (x509 == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
BIO_free(bio);
return NGX_ERROR;
}
if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_certificate(\"%s\") failed", cert->data);
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
if (X509_set_ex_data(x509, ngx_ssl_certificate_name_index, cert->data)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index,
SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index))
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
/* read rest of the chain */
for ( ;; ) {
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (x509 == NULL) {
n = ERR_peek_last_error();
if (ERR_GET_LIB(n) == ERR_LIB_PEM
&& ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
{
/* end of file */
ERR_clear_error();
break;
}
/* some real error */
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"PEM_read_bio_X509(\"%s\") failed", cert->data);
BIO_free(bio);
return NGX_ERROR;
}
#ifdef SSL_CTRL_CHAIN_CERT
/*
* SSL_CTX_add0_chain_cert() is needed to add chain to
* a particular certificate when multiple certificates are used;
* only available in OpenSSL 1.0.2+
*/
if (SSL_CTX_add0_chain_cert(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_add0_chain_cert(\"%s\") failed",
cert->data);
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
#else
if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_add_extra_chain_cert(\"%s\") failed",
cert->data);
X509_free(x509);
BIO_free(bio);
return NGX_ERROR;
}
#endif
}
BIO_free(bio);
if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {
#ifndef OPENSSL_NO_ENGINE
u_char *p, *last;
ENGINE *engine;
EVP_PKEY *pkey;
p = key->data + sizeof("engine:") - 1;
last = (u_char *) ngx_strchr(p, ':');
if (last == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid syntax in \"%V\"", key);
return NGX_ERROR;
}
*last = '\0';
engine = ENGINE_by_id((char *) p);
if (engine == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"ENGINE_by_id(\"%s\") failed", p);
return NGX_ERROR;
}
*last++ = ':';
pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
if (pkey == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"ENGINE_load_private_key(\"%s\") failed", last);
ENGINE_free(engine);
return NGX_ERROR;
}
ENGINE_free(engine);
if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_PrivateKey(\"%s\") failed", last);
EVP_PKEY_free(pkey);
return NGX_ERROR;
}
EVP_PKEY_free(pkey);
return NGX_OK;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"loading \"engine:...\" certificate keys "
"is not supported");
return NGX_ERROR;
#endif
}
if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
return NGX_ERROR;
}
if (passwords) {
tries = passwords->nelts;
pwd = passwords->elts;
SSL_CTX_set_default_passwd_cb(ssl->ctx, ngx_ssl_password_callback);
SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, pwd);
} else {
tries = 1;
#if (NGX_SUPPRESS_WARN)
pwd = NULL;
#endif
}
for ( ;; ) {
if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
SSL_FILETYPE_PEM)
!= 0)
{
break;
}
if (--tries) {
ERR_clear_error();
SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ++pwd);
continue;
}
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);
return NGX_ERROR;
}
SSL_CTX_set_default_passwd_cb(ssl->ctx, NULL);
return NGX_OK;
}
static int
ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata)
{
ngx_str_t *pwd = userdata;
if (rwflag) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"ngx_ssl_password_callback() is called for encryption");
return 0;
}
if (pwd->len > (size_t) size) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"password is truncated to %d bytes", size);
} else {
size = pwd->len;
}
ngx_memcpy(buf, pwd->data, size);
return size;
}
ngx_int_t
ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
ngx_uint_t prefer_server_ciphers)
{
if (SSL_CTX_set_cipher_list(ssl->ctx, (char *) ciphers->data) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_cipher_list(\"%V\") failed",
ciphers);
return NGX_ERROR;
}
if (prefer_server_ciphers) {
SSL_CTX_set_options(ssl->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
}
#if (OPENSSL_VERSION_NUMBER < 0x10100001L && !defined LIBRESSL_VERSION_NUMBER)
/* a temporary 512-bit RSA key is required for export versions of MSIE */
SSL_CTX_set_tmp_rsa_callback(ssl->ctx, ngx_ssl_rsa512_key_callback);
#endif
return NGX_OK;
}
ngx_int_t
ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_int_t depth)
{
STACK_OF(X509_NAME) *list;
SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback);
SSL_CTX_set_verify_depth(ssl->ctx, depth);
if (cert->len == 0) {
return NGX_OK;
}
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
return NGX_ERROR;
}
if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_load_verify_locations(\"%s\") failed",
cert->data);
return NGX_ERROR;
}
/*
* SSL_CTX_load_verify_locations() may leave errors in the error queue
* while returning success
*/
ERR_clear_error();
list = SSL_load_client_CA_file((char *) cert->data);
if (list == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_load_client_CA_file(\"%s\") failed", cert->data);
return NGX_ERROR;
}
/*
* before 0.9.7h and 0.9.8 SSL_load_client_CA_file()
* always leaved an error in the error queue
*/
ERR_clear_error();
SSL_CTX_set_client_CA_list(ssl->ctx, list);
return NGX_OK;
}
ngx_int_t
ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_int_t depth)
{
SSL_CTX_set_verify_depth(ssl->ctx, depth);
if (cert->len == 0) {
return NGX_OK;
}
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
return NGX_ERROR;
}
if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_load_verify_locations(\"%s\") failed",
cert->data);
return NGX_ERROR;
}
/*
* SSL_CTX_load_verify_locations() may leave errors in the error queue
* while returning success
*/
ERR_clear_error();
return NGX_OK;
}
ngx_int_t
ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
{
X509_STORE *store;
X509_LOOKUP *lookup;
if (crl->len == 0) {
return NGX_OK;
}
if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {
return NGX_ERROR;
}
store = SSL_CTX_get_cert_store(ssl->ctx);
if (store == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_get_cert_store() failed");
return NGX_ERROR;
}
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (lookup == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"X509_STORE_add_lookup() failed");
return NGX_ERROR;
}
if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"X509_LOOKUP_load_file(\"%s\") failed", crl->data);
return NGX_ERROR;
}
X509_STORE_set_flags(store,
X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
return NGX_OK;
}
static int
ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
{
#if (NGX_DEBUG)
char *subject, *issuer;
int err, depth;
X509 *cert;
X509_NAME *sname, *iname;
ngx_connection_t *c;
ngx_ssl_conn_t *ssl_conn;
ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,
SSL_get_ex_data_X509_STORE_CTX_idx());
c = ngx_ssl_get_connection(ssl_conn);
cert = X509_STORE_CTX_get_current_cert(x509_store);
err = X509_STORE_CTX_get_error(x509_store);
depth = X509_STORE_CTX_get_error_depth(x509_store);
sname = X509_get_subject_name(cert);
subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)";
iname = X509_get_issuer_name(cert);
issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
"verify:%d, error:%d, depth:%d, "
"subject:\"%s\", issuer:\"%s\"",
ok, err, depth, subject, issuer);
if (sname) {
OPENSSL_free(subject);
}
if (iname) {
OPENSSL_free(issuer);
}
#endif
return 1;
}
static void
ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
{
BIO *rbio, *wbio;
ngx_connection_t *c;
if ((where & SSL_CB_HANDSHAKE_START)
&& SSL_is_server((ngx_ssl_conn_t *) ssl_conn))
{
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
if (c->ssl->handshaked) {
c->ssl->renegotiation = 1;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL renegotiation");
}
}
if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) {
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
if (!c->ssl->handshake_buffer_set) {
/*
* By default OpenSSL uses 4k buffer during a handshake,
* which is too low for long certificate chains and might
* result in extra round-trips.
*
* To adjust a buffer size we detect that buffering was added
* to write side of the connection by comparing rbio and wbio.
* If they are different, we assume that it's due to buffering
* added to wbio, and set buffer size.
*/
rbio = SSL_get_rbio((ngx_ssl_conn_t *) ssl_conn);
wbio = SSL_get_wbio((ngx_ssl_conn_t *) ssl_conn);
if (rbio != wbio) {
(void) BIO_set_write_buffer_size(wbio, NGX_SSL_BUFSIZE);
c->ssl->handshake_buffer_set = 1;
}
}
}
}
RSA *
ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
int key_length)
{
static RSA *key;
if (key_length != 512) {
return NULL;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100003L && !defined OPENSSL_NO_DEPRECATED)
if (key == NULL) {
key = RSA_generate_key(512, RSA_F4, NULL, NULL);
}
#endif
return key;
}
ngx_array_t *
ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
{
u_char *p, *last, *end;
size_t len;
ssize_t n;
ngx_fd_t fd;
ngx_str_t *pwd;
ngx_array_t *passwords;
ngx_pool_cleanup_t *cln;
u_char buf[NGX_SSL_PASSWORD_BUFFER_SIZE];
if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
return NULL;
}
cln = ngx_pool_cleanup_add(cf->temp_pool, 0);
passwords = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));
if (cln == NULL || passwords == NULL) {
return NULL;
}
cln->handler = ngx_ssl_passwords_cleanup;
cln->data = passwords;
fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
if (fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%s\" failed", file->data);
return NULL;
}
len = 0;
last = buf;
do {
n = ngx_read_fd(fd, last, NGX_SSL_PASSWORD_BUFFER_SIZE - len);
if (n == -1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_read_fd_n " \"%s\" failed", file->data);
passwords = NULL;
goto cleanup;
}
end = last + n;
if (len && n == 0) {
*end++ = LF;
}
p = buf;
for ( ;; ) {
last = ngx_strlchr(last, end, LF);
if (last == NULL) {
break;
}
len = last++ - p;
if (len && p[len - 1] == CR) {
len--;
}
if (len) {
pwd = ngx_array_push(passwords);
if (pwd == NULL) {
passwords = NULL;
goto cleanup;
}
pwd->len = len;
pwd->data = ngx_pnalloc(cf->temp_pool, len);
if (pwd->data == NULL) {
passwords->nelts--;
passwords = NULL;
goto cleanup;
}
ngx_memcpy(pwd->data, p, len);
}
p = last;
}
len = end - p;
if (len == NGX_SSL_PASSWORD_BUFFER_SIZE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too long line in \"%s\"", file->data);
passwords = NULL;
goto cleanup;
}
ngx_memmove(buf, p, len);
last = buf + len;
} while (n != 0);
if (passwords->nelts == 0) {
pwd = ngx_array_push(passwords);
if (pwd == NULL) {
passwords = NULL;
goto cleanup;
}
ngx_memzero(pwd, sizeof(ngx_str_t));
}
cleanup:
if (ngx_close_file(fd) == NGX_FILE_ERROR) {
ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno,
ngx_close_file_n " \"%s\" failed", file->data);
}
ngx_memzero(buf, NGX_SSL_PASSWORD_BUFFER_SIZE);
return passwords;
}
static void
ngx_ssl_passwords_cleanup(void *data)
{
ngx_array_t *passwords = data;
ngx_str_t *pwd;
ngx_uint_t i;
pwd = passwords->elts;
for (i = 0; i < passwords->nelts; i++) {
ngx_memzero(pwd[i].data, pwd[i].len);
}
}
ngx_int_t
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
{
DH *dh;
BIO *bio;
if (file->len == 0) {
return NGX_OK;
}
if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
return NGX_ERROR;
}
bio = BIO_new_file((char *) file->data, "r");
if (bio == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"BIO_new_file(\"%s\") failed", file->data);
return NGX_ERROR;
}
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
if (dh == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"PEM_read_bio_DHparams(\"%s\") failed", file->data);
BIO_free(bio);
return NGX_ERROR;
}
SSL_CTX_set_tmp_dh(ssl->ctx, dh);
DH_free(dh);
BIO_free(bio);
return NGX_OK;
}
ngx_int_t
ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
{
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#ifndef OPENSSL_NO_ECDH
/*
* Elliptic-Curve Diffie-Hellman parameters are either "named curves"
* from RFC 4492 section 5.1.1, or explicitly described curves over
* binary fields. OpenSSL only supports the "named curves", which provide
* maximum interoperability.
*/
#if (defined SSL_CTX_set1_curves_list || defined SSL_CTRL_SET_CURVES_LIST)
/*
* OpenSSL 1.0.2+ allows configuring a curve list instead of a single
* curve previously supported. By default an internal list is used,
* with prime256v1 being preferred by server in OpenSSL 1.0.2b+
* and X25519 in OpenSSL 1.1.0+.
*
* By default a curve preferred by the client will be used for
* key exchange. The SSL_OP_CIPHER_SERVER_PREFERENCE option can
* be used to prefer server curves instead, similar to what it
* does for ciphers.
*/
SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);
#if SSL_CTRL_SET_ECDH_AUTO
/* not needed in OpenSSL 1.1.0+ */
SSL_CTX_set_ecdh_auto(ssl->ctx, 1);
#endif
if (ngx_strcmp(name->data, "auto") == 0) {
return NGX_OK;
}
if (SSL_CTX_set1_curves_list(ssl->ctx, (char *) name->data) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set1_curves_list(\"%s\") failed", name->data);
return NGX_ERROR;
}
#else
int nid;
char *curve;
EC_KEY *ecdh;
if (ngx_strcmp(name->data, "auto") == 0) {
curve = "prime256v1";
} else {
curve = (char *) name->data;
}
nid = OBJ_sn2nid(curve);
if (nid == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"OBJ_sn2nid(\"%s\") failed: unknown curve", curve);
return NGX_ERROR;
}
ecdh = EC_KEY_new_by_curve_name(nid);
if (ecdh == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"EC_KEY_new_by_curve_name(\"%s\") failed", curve);
return NGX_ERROR;
}
SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);
SSL_CTX_set_tmp_ecdh(ssl->ctx, ecdh);
EC_KEY_free(ecdh);
#endif
#endif
#endif
return NGX_OK;
}
ngx_int_t
ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
{
ngx_ssl_connection_t *sc;
sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t));
if (sc == NULL) {
return NGX_ERROR;
}
sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);
sc->buffer_size = ssl->buffer_size;
sc->dyn_rec = ssl->dyn_rec;
sc->session_ctx = ssl->ctx;
sc->connection = SSL_new(ssl->ctx);
if (sc->connection == NULL) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");
return NGX_ERROR;
}
if (SSL_set_fd(sc->connection, c->fd) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");
return NGX_ERROR;
}
if (flags & NGX_SSL_CLIENT) {
SSL_set_connect_state(sc->connection);
} else {
SSL_set_accept_state(sc->connection);
}
if (SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
return NGX_ERROR;
}
c->ssl = sc;
return NGX_OK;
}
ngx_int_t
ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session)
{
if (session) {
if (SSL_set_session(c->ssl->connection, session) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_session() failed");
return NGX_ERROR;
}
}
return NGX_OK;
}
ngx_int_t
ngx_ssl_handshake(ngx_connection_t *c)
{
int n, sslerr;
ngx_err_t err;
ngx_ssl_clear_error(c->log);
n = SSL_do_handshake(c->ssl->connection);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
if (n == 1) {
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
#if (NGX_DEBUG)
{
char buf[129], *s, *d;
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
const
#endif
SSL_CIPHER *cipher;
cipher = SSL_get_current_cipher(c->ssl->connection);
if (cipher) {
SSL_CIPHER_description(cipher, &buf[1], 128);
for (s = &buf[1], d = buf; *s; s++) {
if (*s == ' ' && *d == ' ') {
continue;
}
if (*s == LF || *s == CR) {
continue;
}
*++d = *s;
}
if (*d != ' ') {
d++;
}
*d = '\0';
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL: %s, cipher: \"%s\"",
SSL_get_version(c->ssl->connection), &buf[1]);
if (SSL_session_reused(c->ssl->connection)) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL reused session");
}
} else {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL no shared ciphers");
}
}
#endif
c->ssl->handshaked = 1;
c->recv = ngx_ssl_recv;
c->send = ngx_ssl_write;
c->recv_chain = ngx_ssl_recv_chain;
c->send_chain = ngx_ssl_send_chain;
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS
/* initial handshake done, disable renegotiation (CVE-2009-3555) */
if (c->ssl->connection->s3 && SSL_is_server(c->ssl->connection)) {
c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
}
#endif
#endif
return NGX_OK;
}
sslerr = SSL_get_error(c->ssl->connection, n);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
if (sslerr == SSL_ERROR_WANT_READ) {
c->read->ready = 0;
c->read->handler = ngx_ssl_handshake_handler;
c->write->handler = ngx_ssl_handshake_handler;
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
return NGX_AGAIN;
}
if (sslerr == SSL_ERROR_WANT_WRITE) {
c->write->ready = 0;
c->read->handler = ngx_ssl_handshake_handler;
c->write->handler = ngx_ssl_handshake_handler;
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
return NGX_AGAIN;
}
err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
c->ssl->no_wait_shutdown = 1;
c->ssl->no_send_shutdown = 1;
c->read->eof = 1;
if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
ngx_connection_error(c, err,
"peer closed connection in SSL handshake");
return NGX_ERROR;
}
c->read->error = 1;
ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed");
return NGX_ERROR;
}
static void
ngx_ssl_handshake_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
c = ev->data;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL handshake handler: %d", ev->write);
if (ev->timedout) {
c->ssl->handler(c);
return;
}
if (ngx_ssl_handshake(c) == NGX_AGAIN) {
return;
}
c->ssl->handler(c);
}
ssize_t
ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit)
{
u_char *last;
ssize_t n, bytes, size;
ngx_buf_t *b;
bytes = 0;
b = cl->buf;
last = b->last;
for ( ;; ) {
size = b->end - last;
if (limit) {
if (bytes >= limit) {
return bytes;
}
if (bytes + size > limit) {
size = (ssize_t) (limit - bytes);
}
}
n = ngx_ssl_recv(c, last, size);
if (n > 0) {
last += n;
bytes += n;
if (last == b->end) {
cl = cl->next;
if (cl == NULL) {
return bytes;
}
b = cl->buf;
last = b->last;
}
continue;
}
if (bytes) {
if (n == 0 || n == NGX_ERROR) {
c->read->ready = 1;
}
return bytes;
}
return n;
}
}
ssize_t
ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size)
{
int n, bytes;
if (c->ssl->last == NGX_ERROR) {
c->read->error = 1;
return NGX_ERROR;
}
if (c->ssl->last == NGX_DONE) {
c->read->ready = 0;
c->read->eof = 1;
return 0;
}
bytes = 0;
ngx_ssl_clear_error(c->log);
/*
* SSL_read() may return data in parts, so try to read
* until SSL_read() would return no data
*/
for ( ;; ) {
n = SSL_read(c->ssl->connection, buf, size);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n);
if (n > 0) {
bytes += n;
}
c->ssl->last = ngx_ssl_handle_recv(c, n);
if (c->ssl->last == NGX_OK) {
size -= n;
if (size == 0) {
c->read->ready = 1;
return bytes;
}
buf += n;
continue;
}
if (bytes) {
if (c->ssl->last != NGX_AGAIN) {
c->read->ready = 1;
}
return bytes;
}
switch (c->ssl->last) {
case NGX_DONE:
c->read->ready = 0;
c->read->eof = 1;
return 0;
case NGX_ERROR:
c->read->error = 1;
/* fall through */
case NGX_AGAIN:
return c->ssl->last;
}
}
}
static ngx_int_t
ngx_ssl_handle_recv(ngx_connection_t *c, int n)
{
int sslerr;
ngx_err_t err;
if (c->ssl->renegotiation) {
/*
* disable renegotiation (CVE-2009-3555):
* OpenSSL (at least up to 0.9.8l) does not handle disabled
* renegotiation gracefully, so drop connection here
*/
ngx_log_error(NGX_LOG_NOTICE, c->log, 0, "SSL renegotiation disabled");
while (ERR_peek_error()) {
ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0,
"ignoring stale global SSL error");
}
ERR_clear_error();
c->ssl->no_wait_shutdown = 1;
c->ssl->no_send_shutdown = 1;
return NGX_ERROR;
}
if (n > 0) {
if (c->ssl->saved_write_handler) {
c->write->handler = c->ssl->saved_write_handler;
c->ssl->saved_write_handler = NULL;
c->write->ready = 1;
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
ngx_post_event(c->write, &ngx_posted_events);
}
return NGX_OK;
}
sslerr = SSL_get_error(c->ssl->connection, n);
err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
if (sslerr == SSL_ERROR_WANT_READ) {
c->read->ready = 0;
return NGX_AGAIN;
}
if (sslerr == SSL_ERROR_WANT_WRITE) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"peer started SSL renegotiation");
c->write->ready = 0;
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
/*
* we do not set the timer because there is already the read event timer
*/
if (c->ssl->saved_write_handler == NULL) {
c->ssl->saved_write_handler = c->write->handler;
c->write->handler = ngx_ssl_write_handler;
}
return NGX_AGAIN;
}
c->ssl->no_wait_shutdown = 1;
c->ssl->no_send_shutdown = 1;
if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"peer shutdown SSL cleanly");
return NGX_DONE;
}
ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed");
return NGX_ERROR;
}
static void
ngx_ssl_write_handler(ngx_event_t *wev)
{
ngx_connection_t *c;
c = wev->data;
c->read->handler(c->read);
}
/*
* OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer
* before the SSL_write() call to decrease a SSL overhead.
*
* Besides for protocols such as HTTP it is possible to always buffer
* the output to decrease a SSL overhead some more.
*/
ngx_chain_t *
ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
{
int n;
ngx_uint_t flush;
ssize_t send, size;
ngx_buf_t *buf;
if (!c->ssl->buffer) {
while (in) {
if (ngx_buf_special(in->buf)) {
in = in->next;
continue;
}
n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos);
if (n == NGX_ERROR) {
return NGX_CHAIN_ERROR;
}
if (n == NGX_AGAIN) {
return in;
}
in->buf->pos += n;
if (in->buf->pos == in->buf->last) {
in = in->next;
}
}
return in;
}
/* the maximum limit size is the maximum int32_t value - the page size */
if (limit == 0 || limit > (off_t) (NGX_MAX_INT32_VALUE - ngx_pagesize)) {
limit = NGX_MAX_INT32_VALUE - ngx_pagesize;
}
buf = c->ssl->buf;
if (buf == NULL) {
buf = ngx_create_temp_buf(c->pool, c->ssl->buffer_size);
if (buf == NULL) {
return NGX_CHAIN_ERROR;
}
c->ssl->buf = buf;
}
if (buf->start == NULL) {
buf->start = ngx_palloc(c->pool, c->ssl->buffer_size);
if (buf->start == NULL) {
return NGX_CHAIN_ERROR;
}
buf->pos = buf->start;
buf->last = buf->start;
buf->end = buf->start + c->ssl->buffer_size;
}
send = buf->last - buf->pos;
flush = (in == NULL) ? 1 : buf->flush;
for ( ;; ) {
/* Dynamic record resizing:
We want the initial records to fit into one TCP segment
so we don't get TCP HoL blocking due to TCP Slow Start.
A connection always starts with small records, but after
a given amount of records sent, we make the records larger
to reduce header overhead.
After a connection has idled for a given timeout, begin
the process from the start. The actual parameters are
configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */
if (c->ssl->dyn_rec.timeout > 0 ) {
if (ngx_current_msec - c->ssl->dyn_rec_last_write >
c->ssl->dyn_rec.timeout)
{
buf->end = buf->start + c->ssl->dyn_rec.size_lo;
c->ssl->dyn_rec_records_sent = 0;
} else {
if (c->ssl->dyn_rec_records_sent >
c->ssl->dyn_rec.threshold * 2)
{
buf->end = buf->start + c->ssl->buffer_size;
} else if (c->ssl->dyn_rec_records_sent >
c->ssl->dyn_rec.threshold)
{
buf->end = buf->start + c->ssl->dyn_rec.size_hi;
} else {
buf->end = buf->start + c->ssl->dyn_rec.size_lo;
}
}
}
while (in && buf->last < buf->end && send < limit) {
if (in->buf->last_buf || in->buf->flush) {
flush = 1;
}
if (ngx_buf_special(in->buf)) {
in = in->next;
continue;
}
size = in->buf->last - in->buf->pos;
if (size > buf->end - buf->last) {
size = buf->end - buf->last;
}
if (send + size > limit) {
size = (ssize_t) (limit - send);
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL buf copy: %z", size);
ngx_memcpy(buf->last, in->buf->pos, size);
buf->last += size;
in->buf->pos += size;
send += size;
if (in->buf->pos == in->buf->last) {
in = in->next;
}
}
if (!flush && send < limit && buf->last < buf->end) {
break;
}
size = buf->last - buf->pos;
if (size == 0) {
buf->flush = 0;
c->buffered &= ~NGX_SSL_BUFFERED;
return in;
}
n = ngx_ssl_write(c, buf->pos, size);
if (n == NGX_ERROR) {
return NGX_CHAIN_ERROR;
}
if (n == NGX_AGAIN) {
break;
}
buf->pos += n;
if (n < size) {
break;
}
flush = 0;
buf->pos = buf->start;
buf->last = buf->start;
if (in == NULL || send == limit) {
break;
}
}
buf->flush = flush;
if (buf->pos < buf->last) {
c->buffered |= NGX_SSL_BUFFERED;
} else {
c->buffered &= ~NGX_SSL_BUFFERED;
}
return in;
}
ssize_t
ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
{
int n, sslerr;
ngx_err_t err;
ngx_ssl_clear_error(c->log);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %uz", size);
n = SSL_write(c->ssl->connection, data, size);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n);
if (n > 0) {
c->ssl->dyn_rec_records_sent++;
c->ssl->dyn_rec_last_write = ngx_current_msec;
if (c->ssl->saved_read_handler) {
c->read->handler = c->ssl->saved_read_handler;
c->ssl->saved_read_handler = NULL;
c->read->ready = 1;
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
ngx_post_event(c->read, &ngx_posted_events);
}
c->sent += n;
return n;
}
sslerr = SSL_get_error(c->ssl->connection, n);
err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
if (sslerr == SSL_ERROR_WANT_WRITE) {
c->write->ready = 0;
return NGX_AGAIN;
}
if (sslerr == SSL_ERROR_WANT_READ) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"peer started SSL renegotiation");
c->read->ready = 0;
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
/*
* we do not set the timer because there is already
* the write event timer
*/
if (c->ssl->saved_read_handler == NULL) {
c->ssl->saved_read_handler = c->read->handler;
c->read->handler = ngx_ssl_read_handler;
}
return NGX_AGAIN;
}
c->ssl->no_wait_shutdown = 1;
c->ssl->no_send_shutdown = 1;
c->write->error = 1;
ngx_ssl_connection_error(c, sslerr, err, "SSL_write() failed");
return NGX_ERROR;
}
static void
ngx_ssl_read_handler(ngx_event_t *rev)
{
ngx_connection_t *c;
c = rev->data;
c->write->handler(c->write);
}
void
ngx_ssl_free_buffer(ngx_connection_t *c)
{
if (c->ssl->buf && c->ssl->buf->start) {
if (ngx_pfree(c->pool, c->ssl->buf->start) == NGX_OK) {
c->ssl->buf->start = NULL;
}
}
}
ngx_int_t
ngx_ssl_shutdown(ngx_connection_t *c)
{
int n, sslerr, mode;
ngx_err_t err;
if (SSL_in_init(c->ssl->connection)) {
/*
* OpenSSL 1.0.2f complains if SSL_shutdown() is called during
* an SSL handshake, while previous versions always return 0.
* Avoid calling SSL_shutdown() if handshake wasn't completed.
*/
SSL_free(c->ssl->connection);
c->ssl = NULL;
return NGX_OK;
}
if (c->timedout) {
mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
SSL_set_quiet_shutdown(c->ssl->connection, 1);
} else {
mode = SSL_get_shutdown(c->ssl->connection);
if (c->ssl->no_wait_shutdown) {
mode |= SSL_RECEIVED_SHUTDOWN;
}
if (c->ssl->no_send_shutdown) {
mode |= SSL_SENT_SHUTDOWN;
}
if (c->ssl->no_wait_shutdown && c->ssl->no_send_shutdown) {
SSL_set_quiet_shutdown(c->ssl->connection, 1);
}
}
SSL_set_shutdown(c->ssl->connection, mode);
ngx_ssl_clear_error(c->log);
n = SSL_shutdown(c->ssl->connection);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
sslerr = 0;
/* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */
if (n != 1 && ERR_peek_error()) {
sslerr = SSL_get_error(c->ssl->connection, n);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL_get_error: %d", sslerr);
}
if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) {
SSL_free(c->ssl->connection);
c->ssl = NULL;
return NGX_OK;
}
if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {
c->read->handler = ngx_ssl_shutdown_handler;
c->write->handler = ngx_ssl_shutdown_handler;
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
return NGX_ERROR;
}
if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
return NGX_ERROR;
}
if (sslerr == SSL_ERROR_WANT_READ) {
ngx_add_timer(c->read, 30000);
}
return NGX_AGAIN;
}
err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed");
SSL_free(c->ssl->connection);
c->ssl = NULL;
return NGX_ERROR;
}
static void
ngx_ssl_shutdown_handler(ngx_event_t *ev)
{
ngx_connection_t *c;
ngx_connection_handler_pt handler;
c = ev->data;
handler = c->ssl->handler;
if (ev->timedout) {
c->timedout = 1;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "SSL shutdown handler");
if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
return;
}
handler(c);
}
static void
ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
char *text)
{
int n;
ngx_uint_t level;
level = NGX_LOG_CRIT;
if (sslerr == SSL_ERROR_SYSCALL) {
if (err == NGX_ECONNRESET
|| err == NGX_EPIPE
|| err == NGX_ENOTCONN
|| err == NGX_ETIMEDOUT
|| err == NGX_ECONNREFUSED
|| err == NGX_ENETDOWN
|| err == NGX_ENETUNREACH
|| err == NGX_EHOSTDOWN
|| err == NGX_EHOSTUNREACH)
{
switch (c->log_error) {
case NGX_ERROR_IGNORE_ECONNRESET:
case NGX_ERROR_INFO:
level = NGX_LOG_INFO;
break;
case NGX_ERROR_ERR:
level = NGX_LOG_ERR;
break;
default:
break;
}
}
} else if (sslerr == SSL_ERROR_SSL) {
n = ERR_GET_REASON(ERR_peek_error());
/* handshake failures */
if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC /* 103 */
|| n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */
|| n == SSL_R_DIGEST_CHECK_FAILED /* 149 */
|| n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */
|| n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */
|| n == SSL_R_LENGTH_MISMATCH /* 159 */
#ifdef SSL_R_NO_CIPHERS_PASSED
|| n == SSL_R_NO_CIPHERS_PASSED /* 182 */
#endif
|| n == SSL_R_NO_CIPHERS_SPECIFIED /* 183 */
|| n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */
|| n == SSL_R_NO_SHARED_CIPHER /* 193 */
|| n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */
#ifdef SSL_R_PARSE_TLSEXT
|| n == SSL_R_PARSE_TLSEXT /* 227 */
#endif
|| n == SSL_R_UNEXPECTED_MESSAGE /* 244 */
|| n == SSL_R_UNEXPECTED_RECORD /* 245 */
|| n == SSL_R_UNKNOWN_ALERT_TYPE /* 246 */
|| n == SSL_R_UNKNOWN_PROTOCOL /* 252 */
|| n == SSL_R_WRONG_VERSION_NUMBER /* 267 */
|| n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */
#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
|| n == SSL_R_RENEGOTIATE_EXT_TOO_LONG /* 335 */
|| n == SSL_R_RENEGOTIATION_ENCODING_ERR /* 336 */
|| n == SSL_R_RENEGOTIATION_MISMATCH /* 337 */
#endif
#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
|| n == SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED /* 338 */
#endif
#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING
|| n == SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING /* 345 */
#endif
#ifdef SSL_R_INAPPROPRIATE_FALLBACK
|| n == SSL_R_INAPPROPRIATE_FALLBACK /* 373 */
#endif
|| n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
#ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE
|| n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */
|| n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC /* 1020 */
|| n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED /* 1021 */
|| n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW /* 1022 */
|| n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE /* 1030 */
|| n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE /* 1040 */
|| n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE /* 1041 */
|| n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE /* 1042 */
|| n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE /* 1043 */
|| n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED /* 1044 */
|| n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED /* 1045 */
|| n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN /* 1046 */
|| n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER /* 1047 */
|| n == SSL_R_TLSV1_ALERT_UNKNOWN_CA /* 1048 */
|| n == SSL_R_TLSV1_ALERT_ACCESS_DENIED /* 1049 */
|| n == SSL_R_TLSV1_ALERT_DECODE_ERROR /* 1050 */
|| n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR /* 1051 */
|| n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION /* 1060 */
|| n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION /* 1070 */
|| n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY /* 1071 */
|| n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR /* 1080 */
|| n == SSL_R_TLSV1_ALERT_USER_CANCELLED /* 1090 */
|| n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION /* 1100 */
#endif
)
{
switch (c->log_error) {
case NGX_ERROR_IGNORE_ECONNRESET:
case NGX_ERROR_INFO:
level = NGX_LOG_INFO;
break;
case NGX_ERROR_ERR:
level = NGX_LOG_ERR;
break;
default:
break;
}
}
}
ngx_ssl_error(level, c->log, err, text);
}
static void
ngx_ssl_clear_error(ngx_log_t *log)
{
while (ERR_peek_error()) {
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "ignoring stale global SSL error");
}
ERR_clear_error();
}
void ngx_cdecl
ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
{
int flags;
u_long n;
va_list args;
u_char *p, *last;
u_char errstr[NGX_MAX_CONF_ERRSTR];
const char *data;
last = errstr + NGX_MAX_CONF_ERRSTR;
va_start(args, fmt);
p = ngx_vslprintf(errstr, last - 1, fmt, args);
va_end(args);
p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
for ( ;; ) {
n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
if (n == 0) {
break;
}
/* ERR_error_string_n() requires at least one byte */
if (p >= last - 1) {
goto next;
}
*p++ = ' ';
ERR_error_string_n(n, (char *) p, last - p);
while (p < last && *p) {
p++;
}
if (p < last && *data && (flags & ERR_TXT_STRING)) {
*p++ = ':';
p = ngx_cpystrn(p, (u_char *) data, last - p);
}
next:
(void) ERR_get_error();
}
ngx_log_error(level, log, err, "%*s)", p - errstr, errstr);
}
ngx_int_t
ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout)
{
long cache_mode;
SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
if (ngx_ssl_session_id_context(ssl, sess_ctx) != NGX_OK) {
return NGX_ERROR;
}
if (builtin_session_cache == NGX_SSL_NO_SCACHE) {
SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF);
return NGX_OK;
}
if (builtin_session_cache == NGX_SSL_NONE_SCACHE) {
/*
* If the server explicitly says that it does not support
* session reuse (see SSL_SESS_CACHE_OFF above), then
* Outlook Express fails to upload a sent email to
* the Sent Items folder on the IMAP server via a separate IMAP
* connection in the background. Therefore we have a special
* mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE)
* where the server pretends that it supports session reuse,
* but it does not actually store any session.
*/
SSL_CTX_set_session_cache_mode(ssl->ctx,
SSL_SESS_CACHE_SERVER
|SSL_SESS_CACHE_NO_AUTO_CLEAR
|SSL_SESS_CACHE_NO_INTERNAL_STORE);
SSL_CTX_sess_set_cache_size(ssl->ctx, 1);
return NGX_OK;
}
cache_mode = SSL_SESS_CACHE_SERVER;
if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) {
cache_mode |= SSL_SESS_CACHE_NO_INTERNAL;
}
SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode);
if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) {
if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) {
SSL_CTX_sess_set_cache_size(ssl->ctx, builtin_session_cache);
}
}
if (shm_zone) {
SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);
SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);
SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx)
{
int n, i;
X509 *cert;
X509_NAME *name;
EVP_MD_CTX *md;
unsigned int len;
STACK_OF(X509_NAME) *list;
u_char buf[EVP_MAX_MD_SIZE];
/*
* Session ID context is set based on the string provided,
* the server certificates, and the client CA list.
*/
md = EVP_MD_CTX_create();
if (md == NULL) {
return NGX_ERROR;
}
if (EVP_DigestInit_ex(md, EVP_sha1(), NULL) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"EVP_DigestInit_ex() failed");
goto failed;
}
if (EVP_DigestUpdate(md, sess_ctx->data, sess_ctx->len) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"EVP_DigestUpdate() failed");
goto failed;
}
for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
cert;
cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
{
if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"X509_digest() failed");
goto failed;
}
if (EVP_DigestUpdate(md, buf, len) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"EVP_DigestUpdate() failed");
goto failed;
}
}
list = SSL_CTX_get_client_CA_list(ssl->ctx);
if (list != NULL) {
n = sk_X509_NAME_num(list);
for (i = 0; i < n; i++) {
name = sk_X509_NAME_value(list, i);
if (X509_NAME_digest(name, EVP_sha1(), buf, &len) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"X509_NAME_digest() failed");
goto failed;
}
if (EVP_DigestUpdate(md, buf, len) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"EVP_DigestUpdate() failed");
goto failed;
}
}
}
if (EVP_DigestFinal_ex(md, buf, &len) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"EVP_DigestUpdate() failed");
goto failed;
}
EVP_MD_CTX_destroy(md);
if (SSL_CTX_set_session_id_context(ssl->ctx, buf, len) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_session_id_context() failed");
return NGX_ERROR;
}
return NGX_OK;
failed:
EVP_MD_CTX_destroy(md);
return NGX_ERROR;
}
ngx_int_t
ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
{
size_t len;
ngx_slab_pool_t *shpool;
ngx_ssl_session_cache_t *cache;
if (data) {
shm_zone->data = data;
return NGX_OK;
}
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
if (shm_zone->shm.exists) {
shm_zone->data = shpool->data;
return NGX_OK;
}
cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));
if (cache == NULL) {
return NGX_ERROR;
}
shpool->data = cache;
shm_zone->data = cache;
ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,
ngx_ssl_session_rbtree_insert_value);
ngx_queue_init(&cache->expire_queue);
len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;
shpool->log_ctx = ngx_slab_alloc(shpool, len);
if (shpool->log_ctx == NULL) {
return NGX_ERROR;
}
ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",
&shm_zone->shm.name);
shpool->log_nomem = 0;
return NGX_OK;
}
/*
* The length of the session id is 16 bytes for SSLv2 sessions and
* between 1 and 32 bytes for SSLv3/TLSv1, typically 32 bytes.
* It seems that the typical length of the external ASN1 representation
* of a session is 118 or 119 bytes for SSLv3/TSLv1.
*
* Thus on 32-bit platforms we allocate separately an rbtree node,
* a session id, and an ASN1 representation, they take accordingly
* 64, 32, and 128 bytes.
*
* On 64-bit platforms we allocate separately an rbtree node + session_id,
* and an ASN1 representation, they take accordingly 128 and 128 bytes.
*
* OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,
* so they are outside the code locked by shared pool mutex
*/
static int
ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
{
int len;
u_char *p, *id, *cached_sess, *session_id;
uint32_t hash;
SSL_CTX *ssl_ctx;
unsigned int session_id_length;
ngx_shm_zone_t *shm_zone;
ngx_connection_t *c;
ngx_slab_pool_t *shpool;
ngx_ssl_sess_id_t *sess_id;
ngx_ssl_session_cache_t *cache;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
len = i2d_SSL_SESSION(sess, NULL);
/* do not cache too big session */
if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {
return 0;
}
p = buf;
i2d_SSL_SESSION(sess, &p);
c = ngx_ssl_get_connection(ssl_conn);
ssl_ctx = c->ssl->session_ctx;
shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
cache = shm_zone->data;
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
ngx_shmtx_lock(&shpool->mutex);
/* drop one or two expired sessions */
ngx_ssl_expire_sessions(cache, shpool, 1);
cached_sess = ngx_slab_alloc_locked(shpool, len);
if (cached_sess == NULL) {
/* drop the oldest non-expired session and try once more */
ngx_ssl_expire_sessions(cache, shpool, 0);
cached_sess = ngx_slab_alloc_locked(shpool, len);
if (cached_sess == NULL) {
sess_id = NULL;
goto failed;
}
}
sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
if (sess_id == NULL) {
/* drop the oldest non-expired session and try once more */
ngx_ssl_expire_sessions(cache, shpool, 0);
sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
if (sess_id == NULL) {
goto failed;
}
}
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
#else
session_id = sess->session_id;
session_id_length = sess->session_id_length;
#endif
#if (NGX_PTR_SIZE == 8)
id = sess_id->sess_id;
#else
id = ngx_slab_alloc_locked(shpool, session_id_length);
if (id == NULL) {
/* drop the oldest non-expired session and try once more */
ngx_ssl_expire_sessions(cache, shpool, 0);
id = ngx_slab_alloc_locked(shpool, session_id_length);
if (id == NULL) {
goto failed;
}
}
#endif
ngx_memcpy(cached_sess, buf, len);
ngx_memcpy(id, session_id, session_id_length);
hash = ngx_crc32_short(session_id, session_id_length);
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl new session: %08XD:%ud:%d",
hash, session_id_length, len);
sess_id->node.key = hash;
sess_id->node.data = (u_char) session_id_length;
sess_id->id = id;
sess_id->len = len;
sess_id->session = cached_sess;
sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);
ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue);
ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node);
ngx_shmtx_unlock(&shpool->mutex);
return 0;
failed:
if (cached_sess) {
ngx_slab_free_locked(shpool, cached_sess);
}
if (sess_id) {
ngx_slab_free_locked(shpool, sess_id);
}
ngx_shmtx_unlock(&shpool->mutex);
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
"could not allocate new session%s", shpool->log_ctx);
return 0;
}
static ngx_ssl_session_t *
ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
#if OPENSSL_VERSION_NUMBER >= 0x10100003L
const
#endif
u_char *id, int len, int *copy)
{
#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
const
#endif
u_char *p;
uint32_t hash;
ngx_int_t rc;
ngx_shm_zone_t *shm_zone;
ngx_slab_pool_t *shpool;
ngx_rbtree_node_t *node, *sentinel;
ngx_ssl_session_t *sess;
ngx_ssl_sess_id_t *sess_id;
ngx_ssl_session_cache_t *cache;
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
ngx_connection_t *c;
hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len);
*copy = 0;
c = ngx_ssl_get_connection(ssl_conn);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl get session: %08XD:%d", hash, len);
shm_zone = SSL_CTX_get_ex_data(c->ssl->session_ctx,
ngx_ssl_session_cache_index);
cache = shm_zone->data;
sess = NULL;
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
ngx_shmtx_lock(&shpool->mutex);
node = cache->session_rbtree.root;
sentinel = cache->session_rbtree.sentinel;
while (node != sentinel) {
if (hash < node->key) {
node = node->left;
continue;
}
if (hash > node->key) {
node = node->right;
continue;
}
/* hash == node->key */
sess_id = (ngx_ssl_sess_id_t *) node;
rc = ngx_memn2cmp((u_char *) (uintptr_t) id, sess_id->id,
(size_t) len, (size_t) node->data);
if (rc == 0) {
if (sess_id->expire > ngx_time()) {
ngx_memcpy(buf, sess_id->session, sess_id->len);
ngx_shmtx_unlock(&shpool->mutex);
p = buf;
sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
return sess;
}
ngx_queue_remove(&sess_id->queue);
ngx_rbtree_delete(&cache->session_rbtree, node);
ngx_slab_free_locked(shpool, sess_id->session);
#if (NGX_PTR_SIZE == 4)
ngx_slab_free_locked(shpool, sess_id->id);
#endif
ngx_slab_free_locked(shpool, sess_id);
sess = NULL;
goto done;
}
node = (rc < 0) ? node->left : node->right;
}
done:
ngx_shmtx_unlock(&shpool->mutex);
return sess;
}
void
ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
{
SSL_CTX_remove_session(ssl, sess);
ngx_ssl_remove_session(ssl, sess);
}
static void
ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
{
u_char *id;
uint32_t hash;
ngx_int_t rc;
unsigned int len;
ngx_shm_zone_t *shm_zone;
ngx_slab_pool_t *shpool;
ngx_rbtree_node_t *node, *sentinel;
ngx_ssl_sess_id_t *sess_id;
ngx_ssl_session_cache_t *cache;
shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);
if (shm_zone == NULL) {
return;
}
cache = shm_zone->data;
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
id = (u_char *) SSL_SESSION_get_id(sess, &len);
#else
id = sess->session_id;
len = sess->session_id_length;
#endif
hash = ngx_crc32_short(id, len);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
"ssl remove session: %08XD:%ud", hash, len);
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
ngx_shmtx_lock(&shpool->mutex);
node = cache->session_rbtree.root;
sentinel = cache->session_rbtree.sentinel;
while (node != sentinel) {
if (hash < node->key) {
node = node->left;
continue;
}
if (hash > node->key) {
node = node->right;
continue;
}
/* hash == node->key */
sess_id = (ngx_ssl_sess_id_t *) node;
rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);
if (rc == 0) {
ngx_queue_remove(&sess_id->queue);
ngx_rbtree_delete(&cache->session_rbtree, node);
ngx_slab_free_locked(shpool, sess_id->session);
#if (NGX_PTR_SIZE == 4)
ngx_slab_free_locked(shpool, sess_id->id);
#endif
ngx_slab_free_locked(shpool, sess_id);
goto done;
}
node = (rc < 0) ? node->left : node->right;
}
done:
ngx_shmtx_unlock(&shpool->mutex);
}
static void
ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
ngx_slab_pool_t *shpool, ngx_uint_t n)
{
time_t now;
ngx_queue_t *q;
ngx_ssl_sess_id_t *sess_id;
now = ngx_time();
while (n < 3) {
if (ngx_queue_empty(&cache->expire_queue)) {
return;
}
q = ngx_queue_last(&cache->expire_queue);
sess_id = ngx_queue_data(q, ngx_ssl_sess_id_t, queue);
if (n++ != 0 && sess_id->expire > now) {
return;
}
ngx_queue_remove(q);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
"expire session: %08Xi", sess_id->node.key);
ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node);
ngx_slab_free_locked(shpool, sess_id->session);
#if (NGX_PTR_SIZE == 4)
ngx_slab_free_locked(shpool, sess_id->id);
#endif
ngx_slab_free_locked(shpool, sess_id);
}
}
static void
ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
ngx_rbtree_node_t **p;
ngx_ssl_sess_id_t *sess_id, *sess_id_temp;
for ( ;; ) {
if (node->key < temp->key) {
p = &temp->left;
} else if (node->key > temp->key) {
p = &temp->right;
} else { /* node->key == temp->key */
sess_id = (ngx_ssl_sess_id_t *) node;
sess_id_temp = (ngx_ssl_sess_id_t *) temp;
p = (ngx_memn2cmp(sess_id->id, sess_id_temp->id,
(size_t) node->data, (size_t) temp->data)
< 0) ? &temp->left : &temp->right;
}
if (*p == sentinel) {
break;
}
temp = *p;
}
*p = node;
node->parent = temp;
node->left = sentinel;
node->right = sentinel;
ngx_rbt_red(node);
}
#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
ngx_int_t
ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
{
u_char buf[80];
size_t size;
ssize_t n;
ngx_str_t *path;
ngx_file_t file;
ngx_uint_t i;
ngx_array_t *keys;
ngx_file_info_t fi;
ngx_ssl_session_ticket_key_t *key;
if (paths == NULL) {
return NGX_OK;
}
keys = ngx_array_create(cf->pool, paths->nelts,
sizeof(ngx_ssl_session_ticket_key_t));
if (keys == NULL) {
return NGX_ERROR;
}
path = paths->elts;
for (i = 0; i < paths->nelts; i++) {
if (ngx_conf_full_name(cf->cycle, &path[i], 1) != NGX_OK) {
return NGX_ERROR;
}
ngx_memzero(&file, sizeof(ngx_file_t));
file.name = path[i];
file.log = cf->log;
file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
NGX_FILE_OPEN, 0);
if (file.fd == NGX_INVALID_FILE) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
ngx_open_file_n " \"%V\" failed", &file.name);
return NGX_ERROR;
}
if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_fd_info_n " \"%V\" failed", &file.name);
goto failed;
}
size = ngx_file_size(&fi);
if (size != 48 && size != 80) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" must be 48 or 80 bytes", &file.name);
goto failed;
}
n = ngx_read_file(&file, buf, size, 0);
if (n == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
ngx_read_file_n " \"%V\" failed", &file.name);
goto failed;
}
if ((size_t) n != size) {
ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
ngx_read_file_n " \"%V\" returned only "
"%z bytes instead of %uz", &file.name, n, size);
goto failed;
}
key = ngx_array_push(keys);
if (key == NULL) {
goto failed;
}
if (size == 48) {
key->size = 48;
ngx_memcpy(key->name, buf, 16);
ngx_memcpy(key->aes_key, buf + 16, 16);
ngx_memcpy(key->hmac_key, buf + 32, 16);
} else {
key->size = 80;
ngx_memcpy(key->name, buf, 16);
ngx_memcpy(key->hmac_key, buf + 16, 32);
ngx_memcpy(key->aes_key, buf + 48, 32);
}
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%V\" failed", &file.name);
}
}
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_ticket_keys_index, keys)
== 0)
{
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_set_ex_data() failed");
return NGX_ERROR;
}
if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx,
ngx_ssl_session_ticket_key_callback)
== 0)
{
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
"nginx was built with Session Tickets support, however, "
"now it is linked dynamically to an OpenSSL library "
"which has no tlsext support, therefore Session Tickets "
"are not available");
}
return NGX_OK;
failed:
if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
ngx_close_file_n " \"%V\" failed", &file.name);
}
return NGX_ERROR;
}
static int
ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
HMAC_CTX *hctx, int enc)
{
size_t size;
SSL_CTX *ssl_ctx;
ngx_uint_t i;
ngx_array_t *keys;
ngx_connection_t *c;
ngx_ssl_session_ticket_key_t *key;
const EVP_MD *digest;
const EVP_CIPHER *cipher;
#if (NGX_DEBUG)
u_char buf[32];
#endif
c = ngx_ssl_get_connection(ssl_conn);
ssl_ctx = c->ssl->session_ctx;
#ifdef OPENSSL_NO_SHA256
digest = EVP_sha1();
#else
digest = EVP_sha256();
#endif
keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_ticket_keys_index);
if (keys == NULL) {
return -1;
}
key = keys->elts;
if (enc == 1) {
/* encrypt session ticket */
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl session ticket encrypt, key: \"%*s\" (%s session)",
ngx_hex_dump(buf, key[0].name, 16) - buf, buf,
SSL_session_reused(ssl_conn) ? "reused" : "new");
if (key[0].size == 48) {
cipher = EVP_aes_128_cbc();
size = 16;
} else {
cipher = EVP_aes_256_cbc();
size = 32;
}
if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "RAND_bytes() failed");
return -1;
}
if (EVP_EncryptInit_ex(ectx, cipher, NULL, key[0].aes_key, iv) != 1) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"EVP_EncryptInit_ex() failed");
return -1;
}
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL) != 1) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed");
return -1;
}
#else
HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL);
#endif
ngx_memcpy(name, key[0].name, 16);
return 1;
} else {
/* decrypt session ticket */
for (i = 0; i < keys->nelts; i++) {
if (ngx_memcmp(name, key[i].name, 16) == 0) {
goto found;
}
}
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl session ticket decrypt, key: \"%*s\" not found",
ngx_hex_dump(buf, name, 16) - buf, buf);
return 0;
found:
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
"ssl session ticket decrypt, key: \"%*s\"%s",
ngx_hex_dump(buf, key[i].name, 16) - buf, buf,
(i == 0) ? " (default)" : "");
if (key[i].size == 48) {
cipher = EVP_aes_128_cbc();
size = 16;
} else {
cipher = EVP_aes_256_cbc();
size = 32;
}
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
if (HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL) != 1) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "HMAC_Init_ex() failed");
return -1;
}
#else
HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL);
#endif
if (EVP_DecryptInit_ex(ectx, cipher, NULL, key[i].aes_key, iv) != 1) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
"EVP_DecryptInit_ex() failed");
return -1;
}
return (i == 0) ? 1 : 2 /* renew */;
}
}
#else
ngx_int_t
ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
{
if (paths) {
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_session_ticket_key\" ignored, not supported");
}
return NGX_OK;
}
#endif
void
ngx_ssl_cleanup_ctx(void *data)
{
ngx_ssl_t *ssl = data;
X509 *cert, *next;
cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
while (cert) {
next = X509_get_ex_data(cert, ngx_ssl_next_certificate_index);
X509_free(cert);
cert = next;
}
SSL_CTX_free(ssl->ctx);
}
ngx_int_t
ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name)
{
X509 *cert;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_ERROR;
}
#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
/* X509_check_host() is only available in OpenSSL 1.0.2+ */
if (name->len == 0) {
goto failed;
}
if (X509_check_host(cert, (char *) name->data, name->len, 0, NULL) != 1) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"X509_check_host(): no match");
goto failed;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"X509_check_host(): match");
goto found;
#else
{
int n, i;
X509_NAME *sname;
ASN1_STRING *str;
X509_NAME_ENTRY *entry;
GENERAL_NAME *altname;
STACK_OF(GENERAL_NAME) *altnames;
/*
* As per RFC6125 and RFC2818, we check subjectAltName extension,
* and if it's not present - commonName in Subject is checked.
*/
altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
if (altnames) {
n = sk_GENERAL_NAME_num(altnames);
for (i = 0; i < n; i++) {
altname = sk_GENERAL_NAME_value(altnames, i);
if (altname->type != GEN_DNS) {
continue;
}
str = altname->d.dNSName;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL subjectAltName: \"%*s\"",
ASN1_STRING_length(str), ASN1_STRING_data(str));
if (ngx_ssl_check_name(name, str) == NGX_OK) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL subjectAltName: match");
GENERAL_NAMES_free(altnames);
goto found;
}
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL subjectAltName: no match");
GENERAL_NAMES_free(altnames);
goto failed;
}
/*
* If there is no subjectAltName extension, check commonName
* in Subject. While RFC2818 requires to only check "most specific"
* CN, both Apache and OpenSSL check all CNs, and so do we.
*/
sname = X509_get_subject_name(cert);
if (sname == NULL) {
goto failed;
}
i = -1;
for ( ;; ) {
i = X509_NAME_get_index_by_NID(sname, NID_commonName, i);
if (i < 0) {
break;
}
entry = X509_NAME_get_entry(sname, i);
str = X509_NAME_ENTRY_get_data(entry);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL commonName: \"%*s\"",
ASN1_STRING_length(str), ASN1_STRING_data(str));
if (ngx_ssl_check_name(name, str) == NGX_OK) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL commonName: match");
goto found;
}
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL commonName: no match");
}
#endif
failed:
X509_free(cert);
return NGX_ERROR;
found:
X509_free(cert);
return NGX_OK;
}
#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
static ngx_int_t
ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *pattern)
{
u_char *s, *p, *end;
size_t slen, plen;
s = name->data;
slen = name->len;
p = ASN1_STRING_data(pattern);
plen = ASN1_STRING_length(pattern);
if (slen == plen && ngx_strncasecmp(s, p, plen) == 0) {
return NGX_OK;
}
if (plen > 2 && p[0] == '*' && p[1] == '.') {
plen -= 1;
p += 1;
end = s + slen;
s = ngx_strlchr(s, end, '.');
if (s == NULL) {
return NGX_ERROR;
}
slen = end - s;
if (plen == slen && ngx_strncasecmp(s, p, plen) == 0) {
return NGX_OK;
}
}
return NGX_ERROR;
}
#endif
ngx_int_t
ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
s->data = (u_char *) SSL_get_version(c->ssl->connection);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
s->data = (u_char *) SSL_get_cipher_name(c->ssl->connection);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
#ifdef SSL_CTRL_GET_RAW_CIPHERLIST
int n, i, bytes;
size_t len;
u_char *ciphers, *p;
const SSL_CIPHER *cipher;
bytes = SSL_get0_raw_cipherlist(c->ssl->connection, NULL);
n = SSL_get0_raw_cipherlist(c->ssl->connection, &ciphers);
if (n <= 0) {
s->len = 0;
return NGX_OK;
}
len = 0;
n /= bytes;
for (i = 0; i < n; i++) {
cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes);
if (cipher) {
len += ngx_strlen(SSL_CIPHER_get_name(cipher));
} else {
len += sizeof("0x") - 1 + bytes * (sizeof("00") - 1);
}
len += sizeof(":") - 1;
}
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
return NGX_ERROR;
}
p = s->data;
for (i = 0; i < n; i++) {
cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes);
if (cipher) {
p = ngx_sprintf(p, "%s", SSL_CIPHER_get_name(cipher));
} else {
p = ngx_sprintf(p, "0x");
p = ngx_hex_dump(p, ciphers + i * bytes, bytes);
}
*p++ = ':';
}
p--;
s->len = p - s->data;
#else
u_char buf[4096];
if (SSL_get_shared_ciphers(c->ssl->connection, (char *) buf, 4096)
== NULL)
{
s->len = 0;
return NGX_OK;
}
s->len = ngx_strlen(buf);
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(s->data, buf, s->len);
#endif
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
#ifdef SSL_CTRL_GET_CURVES
int *curves, n, i, nid;
u_char *p;
size_t len;
n = SSL_get1_curves(c->ssl->connection, NULL);
if (n <= 0) {
s->len = 0;
return NGX_OK;
}
curves = ngx_palloc(pool, n * sizeof(int));
n = SSL_get1_curves(c->ssl->connection, curves);
len = 0;
for (i = 0; i < n; i++) {
nid = curves[i];
if (nid & TLSEXT_nid_unknown) {
len += sizeof("0x0000") - 1;
} else {
len += ngx_strlen(OBJ_nid2sn(nid));
}
len += sizeof(":") - 1;
}
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
return NGX_ERROR;
}
p = s->data;
for (i = 0; i < n; i++) {
nid = curves[i];
if (nid & TLSEXT_nid_unknown) {
p = ngx_sprintf(p, "0x%04xd", nid & 0xffff);
} else {
p = ngx_sprintf(p, "%s", OBJ_nid2sn(nid));
}
*p++ = ':';
}
p--;
s->len = p - s->data;
#else
s->len = 0;
#endif
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
u_char *buf;
SSL_SESSION *sess;
unsigned int len;
sess = SSL_get0_session(c->ssl->connection);
if (sess == NULL) {
s->len = 0;
return NGX_OK;
}
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
buf = (u_char *) SSL_SESSION_get_id(sess, &len);
#else
buf = sess->session_id;
len = sess->session_id_length;
#endif
s->len = 2 * len;
s->data = ngx_pnalloc(pool, 2 * len);
if (s->data == NULL) {
return NGX_ERROR;
}
ngx_hex_dump(s->data, buf, len);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
if (SSL_session_reused(c->ssl->connection)) {
ngx_str_set(s, "r");
} else {
ngx_str_set(s, ".");
}
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
size_t len;
const char *name;
name = SSL_get_servername(c->ssl->connection, TLSEXT_NAMETYPE_host_name);
if (name) {
len = ngx_strlen(name);
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
return NGX_ERROR;
}
ngx_memcpy(s->data, name, len);
return NGX_OK;
}
#endif
s->len = 0;
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
size_t len;
BIO *bio;
X509 *cert;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
X509_free(cert);
return NGX_ERROR;
}
if (PEM_write_bio_X509(bio, cert) == 0) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed");
goto failed;
}
len = BIO_pending(bio);
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
goto failed;
}
BIO_read(bio, s->data, len);
BIO_free(bio);
X509_free(cert);
return NGX_OK;
failed:
BIO_free(bio);
X509_free(cert);
return NGX_ERROR;
}
ngx_int_t
ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
u_char *p;
size_t len;
ngx_uint_t i;
ngx_str_t cert;
if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {
return NGX_ERROR;
}
if (cert.len == 0) {
s->len = 0;
return NGX_OK;
}
len = cert.len - 1;
for (i = 0; i < cert.len - 1; i++) {
if (cert.data[i] == LF) {
len++;
}
}
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
return NGX_ERROR;
}
p = s->data;
for (i = 0; i < cert.len - 1; i++) {
*p++ = cert.data[i];
if (cert.data[i] == LF) {
*p++ = '\t';
}
}
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_escaped_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s)
{
ngx_str_t cert;
uintptr_t n;
if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {
return NGX_ERROR;
}
if (cert.len == 0) {
s->len = 0;
return NGX_OK;
}
n = ngx_escape_uri(NULL, cert.data, cert.len, NGX_ESCAPE_URI_COMPONENT);
s->len = cert.len + n * 2;
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
return NGX_ERROR;
}
ngx_escape_uri(s->data, cert.data, cert.len, NGX_ESCAPE_URI_COMPONENT);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
BIO *bio;
X509 *cert;
X509_NAME *name;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
name = X509_get_subject_name(cert);
if (name == NULL) {
return NGX_ERROR;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
X509_free(cert);
return NGX_ERROR;
}
if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) {
goto failed;
}
s->len = BIO_pending(bio);
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
goto failed;
}
BIO_read(bio, s->data, s->len);
BIO_free(bio);
X509_free(cert);
return NGX_OK;
failed:
BIO_free(bio);
X509_free(cert);
return NGX_ERROR;
}
ngx_int_t
ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
BIO *bio;
X509 *cert;
X509_NAME *name;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
name = X509_get_issuer_name(cert);
if (name == NULL) {
return NGX_ERROR;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
X509_free(cert);
return NGX_ERROR;
}
if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) {
goto failed;
}
s->len = BIO_pending(bio);
s->data = ngx_pnalloc(pool, s->len);
if (s->data == NULL) {
goto failed;
}
BIO_read(bio, s->data, s->len);
BIO_free(bio);
X509_free(cert);
return NGX_OK;
failed:
BIO_free(bio);
X509_free(cert);
return NGX_ERROR;
}
ngx_int_t
ngx_ssl_get_subject_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s)
{
char *p;
size_t len;
X509 *cert;
X509_NAME *name;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
name = X509_get_subject_name(cert);
if (name == NULL) {
X509_free(cert);
return NGX_ERROR;
}
p = X509_NAME_oneline(name, NULL, 0);
for (len = 0; p[len]; len++) { /* void */ }
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
OPENSSL_free(p);
X509_free(cert);
return NGX_ERROR;
}
ngx_memcpy(s->data, p, len);
OPENSSL_free(p);
X509_free(cert);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_issuer_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s)
{
char *p;
size_t len;
X509 *cert;
X509_NAME *name;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
name = X509_get_issuer_name(cert);
if (name == NULL) {
X509_free(cert);
return NGX_ERROR;
}
p = X509_NAME_oneline(name, NULL, 0);
for (len = 0; p[len]; len++) { /* void */ }
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
OPENSSL_free(p);
X509_free(cert);
return NGX_ERROR;
}
ngx_memcpy(s->data, p, len);
OPENSSL_free(p);
X509_free(cert);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
size_t len;
X509 *cert;
BIO *bio;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
X509_free(cert);
return NGX_ERROR;
}
i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert));
len = BIO_pending(bio);
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
BIO_free(bio);
X509_free(cert);
return NGX_ERROR;
}
BIO_read(bio, s->data, len);
BIO_free(bio);
X509_free(cert);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
X509 *cert;
unsigned int len;
u_char buf[EVP_MAX_MD_SIZE];
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
if (!X509_digest(cert, EVP_sha1(), buf, &len)) {
X509_free(cert);
return NGX_ERROR;
}
s->len = 2 * len;
s->data = ngx_pnalloc(pool, 2 * len);
if (s->data == NULL) {
X509_free(cert);
return NGX_ERROR;
}
ngx_hex_dump(s->data, buf, len);
X509_free(cert);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
X509 *cert;
long rc;
const char *str;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
ngx_str_set(s, "NONE");
return NGX_OK;
}
X509_free(cert);
rc = SSL_get_verify_result(c->ssl->connection);
if (rc == X509_V_OK) {
ngx_str_set(s, "SUCCESS");
return NGX_OK;
}
str = X509_verify_cert_error_string(rc);
s->data = ngx_pnalloc(pool, sizeof("FAILED:") - 1 + ngx_strlen(str));
if (s->data == NULL) {
return NGX_ERROR;
}
s->len = ngx_sprintf(s->data, "FAILED:%s", str) - s->data;
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
BIO *bio;
X509 *cert;
size_t len;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
X509_free(cert);
return NGX_ERROR;
}
#if OPENSSL_VERSION_NUMBER > 0x10100000L
ASN1_TIME_print(bio, X509_get0_notBefore(cert));
#else
ASN1_TIME_print(bio, X509_get_notBefore(cert));
#endif
len = BIO_pending(bio);
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
BIO_free(bio);
X509_free(cert);
return NGX_ERROR;
}
BIO_read(bio, s->data, len);
BIO_free(bio);
X509_free(cert);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
BIO *bio;
X509 *cert;
size_t len;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
X509_free(cert);
return NGX_ERROR;
}
#if OPENSSL_VERSION_NUMBER > 0x10100000L
ASN1_TIME_print(bio, X509_get0_notAfter(cert));
#else
ASN1_TIME_print(bio, X509_get_notAfter(cert));
#endif
len = BIO_pending(bio);
s->len = len;
s->data = ngx_pnalloc(pool, len);
if (s->data == NULL) {
BIO_free(bio);
X509_free(cert);
return NGX_ERROR;
}
BIO_read(bio, s->data, len);
BIO_free(bio);
X509_free(cert);
return NGX_OK;
}
ngx_int_t
ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
{
X509 *cert;
time_t now, end;
s->len = 0;
cert = SSL_get_peer_certificate(c->ssl->connection);
if (cert == NULL) {
return NGX_OK;
}
#if OPENSSL_VERSION_NUMBER > 0x10100000L
end = ngx_ssl_parse_time(X509_get0_notAfter(cert));
#else
end = ngx_ssl_parse_time(X509_get_notAfter(cert));
#endif
if (end == (time_t) NGX_ERROR) {
X509_free(cert);
return NGX_OK;
}
now = ngx_time();
if (end < now + 86400) {
ngx_str_set(s, "0");
X509_free(cert);
return NGX_OK;
}
s->data = ngx_pnalloc(pool, NGX_TIME_T_LEN);
if (s->data == NULL) {
X509_free(cert);
return NGX_ERROR;
}
s->len = ngx_sprintf(s->data, "%T", (end - now) / 86400) - s->data;
X509_free(cert);
return NGX_OK;
}
static time_t
ngx_ssl_parse_time(
#if OPENSSL_VERSION_NUMBER > 0x10100000L
const
#endif
ASN1_TIME *asn1time)
{
BIO *bio;
char *value;
size_t len;
time_t time;
/*
* OpenSSL doesn't provide a way to convert ASN1_TIME
* into time_t. To do this, we use ASN1_TIME_print(),
* which uses the "MMM DD HH:MM:SS YYYY [GMT]" format (e.g.,
* "Feb 3 00:55:52 2015 GMT"), and parse the result.
*/
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
return NGX_ERROR;
}
/* fake weekday prepended to match C asctime() format */
BIO_write(bio, "Tue ", sizeof("Tue ") - 1);
ASN1_TIME_print(bio, asn1time);
len = BIO_get_mem_data(bio, &value);
time = ngx_parse_http_time((u_char *) value, len);
BIO_free(bio);
return time;
}
static void *
ngx_openssl_create_conf(ngx_cycle_t *cycle)
{
ngx_openssl_conf_t *oscf;
oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t));
if (oscf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* oscf->engine = 0;
*/
return oscf;
}
static char *
ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
#ifndef OPENSSL_NO_ENGINE
ngx_openssl_conf_t *oscf = conf;
ENGINE *engine;
ngx_str_t *value;
if (oscf->engine) {
return "is duplicate";
}
oscf->engine = 1;
value = cf->args->elts;
engine = ENGINE_by_id((char *) value[1].data);
if (engine == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
"ENGINE_by_id(\"%V\") failed", &value[1]);
return NGX_CONF_ERROR;
}
if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
"ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed",
&value[1]);
ENGINE_free(engine);
return NGX_CONF_ERROR;
}
ENGINE_free(engine);
return NGX_CONF_OK;
#else
return "is not supported";
#endif
}
static void
ngx_openssl_exit(ngx_cycle_t *cycle)
{
#if OPENSSL_VERSION_NUMBER < 0x10100003L
EVP_cleanup();
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif
#endif
}
+276
View File
@@ -0,0 +1,276 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_OPENSSL_H_INCLUDED_
#define _NGX_EVENT_OPENSSL_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bn.h>
#include <openssl/conf.h>
#include <openssl/crypto.h>
#include <openssl/dh.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
#include <openssl/evp.h>
#include <openssl/hmac.h>
#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
#endif
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#define NGX_SSL_NAME "OpenSSL"
#if (defined LIBRESSL_VERSION_NUMBER && OPENSSL_VERSION_NUMBER == 0x20000000L)
#undef OPENSSL_VERSION_NUMBER
#define OPENSSL_VERSION_NUMBER 0x1000107fL
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10100001L)
#define ngx_ssl_version() OpenSSL_version(OPENSSL_VERSION)
#else
#define ngx_ssl_version() SSLeay_version(SSLEAY_VERSION)
#endif
#define ngx_ssl_session_t SSL_SESSION
#define ngx_ssl_conn_t SSL
typedef struct {
ngx_msec_t timeout;
ngx_uint_t threshold;
size_t size_lo;
size_t size_hi;
} ngx_ssl_dyn_rec_t;
#if (OPENSSL_VERSION_NUMBER < 0x10002000L)
#define SSL_is_server(s) (s)->server
#endif
struct ngx_ssl_s {
SSL_CTX *ctx;
ngx_log_t *log;
size_t buffer_size;
ngx_ssl_dyn_rec_t dyn_rec;
};
struct ngx_ssl_connection_s {
ngx_ssl_conn_t *connection;
SSL_CTX *session_ctx;
ngx_int_t last;
ngx_buf_t *buf;
size_t buffer_size;
ngx_connection_handler_pt handler;
ngx_event_handler_pt saved_read_handler;
ngx_event_handler_pt saved_write_handler;
unsigned handshaked:1;
unsigned renegotiation:1;
unsigned buffer:1;
unsigned no_wait_shutdown:1;
unsigned no_send_shutdown:1;
unsigned handshake_buffer_set:1;
ngx_ssl_dyn_rec_t dyn_rec;
ngx_msec_t dyn_rec_last_write;
ngx_uint_t dyn_rec_records_sent;
};
#define NGX_SSL_NO_SCACHE -2
#define NGX_SSL_NONE_SCACHE -3
#define NGX_SSL_NO_BUILTIN_SCACHE -4
#define NGX_SSL_DFLT_BUILTIN_SCACHE -5
#define NGX_SSL_MAX_SESSION_SIZE 16384
typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t;
struct ngx_ssl_sess_id_s {
ngx_rbtree_node_t node;
u_char *id;
size_t len;
u_char *session;
ngx_queue_t queue;
time_t expire;
#if (NGX_PTR_SIZE == 8)
void *stub;
u_char sess_id[32];
#endif
};
typedef struct {
ngx_rbtree_t session_rbtree;
ngx_rbtree_node_t sentinel;
ngx_queue_t expire_queue;
} ngx_ssl_session_cache_t;
#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
typedef struct {
size_t size;
u_char name[16];
u_char hmac_key[32];
u_char aes_key[32];
} ngx_ssl_session_ticket_key_t;
#endif
#define NGX_SSL_SSLv2 0x0002
#define NGX_SSL_SSLv3 0x0004
#define NGX_SSL_TLSv1 0x0008
#define NGX_SSL_TLSv1_1 0x0010
#define NGX_SSL_TLSv1_2 0x0020
#define NGX_SSL_TLSv1_3 0x0040
#define NGX_SSL_BUFFER 1
#define NGX_SSL_CLIENT 2
#define NGX_SSL_BUFSIZE 16384
ngx_int_t ngx_ssl_init(ngx_log_t *log);
ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_array_t *certs, ngx_array_t *keys, ngx_array_t *passwords);
ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
ngx_uint_t prefer_server_ciphers);
ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_int_t depth);
ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_int_t depth);
ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);
ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);
RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
int key_length);
ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);
ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_array_t *paths);
ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
ngx_uint_t flags);
void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);
#define ngx_ssl_get_session(c) SSL_get1_session(c->ssl->connection)
#define ngx_ssl_free_session SSL_SESSION_free
#define ngx_ssl_get_connection(ssl_conn) \
SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index)
#define ngx_ssl_get_server_conf(ssl_ctx) \
SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_server_conf_index)
#define ngx_ssl_verify_error_optional(n) \
(n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT \
|| n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN \
|| n == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY \
|| n == X509_V_ERR_CERT_UNTRUSTED \
|| n == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)
ngx_int_t ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name);
ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_escaped_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_subject_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_issuer_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *s);
ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);
ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit);
ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
void ngx_ssl_free_buffer(ngx_connection_t *c);
ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
char *fmt, ...);
void ngx_ssl_cleanup_ctx(void *data);
extern int ngx_ssl_connection_index;
extern int ngx_ssl_server_conf_index;
extern int ngx_ssl_session_cache_index;
extern int ngx_ssl_session_ticket_keys_index;
extern int ngx_ssl_certificate_index;
extern int ngx_ssl_next_certificate_index;
extern int ngx_ssl_certificate_name_index;
extern int ngx_ssl_stapling_index;
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
+1892
View File
@@ -0,0 +1,1892 @@
/*
* Copyright (C) Maxim Dounin
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_connect.h>
#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)
typedef struct {
ngx_str_t staple;
ngx_msec_t timeout;
ngx_resolver_t *resolver;
ngx_msec_t resolver_timeout;
ngx_addr_t *addrs;
ngx_str_t host;
ngx_str_t uri;
in_port_t port;
SSL_CTX *ssl_ctx;
X509 *cert;
X509 *issuer;
u_char *name;
time_t valid;
time_t refresh;
unsigned verify:1;
unsigned loading:1;
} ngx_ssl_stapling_t;
typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t;
struct ngx_ssl_ocsp_ctx_s {
X509 *cert;
X509 *issuer;
u_char *name;
ngx_uint_t naddrs;
ngx_addr_t *addrs;
ngx_str_t host;
ngx_str_t uri;
in_port_t port;
ngx_resolver_t *resolver;
ngx_msec_t resolver_timeout;
ngx_msec_t timeout;
void (*handler)(ngx_ssl_ocsp_ctx_t *ctx);
void *data;
ngx_buf_t *request;
ngx_buf_t *response;
ngx_peer_connection_t peer;
ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *ctx);
ngx_uint_t state;
ngx_uint_t code;
ngx_uint_t count;
ngx_uint_t done;
u_char *header_name_start;
u_char *header_name_end;
u_char *header_start;
u_char *header_end;
ngx_pool_t *pool;
ngx_log_t *log;
};
static ngx_int_t ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
X509 *cert, ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_ssl_stapling_t *staple, ngx_str_t *file);
static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_ssl_stapling_t *staple);
static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_ssl_stapling_t *staple, ngx_str_t *responder);
static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
void *data);
static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);
static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time);
static void ngx_ssl_stapling_cleanup(void *data);
static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);
static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);
static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);
static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev);
static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev);
static void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev);
static ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx);
static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx);
static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);
static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);
static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);
static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);
static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);
ngx_int_t
ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
ngx_str_t *responder, ngx_uint_t verify)
{
X509 *cert;
for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
cert;
cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
{
if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify)
!= NGX_OK)
{
return NGX_ERROR;
}
}
SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
return NGX_OK;
}
static ngx_int_t
ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert,
ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify)
{
ngx_int_t rc;
ngx_pool_cleanup_t *cln;
ngx_ssl_stapling_t *staple;
staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));
if (staple == NULL) {
return NGX_ERROR;
}
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_ERROR;
}
cln->handler = ngx_ssl_stapling_cleanup;
cln->data = staple;
if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
return NGX_ERROR;
}
staple->ssl_ctx = ssl->ctx;
staple->timeout = 60000;
staple->verify = verify;
staple->cert = cert;
staple->name = X509_get_ex_data(staple->cert,
ngx_ssl_certificate_name_index);
if (file->len) {
/* use OCSP response from the file */
if (ngx_ssl_stapling_file(cf, ssl, staple, file) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
rc = ngx_ssl_stapling_issuer(cf, ssl, staple);
if (rc == NGX_DECLINED) {
return NGX_OK;
}
if (rc != NGX_OK) {
return NGX_ERROR;
}
rc = ngx_ssl_stapling_responder(cf, ssl, staple, responder);
if (rc == NGX_DECLINED) {
return NGX_OK;
}
if (rc != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_ssl_stapling_t *staple, ngx_str_t *file)
{
BIO *bio;
int len;
u_char *p, *buf;
OCSP_RESPONSE *response;
if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
return NGX_ERROR;
}
bio = BIO_new_file((char *) file->data, "r");
if (bio == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"BIO_new_file(\"%s\") failed", file->data);
return NGX_ERROR;
}
response = d2i_OCSP_RESPONSE_bio(bio, NULL);
if (response == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"d2i_OCSP_RESPONSE_bio(\"%s\") failed", file->data);
BIO_free(bio);
return NGX_ERROR;
}
len = i2d_OCSP_RESPONSE(response, NULL);
if (len <= 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
goto failed;
}
buf = ngx_alloc(len, ssl->log);
if (buf == NULL) {
goto failed;
}
p = buf;
len = i2d_OCSP_RESPONSE(response, &p);
if (len <= 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
ngx_free(buf);
goto failed;
}
OCSP_RESPONSE_free(response);
BIO_free(bio);
staple->staple.data = buf;
staple->staple.len = len;
staple->valid = NGX_MAX_TIME_T_VALUE;
return NGX_OK;
failed:
OCSP_RESPONSE_free(response);
BIO_free(bio);
return NGX_ERROR;
}
static ngx_int_t
ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_ssl_stapling_t *staple)
{
int i, n, rc;
X509 *cert, *issuer;
X509_STORE *store;
X509_STORE_CTX *store_ctx;
STACK_OF(X509) *chain;
cert = staple->cert;
#ifdef SSL_CTRL_SELECT_CURRENT_CERT
/* OpenSSL 1.0.2+ */
SSL_CTX_select_current_cert(ssl->ctx, cert);
#endif
#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
/* OpenSSL 1.0.1+ */
SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
#else
chain = ssl->ctx->extra_certs;
#endif
n = sk_X509_num(chain);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
"SSL get issuer: %d extra certs", n);
for (i = 0; i < n; i++) {
issuer = sk_X509_value(chain, i);
if (X509_check_issued(issuer, cert) == X509_V_OK) {
#if OPENSSL_VERSION_NUMBER >= 0x10100001L
X509_up_ref(issuer);
#else
CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);
#endif
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
"SSL get issuer: found %p in extra certs", issuer);
staple->issuer = issuer;
return NGX_OK;
}
}
store = SSL_CTX_get_cert_store(ssl->ctx);
if (store == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_get_cert_store() failed");
return NGX_ERROR;
}
store_ctx = X509_STORE_CTX_new();
if (store_ctx == NULL) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"X509_STORE_CTX_new() failed");
return NGX_ERROR;
}
if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"X509_STORE_CTX_init() failed");
X509_STORE_CTX_free(store_ctx);
return NGX_ERROR;
}
rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert);
if (rc == -1) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"X509_STORE_CTX_get1_issuer() failed");
X509_STORE_CTX_free(store_ctx);
return NGX_ERROR;
}
if (rc == 0) {
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_stapling\" ignored, "
"issuer certificate not found for certificate \"%s\"",
staple->name);
X509_STORE_CTX_free(store_ctx);
return NGX_DECLINED;
}
X509_STORE_CTX_free(store_ctx);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
"SSL get issuer: found %p in cert store", issuer);
staple->issuer = issuer;
return NGX_OK;
}
static ngx_int_t
ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_ssl_stapling_t *staple, ngx_str_t *responder)
{
char *s;
ngx_str_t rsp;
ngx_url_t u;
STACK_OF(OPENSSL_STRING) *aia;
if (responder->len == 0) {
/* extract OCSP responder URL from certificate */
aia = X509_get1_ocsp(staple->cert);
if (aia == NULL) {
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_stapling\" ignored, "
"no OCSP responder URL in the certificate \"%s\"",
staple->name);
return NGX_DECLINED;
}
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
s = sk_OPENSSL_STRING_value(aia, 0);
#else
s = sk_value(aia, 0);
#endif
if (s == NULL) {
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_stapling\" ignored, "
"no OCSP responder URL in the certificate \"%s\"",
staple->name);
X509_email_free(aia);
return NGX_DECLINED;
}
responder = &rsp;
responder->len = ngx_strlen(s);
responder->data = ngx_palloc(cf->pool, responder->len);
if (responder->data == NULL) {
X509_email_free(aia);
return NGX_ERROR;
}
ngx_memcpy(responder->data, s, responder->len);
X509_email_free(aia);
}
ngx_memzero(&u, sizeof(ngx_url_t));
u.url = *responder;
u.default_port = 80;
u.uri_part = 1;
if (u.url.len > 7
&& ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
{
u.url.len -= 7;
u.url.data += 7;
} else {
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_stapling\" ignored, "
"invalid URL prefix in OCSP responder \"%V\" "
"in the certificate \"%s\"",
&u.url, staple->name);
return NGX_DECLINED;
}
if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
if (u.err) {
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_stapling\" ignored, "
"%s in OCSP responder \"%V\" "
"in the certificate \"%s\"",
u.err, &u.url, staple->name);
return NGX_DECLINED;
}
return NGX_ERROR;
}
staple->addrs = u.addrs;
staple->host = u.host;
staple->uri = u.uri;
staple->port = u.port;
if (staple->uri.len == 0) {
ngx_str_set(&staple->uri, "/");
}
return NGX_OK;
}
ngx_int_t
ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
{
X509 *cert;
ngx_ssl_stapling_t *staple;
for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
cert;
cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
{
staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
staple->resolver = resolver;
staple->resolver_timeout = resolver_timeout;
}
return NGX_OK;
}
static int
ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
{
int rc;
X509 *cert;
u_char *p;
ngx_connection_t *c;
ngx_ssl_stapling_t *staple;
c = ngx_ssl_get_connection(ssl_conn);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
"SSL certificate status callback");
rc = SSL_TLSEXT_ERR_NOACK;
cert = SSL_get_certificate(ssl_conn);
staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
if (staple == NULL) {
return rc;
}
if (staple->staple.len
&& staple->valid >= ngx_time())
{
/* we have to copy ocsp response as OpenSSL will free it by itself */
p = OPENSSL_malloc(staple->staple.len);
if (p == NULL) {
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "OPENSSL_malloc() failed");
return SSL_TLSEXT_ERR_NOACK;
}
ngx_memcpy(p, staple->staple.data, staple->staple.len);
SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len);
rc = SSL_TLSEXT_ERR_OK;
}
ngx_ssl_stapling_update(staple);
return rc;
}
static void
ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
{
ngx_ssl_ocsp_ctx_t *ctx;
if (staple->host.len == 0
|| staple->loading || staple->refresh >= ngx_time())
{
return;
}
staple->loading = 1;
ctx = ngx_ssl_ocsp_start();
if (ctx == NULL) {
return;
}
ctx->cert = staple->cert;
ctx->issuer = staple->issuer;
ctx->name = staple->name;
ctx->addrs = staple->addrs;
ctx->host = staple->host;
ctx->uri = staple->uri;
ctx->port = staple->port;
ctx->timeout = staple->timeout;
ctx->resolver = staple->resolver;
ctx->resolver_timeout = staple->resolver_timeout;
ctx->handler = ngx_ssl_stapling_ocsp_handler;
ctx->data = staple;
ngx_ssl_ocsp_request(ctx);
return;
}
static void
ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
{
#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
const
#endif
u_char *p;
int n;
size_t len;
time_t now, valid;
ngx_str_t response;
X509_STORE *store;
STACK_OF(X509) *chain;
OCSP_CERTID *id;
OCSP_RESPONSE *ocsp;
OCSP_BASICRESP *basic;
ngx_ssl_stapling_t *staple;
ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
staple = ctx->data;
now = ngx_time();
ocsp = NULL;
basic = NULL;
id = NULL;
if (ctx->code != 200) {
goto error;
}
/* check the response */
len = ctx->response->last - ctx->response->pos;
p = ctx->response->pos;
ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
if (ocsp == NULL) {
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
"d2i_OCSP_RESPONSE() failed");
goto error;
}
n = OCSP_response_status(ocsp);
if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP response not successful (%d: %s)",
n, OCSP_response_status_str(n));
goto error;
}
basic = OCSP_response_get1_basic(ocsp);
if (basic == NULL) {
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP_response_get1_basic() failed");
goto error;
}
store = SSL_CTX_get_cert_store(staple->ssl_ctx);
if (store == NULL) {
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
"SSL_CTX_get_cert_store() failed");
goto error;
}
#ifdef SSL_CTRL_SELECT_CURRENT_CERT
/* OpenSSL 1.0.2+ */
SSL_CTX_select_current_cert(staple->ssl_ctx, ctx->cert);
#endif
#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS
/* OpenSSL 1.0.1+ */
SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
#else
chain = staple->ssl_ctx->extra_certs;
#endif
if (OCSP_basic_verify(basic, chain, store,
staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
!= 1)
{
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP_basic_verify() failed");
goto error;
}
id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
if (id == NULL) {
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
"OCSP_cert_to_id() failed");
goto error;
}
if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
&thisupdate, &nextupdate)
!= 1)
{
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"certificate status not found in the OCSP response");
goto error;
}
if (n != V_OCSP_CERTSTATUS_GOOD) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"certificate status \"%s\" in the OCSP response",
OCSP_cert_status_str(n));
goto error;
}
if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP_check_validity() failed");
goto error;
}
if (nextupdate) {
valid = ngx_ssl_stapling_time(nextupdate);
if (valid == (time_t) NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"invalid nextUpdate time in certificate status");
goto error;
}
} else {
valid = NGX_MAX_TIME_T_VALUE;
}
OCSP_CERTID_free(id);
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(ocsp);
id = NULL;
basic = NULL;
ocsp = NULL;
/* copy the response to memory not in ctx->pool */
response.len = len;
response.data = ngx_alloc(response.len, ctx->log);
if (response.data == NULL) {
goto error;
}
ngx_memcpy(response.data, ctx->response->pos, response.len);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp response, %s, %uz",
OCSP_cert_status_str(n), response.len);
if (staple->staple.data) {
ngx_free(staple->staple.data);
}
staple->staple = response;
staple->valid = valid;
/*
* refresh before the response expires,
* but not earlier than in 5 minutes, and at least in an hour
*/
staple->loading = 0;
staple->refresh = ngx_max(ngx_min(valid - 300, now + 3600), now + 300);
ngx_ssl_ocsp_done(ctx);
return;
error:
staple->loading = 0;
staple->refresh = now + 300;
if (id) {
OCSP_CERTID_free(id);
}
if (basic) {
OCSP_BASICRESP_free(basic);
}
if (ocsp) {
OCSP_RESPONSE_free(ocsp);
}
ngx_ssl_ocsp_done(ctx);
}
static time_t
ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time)
{
BIO *bio;
char *value;
size_t len;
time_t time;
/*
* OpenSSL doesn't provide a way to convert ASN1_GENERALIZEDTIME
* into time_t. To do this, we use ASN1_GENERALIZEDTIME_print(),
* which uses the "MMM DD HH:MM:SS YYYY [GMT]" format (e.g.,
* "Feb 3 00:55:52 2015 GMT"), and parse the result.
*/
bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
return NGX_ERROR;
}
/* fake weekday prepended to match C asctime() format */
BIO_write(bio, "Tue ", sizeof("Tue ") - 1);
ASN1_GENERALIZEDTIME_print(bio, asn1time);
len = BIO_get_mem_data(bio, &value);
time = ngx_parse_http_time((u_char *) value, len);
BIO_free(bio);
return time;
}
static void
ngx_ssl_stapling_cleanup(void *data)
{
ngx_ssl_stapling_t *staple = data;
if (staple->issuer) {
X509_free(staple->issuer);
}
if (staple->staple.data) {
ngx_free(staple->staple.data);
}
}
static ngx_ssl_ocsp_ctx_t *
ngx_ssl_ocsp_start(void)
{
ngx_log_t *log;
ngx_pool_t *pool;
ngx_ssl_ocsp_ctx_t *ctx;
pool = ngx_create_pool(2048, ngx_cycle->log);
if (pool == NULL) {
return NULL;
}
ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));
if (ctx == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
log = ngx_palloc(pool, sizeof(ngx_log_t));
if (log == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
ctx->pool = pool;
*log = *ctx->pool->log;
ctx->pool->log = log;
ctx->log = log;
log->handler = ngx_ssl_ocsp_log_error;
log->data = ctx;
log->action = "requesting certificate status";
return ctx;
}
static void
ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx)
{
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp done");
if (ctx->peer.connection) {
ngx_close_connection(ctx->peer.connection);
}
ngx_destroy_pool(ctx->pool);
}
static void
ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)
{
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp error");
ctx->code = 0;
ctx->handler(ctx);
}
static void
ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
{
ngx_resolver_ctx_t *resolve, temp;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp request");
if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {
ngx_ssl_ocsp_error(ctx);
return;
}
if (ctx->resolver) {
/* resolve OCSP responder hostname */
temp.name = ctx->host;
resolve = ngx_resolve_start(ctx->resolver, &temp);
if (resolve == NULL) {
ngx_ssl_ocsp_error(ctx);
return;
}
if (resolve == NGX_NO_RESOLVER) {
ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
"no resolver defined to resolve %V", &ctx->host);
goto connect;
}
resolve->name = ctx->host;
resolve->handler = ngx_ssl_ocsp_resolve_handler;
resolve->data = ctx;
resolve->timeout = ctx->resolver_timeout;
if (ngx_resolve_name(resolve) != NGX_OK) {
ngx_ssl_ocsp_error(ctx);
return;
}
return;
}
connect:
ngx_ssl_ocsp_connect(ctx);
}
static void
ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)
{
ngx_ssl_ocsp_ctx_t *ctx = resolve->data;
u_char *p;
size_t len;
socklen_t socklen;
ngx_uint_t i;
struct sockaddr *sockaddr;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp resolve handler");
if (resolve->state) {
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"%V could not be resolved (%i: %s)",
&resolve->name, resolve->state,
ngx_resolver_strerror(resolve->state));
goto failed;
}
#if (NGX_DEBUG)
{
u_char text[NGX_SOCKADDR_STRLEN];
ngx_str_t addr;
addr.data = text;
for (i = 0; i < resolve->naddrs; i++) {
addr.len = ngx_sock_ntop(resolve->addrs[i].sockaddr,
resolve->addrs[i].socklen,
text, NGX_SOCKADDR_STRLEN, 0);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"name was resolved to %V", &addr);
}
}
#endif
ctx->naddrs = resolve->naddrs;
ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));
if (ctx->addrs == NULL) {
goto failed;
}
for (i = 0; i < resolve->naddrs; i++) {
socklen = resolve->addrs[i].socklen;
sockaddr = ngx_palloc(ctx->pool, socklen);
if (sockaddr == NULL) {
goto failed;
}
ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen);
ngx_inet_set_port(sockaddr, ctx->port);
ctx->addrs[i].sockaddr = sockaddr;
ctx->addrs[i].socklen = socklen;
p = ngx_pnalloc(ctx->pool, NGX_SOCKADDR_STRLEN);
if (p == NULL) {
goto failed;
}
len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
ctx->addrs[i].name.len = len;
ctx->addrs[i].name.data = p;
}
ngx_resolve_name_done(resolve);
ngx_ssl_ocsp_connect(ctx);
return;
failed:
ngx_resolve_name_done(resolve);
ngx_ssl_ocsp_error(ctx);
}
static void
ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
{
ngx_int_t rc;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp connect");
/* TODO: use all ip addresses */
ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
ctx->peer.socklen = ctx->addrs[0].socklen;
ctx->peer.name = &ctx->addrs[0].name;
ctx->peer.get = ngx_event_get_peer;
ctx->peer.log = ctx->log;
ctx->peer.log_error = NGX_ERROR_ERR;
rc = ngx_event_connect_peer(&ctx->peer);
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp connect peer done");
if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
ngx_ssl_ocsp_error(ctx);
return;
}
ctx->peer.connection->data = ctx;
ctx->peer.connection->pool = ctx->pool;
ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;
ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;
ctx->process = ngx_ssl_ocsp_process_status_line;
ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
if (rc == NGX_OK) {
ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
return;
}
}
static void
ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
{
ssize_t n, size;
ngx_connection_t *c;
ngx_ssl_ocsp_ctx_t *ctx;
c = wev->data;
ctx = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,
"ssl ocsp write handler");
if (wev->timedout) {
ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
"OCSP responder timed out");
ngx_ssl_ocsp_error(ctx);
return;
}
size = ctx->request->last - ctx->request->pos;
n = ngx_send(c, ctx->request->pos, size);
if (n == NGX_ERROR) {
ngx_ssl_ocsp_error(ctx);
return;
}
if (n > 0) {
ctx->request->pos += n;
if (n == size) {
wev->handler = ngx_ssl_ocsp_dummy_handler;
if (wev->timer_set) {
ngx_del_timer(wev);
}
if (ngx_handle_write_event(wev, 0) != NGX_OK) {
ngx_ssl_ocsp_error(ctx);
}
return;
}
}
if (!wev->timer_set) {
ngx_add_timer(wev, ctx->timeout);
}
}
static void
ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
{
ssize_t n, size;
ngx_int_t rc;
ngx_connection_t *c;
ngx_ssl_ocsp_ctx_t *ctx;
c = rev->data;
ctx = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,
"ssl ocsp read handler");
if (rev->timedout) {
ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
"OCSP responder timed out");
ngx_ssl_ocsp_error(ctx);
return;
}
if (ctx->response == NULL) {
ctx->response = ngx_create_temp_buf(ctx->pool, 16384);
if (ctx->response == NULL) {
ngx_ssl_ocsp_error(ctx);
return;
}
}
for ( ;; ) {
size = ctx->response->end - ctx->response->last;
n = ngx_recv(c, ctx->response->last, size);
if (n > 0) {
ctx->response->last += n;
rc = ctx->process(ctx);
if (rc == NGX_ERROR) {
ngx_ssl_ocsp_error(ctx);
return;
}
continue;
}
if (n == NGX_AGAIN) {
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_ssl_ocsp_error(ctx);
}
return;
}
break;
}
ctx->done = 1;
rc = ctx->process(ctx);
if (rc == NGX_DONE) {
/* ctx->handler() was called */
return;
}
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP responder prematurely closed connection");
ngx_ssl_ocsp_error(ctx);
}
static void
ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev)
{
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"ssl ocsp dummy handler");
}
static ngx_int_t
ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx)
{
int len;
u_char *p;
uintptr_t escape;
ngx_str_t binary, base64;
ngx_buf_t *b;
OCSP_CERTID *id;
OCSP_REQUEST *ocsp;
ocsp = OCSP_REQUEST_new();
if (ocsp == NULL) {
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
"OCSP_REQUEST_new() failed");
return NGX_ERROR;
}
id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
if (id == NULL) {
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
"OCSP_cert_to_id() failed");
goto failed;
}
if (OCSP_request_add0_id(ocsp, id) == NULL) {
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
"OCSP_request_add0_id() failed");
OCSP_CERTID_free(id);
goto failed;
}
len = i2d_OCSP_REQUEST(ocsp, NULL);
if (len <= 0) {
ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
"i2d_OCSP_REQUEST() failed");
goto failed;
}
binary.len = len;
binary.data = ngx_palloc(ctx->pool, len);
if (binary.data == NULL) {
goto failed;
}
p = binary.data;
len = i2d_OCSP_REQUEST(ocsp, &p);
if (len <= 0) {
ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0,
"i2d_OCSP_REQUEST() failed");
goto failed;
}
base64.len = ngx_base64_encoded_length(binary.len);
base64.data = ngx_palloc(ctx->pool, base64.len);
if (base64.data == NULL) {
goto failed;
}
ngx_encode_base64(&base64, &binary);
escape = ngx_escape_uri(NULL, base64.data, base64.len,
NGX_ESCAPE_URI_COMPONENT);
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp request length %z, escape %d",
base64.len, (int) escape);
len = sizeof("GET ") - 1 + ctx->uri.len + sizeof("/") - 1
+ base64.len + 2 * escape + sizeof(" HTTP/1.0" CRLF) - 1
+ sizeof("Host: ") - 1 + ctx->host.len + sizeof(CRLF) - 1
+ sizeof(CRLF) - 1;
b = ngx_create_temp_buf(ctx->pool, len);
if (b == NULL) {
goto failed;
}
p = b->last;
p = ngx_cpymem(p, "GET ", sizeof("GET ") - 1);
p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len);
if (ctx->uri.data[ctx->uri.len - 1] != '/') {
*p++ = '/';
}
if (escape == 0) {
p = ngx_cpymem(p, base64.data, base64.len);
} else {
p = (u_char *) ngx_escape_uri(p, base64.data, base64.len,
NGX_ESCAPE_URI_COMPONENT);
}
p = ngx_cpymem(p, " HTTP/1.0" CRLF, sizeof(" HTTP/1.0" CRLF) - 1);
p = ngx_cpymem(p, "Host: ", sizeof("Host: ") - 1);
p = ngx_cpymem(p, ctx->host.data, ctx->host.len);
*p++ = CR; *p++ = LF;
/* add "\r\n" at the header end */
*p++ = CR; *p++ = LF;
b->last = p;
ctx->request = b;
OCSP_REQUEST_free(ocsp);
return NGX_OK;
failed:
OCSP_REQUEST_free(ocsp);
return NGX_ERROR;
}
static ngx_int_t
ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx)
{
ngx_int_t rc;
rc = ngx_ssl_ocsp_parse_status_line(ctx);
if (rc == NGX_OK) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp status %ui \"%*s\"",
ctx->code,
ctx->header_end - ctx->header_start,
ctx->header_start);
ctx->process = ngx_ssl_ocsp_process_headers;
return ctx->process(ctx);
}
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
/* rc == NGX_ERROR */
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP responder sent invalid response");
return NGX_ERROR;
}
static ngx_int_t
ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx)
{
u_char ch;
u_char *p;
ngx_buf_t *b;
enum {
sw_start = 0,
sw_H,
sw_HT,
sw_HTT,
sw_HTTP,
sw_first_major_digit,
sw_major_digit,
sw_first_minor_digit,
sw_minor_digit,
sw_status,
sw_space_after_status,
sw_status_text,
sw_almost_done
} state;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp process status line");
state = ctx->state;
b = ctx->response;
for (p = b->pos; p < b->last; p++) {
ch = *p;
switch (state) {
/* "HTTP/" */
case sw_start:
switch (ch) {
case 'H':
state = sw_H;
break;
default:
return NGX_ERROR;
}
break;
case sw_H:
switch (ch) {
case 'T':
state = sw_HT;
break;
default:
return NGX_ERROR;
}
break;
case sw_HT:
switch (ch) {
case 'T':
state = sw_HTT;
break;
default:
return NGX_ERROR;
}
break;
case sw_HTT:
switch (ch) {
case 'P':
state = sw_HTTP;
break;
default:
return NGX_ERROR;
}
break;
case sw_HTTP:
switch (ch) {
case '/':
state = sw_first_major_digit;
break;
default:
return NGX_ERROR;
}
break;
/* the first digit of major HTTP version */
case sw_first_major_digit:
if (ch < '1' || ch > '9') {
return NGX_ERROR;
}
state = sw_major_digit;
break;
/* the major HTTP version or dot */
case sw_major_digit:
if (ch == '.') {
state = sw_first_minor_digit;
break;
}
if (ch < '0' || ch > '9') {
return NGX_ERROR;
}
break;
/* the first digit of minor HTTP version */
case sw_first_minor_digit:
if (ch < '0' || ch > '9') {
return NGX_ERROR;
}
state = sw_minor_digit;
break;
/* the minor HTTP version or the end of the request line */
case sw_minor_digit:
if (ch == ' ') {
state = sw_status;
break;
}
if (ch < '0' || ch > '9') {
return NGX_ERROR;
}
break;
/* HTTP status code */
case sw_status:
if (ch == ' ') {
break;
}
if (ch < '0' || ch > '9') {
return NGX_ERROR;
}
ctx->code = ctx->code * 10 + (ch - '0');
if (++ctx->count == 3) {
state = sw_space_after_status;
ctx->header_start = p - 2;
}
break;
/* space or end of line */
case sw_space_after_status:
switch (ch) {
case ' ':
state = sw_status_text;
break;
case '.': /* IIS may send 403.1, 403.2, etc */
state = sw_status_text;
break;
case CR:
state = sw_almost_done;
break;
case LF:
ctx->header_end = p;
goto done;
default:
return NGX_ERROR;
}
break;
/* any text until end of line */
case sw_status_text:
switch (ch) {
case CR:
state = sw_almost_done;
break;
case LF:
ctx->header_end = p;
goto done;
}
break;
/* end of status line */
case sw_almost_done:
switch (ch) {
case LF:
ctx->header_end = p - 1;
goto done;
default:
return NGX_ERROR;
}
}
}
b->pos = p;
ctx->state = state;
return NGX_AGAIN;
done:
b->pos = p + 1;
ctx->state = sw_start;
return NGX_OK;
}
static ngx_int_t
ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx)
{
size_t len;
ngx_int_t rc;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp process headers");
for ( ;; ) {
rc = ngx_ssl_ocsp_parse_header_line(ctx);
if (rc == NGX_OK) {
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp header \"%*s: %*s\"",
ctx->header_name_end - ctx->header_name_start,
ctx->header_name_start,
ctx->header_end - ctx->header_start,
ctx->header_start);
len = ctx->header_name_end - ctx->header_name_start;
if (len == sizeof("Content-Type") - 1
&& ngx_strncasecmp(ctx->header_name_start,
(u_char *) "Content-Type",
sizeof("Content-Type") - 1)
== 0)
{
len = ctx->header_end - ctx->header_start;
if (len != sizeof("application/ocsp-response") - 1
|| ngx_strncasecmp(ctx->header_start,
(u_char *) "application/ocsp-response",
sizeof("application/ocsp-response") - 1)
!= 0)
{
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP responder sent invalid "
"\"Content-Type\" header: \"%*s\"",
ctx->header_end - ctx->header_start,
ctx->header_start);
return NGX_ERROR;
}
continue;
}
/* TODO: honor Content-Length */
continue;
}
if (rc == NGX_DONE) {
break;
}
if (rc == NGX_AGAIN) {
return NGX_AGAIN;
}
/* rc == NGX_ERROR */
ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
"OCSP responder sent invalid response");
return NGX_ERROR;
}
ctx->process = ngx_ssl_ocsp_process_body;
return ctx->process(ctx);
}
static ngx_int_t
ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)
{
u_char c, ch, *p;
enum {
sw_start = 0,
sw_name,
sw_space_before_value,
sw_value,
sw_space_after_value,
sw_almost_done,
sw_header_almost_done
} state;
state = ctx->state;
for (p = ctx->response->pos; p < ctx->response->last; p++) {
ch = *p;
#if 0
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"s:%d in:'%02Xd:%c'", state, ch, ch);
#endif
switch (state) {
/* first char */
case sw_start:
switch (ch) {
case CR:
ctx->header_end = p;
state = sw_header_almost_done;
break;
case LF:
ctx->header_end = p;
goto header_done;
default:
state = sw_name;
ctx->header_name_start = p;
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
break;
}
if (ch >= '0' && ch <= '9') {
break;
}
return NGX_ERROR;
}
break;
/* header name */
case sw_name:
c = (u_char) (ch | 0x20);
if (c >= 'a' && c <= 'z') {
break;
}
if (ch == ':') {
ctx->header_name_end = p;
state = sw_space_before_value;
break;
}
if (ch == '-') {
break;
}
if (ch >= '0' && ch <= '9') {
break;
}
if (ch == CR) {
ctx->header_name_end = p;
ctx->header_start = p;
ctx->header_end = p;
state = sw_almost_done;
break;
}
if (ch == LF) {
ctx->header_name_end = p;
ctx->header_start = p;
ctx->header_end = p;
goto done;
}
return NGX_ERROR;
/* space* before header value */
case sw_space_before_value:
switch (ch) {
case ' ':
break;
case CR:
ctx->header_start = p;
ctx->header_end = p;
state = sw_almost_done;
break;
case LF:
ctx->header_start = p;
ctx->header_end = p;
goto done;
default:
ctx->header_start = p;
state = sw_value;
break;
}
break;
/* header value */
case sw_value:
switch (ch) {
case ' ':
ctx->header_end = p;
state = sw_space_after_value;
break;
case CR:
ctx->header_end = p;
state = sw_almost_done;
break;
case LF:
ctx->header_end = p;
goto done;
}
break;
/* space* before end of header line */
case sw_space_after_value:
switch (ch) {
case ' ':
break;
case CR:
state = sw_almost_done;
break;
case LF:
goto done;
default:
state = sw_value;
break;
}
break;
/* end of header line */
case sw_almost_done:
switch (ch) {
case LF:
goto done;
default:
return NGX_ERROR;
}
/* end of header */
case sw_header_almost_done:
switch (ch) {
case LF:
goto header_done;
default:
return NGX_ERROR;
}
}
}
ctx->response->pos = p;
ctx->state = state;
return NGX_AGAIN;
done:
ctx->response->pos = p + 1;
ctx->state = sw_start;
return NGX_OK;
header_done:
ctx->response->pos = p + 1;
ctx->state = sw_start;
return NGX_DONE;
}
static ngx_int_t
ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)
{
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
"ssl ocsp process body");
if (ctx->done) {
ctx->handler(ctx);
return NGX_DONE;
}
return NGX_AGAIN;
}
static u_char *
ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)
{
u_char *p;
ngx_ssl_ocsp_ctx_t *ctx;
p = buf;
if (log->action) {
p = ngx_snprintf(buf, len, " while %s", log->action);
len -= p - buf;
buf = p;
}
ctx = log->data;
if (ctx) {
p = ngx_snprintf(buf, len, ", responder: %V", &ctx->host);
len -= p - buf;
buf = p;
}
if (ctx && ctx->peer.name) {
p = ngx_snprintf(buf, len, ", peer: %V", ctx->peer.name);
len -= p - buf;
buf = p;
}
if (ctx && ctx->name) {
p = ngx_snprintf(buf, len, ", certificate: \"%s\"", ctx->name);
len -= p - buf;
buf = p;
}
return p;
}
#else
ngx_int_t
ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
ngx_str_t *responder, ngx_uint_t verify)
{
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_stapling\" ignored, not supported");
return NGX_OK;
}
ngx_int_t
ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
{
return NGX_OK;
}
#endif
+1110
View File
@@ -0,0 +1,1110 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_event_pipe.h>
static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p);
static ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p);
static ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p);
static ngx_inline void ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf);
static ngx_int_t ngx_event_pipe_drain_chains(ngx_event_pipe_t *p);
ngx_int_t
ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)
{
ngx_int_t rc;
ngx_uint_t flags;
ngx_event_t *rev, *wev;
for ( ;; ) {
if (do_write) {
p->log->action = "sending to client";
rc = ngx_event_pipe_write_to_downstream(p);
if (rc == NGX_ABORT) {
return NGX_ABORT;
}
if (rc == NGX_BUSY) {
return NGX_OK;
}
}
p->read = 0;
p->upstream_blocked = 0;
p->log->action = "reading upstream";
if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {
return NGX_ABORT;
}
if (!p->read && !p->upstream_blocked) {
break;
}
do_write = 1;
}
if (p->upstream->fd != (ngx_socket_t) -1) {
rev = p->upstream->read;
flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0;
if (ngx_handle_read_event(rev, flags) != NGX_OK) {
return NGX_ABORT;
}
if (!rev->delayed) {
if (rev->active && !rev->ready) {
ngx_add_timer(rev, p->read_timeout);
} else if (rev->timer_set) {
ngx_del_timer(rev);
}
}
}
if (p->downstream->fd != (ngx_socket_t) -1
&& p->downstream->data == p->output_ctx)
{
wev = p->downstream->write;
if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
return NGX_ABORT;
}
if (!wev->delayed) {
if (wev->active && !wev->ready) {
ngx_add_timer(wev, p->send_timeout);
} else if (wev->timer_set) {
ngx_del_timer(wev);
}
}
}
return NGX_OK;
}
static ngx_int_t
ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
{
off_t limit;
ssize_t n, size;
ngx_int_t rc;
ngx_buf_t *b;
ngx_msec_t delay;
ngx_chain_t *chain, *cl, *ln;
if (p->upstream_eof || p->upstream_error || p->upstream_done) {
return NGX_OK;
}
#if (NGX_THREADS)
if (p->aio) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe read upstream: aio");
return NGX_AGAIN;
}
if (p->writing) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe read upstream: writing");
rc = ngx_event_pipe_write_chain_to_temp_file(p);
if (rc != NGX_OK) {
return rc;
}
}
#endif
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe read upstream: %d", p->upstream->read->ready);
for ( ;; ) {
if (p->upstream_eof || p->upstream_error || p->upstream_done) {
break;
}
if (p->preread_bufs == NULL && !p->upstream->read->ready) {
break;
}
if (p->preread_bufs) {
/* use the pre-read bufs if they exist */
chain = p->preread_bufs;
p->preread_bufs = NULL;
n = p->preread_size;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe preread: %z", n);
if (n) {
p->read = 1;
}
} else {
#if (NGX_HAVE_KQUEUE)
/*
* kqueue notifies about the end of file or a pending error.
* This test allows not to allocate a buf on these conditions
* and not to call c->recv_chain().
*/
if (p->upstream->read->available == 0
&& p->upstream->read->pending_eof)
{
p->upstream->read->ready = 0;
p->upstream->read->eof = 1;
p->upstream_eof = 1;
p->read = 1;
if (p->upstream->read->kq_errno) {
p->upstream->read->error = 1;
p->upstream_error = 1;
p->upstream_eof = 0;
ngx_log_error(NGX_LOG_ERR, p->log,
p->upstream->read->kq_errno,
"kevent() reported that upstream "
"closed connection");
}
break;
}
#endif
if (p->limit_rate) {
if (p->upstream->read->delayed) {
break;
}
limit = (off_t) p->limit_rate * (ngx_time() - p->start_sec + 1)
- p->read_length;
if (limit <= 0) {
p->upstream->read->delayed = 1;
delay = (ngx_msec_t) (- limit * 1000 / p->limit_rate + 1);
ngx_add_timer(p->upstream->read, delay);
break;
}
} else {
limit = 0;
}
if (p->free_raw_bufs) {
/* use the free bufs if they exist */
chain = p->free_raw_bufs;
if (p->single_buf) {
p->free_raw_bufs = p->free_raw_bufs->next;
chain->next = NULL;
} else {
p->free_raw_bufs = NULL;
}
} else if (p->allocated < p->bufs.num) {
/* allocate a new buf if it's still allowed */
b = ngx_create_temp_buf(p->pool, p->bufs.size);
if (b == NULL) {
return NGX_ABORT;
}
p->allocated++;
chain = ngx_alloc_chain_link(p->pool);
if (chain == NULL) {
return NGX_ABORT;
}
chain->buf = b;
chain->next = NULL;
} else if (!p->cacheable
&& p->downstream->data == p->output_ctx
&& p->downstream->write->ready
&& !p->downstream->write->delayed)
{
/*
* if the bufs are not needed to be saved in a cache and
* a downstream is ready then write the bufs to a downstream
*/
p->upstream_blocked = 1;
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe downstream ready");
break;
} else if (p->cacheable
|| p->temp_file->offset < p->max_temp_file_size)
{
/*
* if it is allowed, then save some bufs from p->in
* to a temporary file, and add them to a p->out chain
*/
rc = ngx_event_pipe_write_chain_to_temp_file(p);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe temp offset: %O", p->temp_file->offset);
if (rc == NGX_BUSY) {
break;
}
if (rc != NGX_OK) {
return rc;
}
chain = p->free_raw_bufs;
if (p->single_buf) {
p->free_raw_bufs = p->free_raw_bufs->next;
chain->next = NULL;
} else {
p->free_raw_bufs = NULL;
}
} else {
/* there are no bufs to read in */
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"no pipe bufs to read in");
break;
}
n = p->upstream->recv_chain(p->upstream, chain, limit);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe recv chain: %z", n);
if (p->free_raw_bufs) {
chain->next = p->free_raw_bufs;
}
p->free_raw_bufs = chain;
if (n == NGX_ERROR) {
p->upstream_error = 1;
break;
}
if (n == NGX_AGAIN) {
if (p->single_buf) {
ngx_event_pipe_remove_shadow_links(chain->buf);
}
break;
}
p->read = 1;
if (n == 0) {
p->upstream_eof = 1;
break;
}
}
delay = p->limit_rate ? (ngx_msec_t) n * 1000 / p->limit_rate : 0;
p->read_length += n;
cl = chain;
p->free_raw_bufs = NULL;
while (cl && n > 0) {
ngx_event_pipe_remove_shadow_links(cl->buf);
size = cl->buf->end - cl->buf->last;
if (n >= size) {
cl->buf->last = cl->buf->end;
/* STUB */ cl->buf->num = p->num++;
if (p->input_filter(p, cl->buf) == NGX_ERROR) {
return NGX_ABORT;
}
n -= size;
ln = cl;
cl = cl->next;
ngx_free_chain(p->pool, ln);
} else {
cl->buf->last += n;
n = 0;
}
}
if (cl) {
for (ln = cl; ln->next; ln = ln->next) { /* void */ }
ln->next = p->free_raw_bufs;
p->free_raw_bufs = cl;
}
if (delay > 0) {
p->upstream->read->delayed = 1;
ngx_add_timer(p->upstream->read, delay);
break;
}
}
#if (NGX_DEBUG)
for (cl = p->busy; cl; cl = cl->next) {
ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe buf busy s:%d t:%d f:%d "
"%p, pos %p, size: %z "
"file: %O, size: %O",
(cl->buf->shadow ? 1 : 0),
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
for (cl = p->out; cl; cl = cl->next) {
ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe buf out s:%d t:%d f:%d "
"%p, pos %p, size: %z "
"file: %O, size: %O",
(cl->buf->shadow ? 1 : 0),
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
for (cl = p->in; cl; cl = cl->next) {
ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe buf in s:%d t:%d f:%d "
"%p, pos %p, size: %z "
"file: %O, size: %O",
(cl->buf->shadow ? 1 : 0),
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
for (cl = p->free_raw_bufs; cl; cl = cl->next) {
ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe buf free s:%d t:%d f:%d "
"%p, pos %p, size: %z "
"file: %O, size: %O",
(cl->buf->shadow ? 1 : 0),
cl->buf->temporary, cl->buf->in_file,
cl->buf->start, cl->buf->pos,
cl->buf->last - cl->buf->pos,
cl->buf->file_pos,
cl->buf->file_last - cl->buf->file_pos);
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe length: %O", p->length);
#endif
if (p->free_raw_bufs && p->length != -1) {
cl = p->free_raw_bufs;
if (cl->buf->last - cl->buf->pos >= p->length) {
p->free_raw_bufs = cl->next;
/* STUB */ cl->buf->num = p->num++;
if (p->input_filter(p, cl->buf) == NGX_ERROR) {
return NGX_ABORT;
}
ngx_free_chain(p->pool, cl);
}
}
if (p->length == 0) {
p->upstream_done = 1;
p->read = 1;
}
if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) {
/* STUB */ p->free_raw_bufs->buf->num = p->num++;
if (p->input_filter(p, p->free_raw_bufs->buf) == NGX_ERROR) {
return NGX_ABORT;
}
p->free_raw_bufs = p->free_raw_bufs->next;
if (p->free_bufs && p->buf_to_file == NULL) {
for (cl = p->free_raw_bufs; cl; cl = cl->next) {
if (cl->buf->shadow == NULL) {
ngx_pfree(p->pool, cl->buf->start);
}
}
}
}
if (p->cacheable && (p->in || p->buf_to_file)) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write chain");
rc = ngx_event_pipe_write_chain_to_temp_file(p);
if (rc != NGX_OK) {
return rc;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
{
u_char *prev;
size_t bsize;
ngx_int_t rc;
ngx_uint_t flush, flushed, prev_last_shadow;
ngx_chain_t *out, **ll, *cl;
ngx_connection_t *downstream;
downstream = p->downstream;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write downstream: %d", downstream->write->ready);
#if (NGX_THREADS)
if (p->writing) {
rc = ngx_event_pipe_write_chain_to_temp_file(p);
if (rc == NGX_ABORT) {
return NGX_ABORT;
}
}
#endif
flushed = 0;
for ( ;; ) {
if (p->downstream_error) {
return ngx_event_pipe_drain_chains(p);
}
if (p->upstream_eof || p->upstream_error || p->upstream_done) {
/* pass the p->out and p->in chains to the output filter */
for (cl = p->busy; cl; cl = cl->next) {
cl->buf->recycled = 0;
}
if (p->out) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write downstream flush out");
for (cl = p->out; cl; cl = cl->next) {
cl->buf->recycled = 0;
}
rc = p->output_filter(p->output_ctx, p->out);
if (rc == NGX_ERROR) {
p->downstream_error = 1;
return ngx_event_pipe_drain_chains(p);
}
p->out = NULL;
}
if (p->writing) {
break;
}
if (p->in) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write downstream flush in");
for (cl = p->in; cl; cl = cl->next) {
cl->buf->recycled = 0;
}
rc = p->output_filter(p->output_ctx, p->in);
if (rc == NGX_ERROR) {
p->downstream_error = 1;
return ngx_event_pipe_drain_chains(p);
}
p->in = NULL;
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write downstream done");
/* TODO: free unused bufs */
p->downstream_done = 1;
break;
}
if (downstream->data != p->output_ctx
|| !downstream->write->ready
|| downstream->write->delayed)
{
break;
}
/* bsize is the size of the busy recycled bufs */
prev = NULL;
bsize = 0;
for (cl = p->busy; cl; cl = cl->next) {
if (cl->buf->recycled) {
if (prev == cl->buf->start) {
continue;
}
bsize += cl->buf->end - cl->buf->start;
prev = cl->buf->start;
}
}
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write busy: %uz", bsize);
out = NULL;
if (bsize >= (size_t) p->busy_size) {
flush = 1;
goto flush;
}
flush = 0;
ll = NULL;
prev_last_shadow = 1;
for ( ;; ) {
if (p->out) {
cl = p->out;
if (cl->buf->recycled) {
ngx_log_error(NGX_LOG_ALERT, p->log, 0,
"recycled buffer in pipe out chain");
}
p->out = p->out->next;
} else if (!p->cacheable && !p->writing && p->in) {
cl = p->in;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write buf ls:%d %p %z",
cl->buf->last_shadow,
cl->buf->pos,
cl->buf->last - cl->buf->pos);
if (cl->buf->recycled && prev_last_shadow) {
if (bsize + cl->buf->end - cl->buf->start > p->busy_size) {
flush = 1;
break;
}
bsize += cl->buf->end - cl->buf->start;
}
prev_last_shadow = cl->buf->last_shadow;
p->in = p->in->next;
} else {
break;
}
cl->next = NULL;
if (out) {
*ll = cl;
} else {
out = cl;
}
ll = &cl->next;
}
flush:
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe write: out:%p, f:%ui", out, flush);
if (out == NULL) {
if (!flush) {
break;
}
/* a workaround for AIO */
if (flushed++ > 10) {
return NGX_BUSY;
}
}
rc = p->output_filter(p->output_ctx, out);
ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag);
if (rc == NGX_ERROR) {
p->downstream_error = 1;
return ngx_event_pipe_drain_chains(p);
}
for (cl = p->free; cl; cl = cl->next) {
if (cl->buf->temp_file) {
if (p->cacheable || !p->cyclic_temp_file) {
continue;
}
/* reset p->temp_offset if all bufs had been sent */
if (cl->buf->file_last == p->temp_file->offset) {
p->temp_file->offset = 0;
}
}
/* TODO: free buf if p->free_bufs && upstream done */
/* add the free shadow raw buf to p->free_raw_bufs */
if (cl->buf->last_shadow) {
if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {
return NGX_ABORT;
}
cl->buf->last_shadow = 0;
}
cl->buf->shadow = NULL;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
{
ssize_t size, bsize, n;
ngx_buf_t *b;
ngx_uint_t prev_last_shadow;
ngx_chain_t *cl, *tl, *next, *out, **ll, **last_out, **last_free;
#if (NGX_THREADS)
if (p->writing) {
if (p->aio) {
return NGX_AGAIN;
}
out = p->writing;
p->writing = NULL;
n = ngx_write_chain_to_temp_file(p->temp_file, NULL);
if (n == NGX_ERROR) {
return NGX_ABORT;
}
goto done;
}
#endif
if (p->buf_to_file) {
out = ngx_alloc_chain_link(p->pool);
if (out == NULL) {
return NGX_ABORT;
}
out->buf = p->buf_to_file;
out->next = p->in;
} else {
out = p->in;
}
if (!p->cacheable) {
size = 0;
cl = out;
ll = NULL;
prev_last_shadow = 1;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe offset: %O", p->temp_file->offset);
do {
bsize = cl->buf->last - cl->buf->pos;
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, p->log, 0,
"pipe buf ls:%d %p, pos %p, size: %z",
cl->buf->last_shadow, cl->buf->start,
cl->buf->pos, bsize);
if (prev_last_shadow
&& ((size + bsize > p->temp_file_write_size)
|| (p->temp_file->offset + size + bsize
> p->max_temp_file_size)))
{
break;
}
prev_last_shadow = cl->buf->last_shadow;
size += bsize;
ll = &cl->next;
cl = cl->next;
} while (cl);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "size: %z", size);
if (ll == NULL) {
return NGX_BUSY;
}
if (cl) {
p->in = cl;
*ll = NULL;
} else {
p->in = NULL;
p->last_in = &p->in;
}
} else {
p->in = NULL;
p->last_in = &p->in;
}
#if (NGX_THREADS)
if (p->thread_handler) {
p->temp_file->thread_write = 1;
p->temp_file->file.thread_task = p->thread_task;
p->temp_file->file.thread_handler = p->thread_handler;
p->temp_file->file.thread_ctx = p->thread_ctx;
}
#endif
n = ngx_write_chain_to_temp_file(p->temp_file, out);
if (n == NGX_ERROR) {
return NGX_ABORT;
}
#if (NGX_THREADS)
if (n == NGX_AGAIN) {
p->writing = out;
p->thread_task = p->temp_file->file.thread_task;
return NGX_AGAIN;
}
done:
#endif
if (p->buf_to_file) {
p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos;
n -= p->buf_to_file->last - p->buf_to_file->pos;
p->buf_to_file = NULL;
out = out->next;
}
if (n > 0) {
/* update previous buffer or add new buffer */
if (p->out) {
for (cl = p->out; cl->next; cl = cl->next) { /* void */ }
b = cl->buf;
if (b->file_last == p->temp_file->offset) {
p->temp_file->offset += n;
b->file_last = p->temp_file->offset;
goto free;
}
last_out = &cl->next;
} else {
last_out = &p->out;
}
cl = ngx_chain_get_free_buf(p->pool, &p->free);
if (cl == NULL) {
return NGX_ABORT;
}
b = cl->buf;
ngx_memzero(b, sizeof(ngx_buf_t));
b->tag = p->tag;
b->file = &p->temp_file->file;
b->file_pos = p->temp_file->offset;
p->temp_file->offset += n;
b->file_last = p->temp_file->offset;
b->in_file = 1;
b->temp_file = 1;
*last_out = cl;
}
free:
for (last_free = &p->free_raw_bufs;
*last_free != NULL;
last_free = &(*last_free)->next)
{
/* void */
}
for (cl = out; cl; cl = next) {
next = cl->next;
cl->next = p->free;
p->free = cl;
b = cl->buf;
if (b->last_shadow) {
tl = ngx_alloc_chain_link(p->pool);
if (tl == NULL) {
return NGX_ABORT;
}
tl->buf = b->shadow;
tl->next = NULL;
*last_free = tl;
last_free = &tl->next;
b->shadow->pos = b->shadow->start;
b->shadow->last = b->shadow->start;
ngx_event_pipe_remove_shadow_links(b->shadow);
}
}
return NGX_OK;
}
/* the copy input filter */
ngx_int_t
ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
ngx_buf_t *b;
ngx_chain_t *cl;
if (buf->pos == buf->last) {
return NGX_OK;
}
cl = ngx_chain_get_free_buf(p->pool, &p->free);
if (cl == NULL) {
return NGX_ERROR;
}
b = cl->buf;
ngx_memcpy(b, buf, sizeof(ngx_buf_t));
b->shadow = buf;
b->tag = p->tag;
b->last_shadow = 1;
b->recycled = 1;
buf->shadow = b;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num);
if (p->in) {
*p->last_in = cl;
} else {
p->in = cl;
}
p->last_in = &cl->next;
if (p->length == -1) {
return NGX_OK;
}
p->length -= b->last - b->pos;
return NGX_OK;
}
static ngx_inline void
ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf)
{
ngx_buf_t *b, *next;
b = buf->shadow;
if (b == NULL) {
return;
}
while (!b->last_shadow) {
next = b->shadow;
b->temporary = 0;
b->recycled = 0;
b->shadow = NULL;
b = next;
}
b->temporary = 0;
b->recycled = 0;
b->last_shadow = 0;
b->shadow = NULL;
buf->shadow = NULL;
}
ngx_int_t
ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b)
{
ngx_chain_t *cl;
cl = ngx_alloc_chain_link(p->pool);
if (cl == NULL) {
return NGX_ERROR;
}
if (p->buf_to_file && b->start == p->buf_to_file->start) {
b->pos = p->buf_to_file->last;
b->last = p->buf_to_file->last;
} else {
b->pos = b->start;
b->last = b->start;
}
b->shadow = NULL;
cl->buf = b;
if (p->free_raw_bufs == NULL) {
p->free_raw_bufs = cl;
cl->next = NULL;
return NGX_OK;
}
if (p->free_raw_bufs->buf->pos == p->free_raw_bufs->buf->last) {
/* add the free buf to the list start */
cl->next = p->free_raw_bufs;
p->free_raw_bufs = cl;
return NGX_OK;
}
/* the first free buf is partially filled, thus add the free buf after it */
cl->next = p->free_raw_bufs->next;
p->free_raw_bufs->next = cl;
return NGX_OK;
}
static ngx_int_t
ngx_event_pipe_drain_chains(ngx_event_pipe_t *p)
{
ngx_chain_t *cl, *tl;
for ( ;; ) {
if (p->busy) {
cl = p->busy;
p->busy = NULL;
} else if (p->out) {
cl = p->out;
p->out = NULL;
} else if (p->in) {
cl = p->in;
p->in = NULL;
} else {
return NGX_OK;
}
while (cl) {
if (cl->buf->last_shadow) {
if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {
return NGX_ABORT;
}
cl->buf->last_shadow = 0;
}
cl->buf->shadow = NULL;
tl = cl->next;
cl->next = p->free;
p->free = cl;
cl = tl;
}
}
}
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_PIPE_H_INCLUDED_
#define _NGX_EVENT_PIPE_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
typedef struct ngx_event_pipe_s ngx_event_pipe_t;
typedef ngx_int_t (*ngx_event_pipe_input_filter_pt)(ngx_event_pipe_t *p,
ngx_buf_t *buf);
typedef ngx_int_t (*ngx_event_pipe_output_filter_pt)(void *data,
ngx_chain_t *chain);
struct ngx_event_pipe_s {
ngx_connection_t *upstream;
ngx_connection_t *downstream;
ngx_chain_t *free_raw_bufs;
ngx_chain_t *in;
ngx_chain_t **last_in;
ngx_chain_t *writing;
ngx_chain_t *out;
ngx_chain_t *free;
ngx_chain_t *busy;
/*
* the input filter i.e. that moves HTTP/1.1 chunks
* from the raw bufs to an incoming chain
*/
ngx_event_pipe_input_filter_pt input_filter;
void *input_ctx;
ngx_event_pipe_output_filter_pt output_filter;
void *output_ctx;
#if (NGX_THREADS || NGX_COMPAT)
ngx_int_t (*thread_handler)(ngx_thread_task_t *task,
ngx_file_t *file);
void *thread_ctx;
ngx_thread_task_t *thread_task;
#endif
unsigned read:1;
unsigned cacheable:1;
unsigned single_buf:1;
unsigned free_bufs:1;
unsigned upstream_done:1;
unsigned upstream_error:1;
unsigned upstream_eof:1;
unsigned upstream_blocked:1;
unsigned downstream_done:1;
unsigned downstream_error:1;
unsigned cyclic_temp_file:1;
unsigned aio:1;
ngx_int_t allocated;
ngx_bufs_t bufs;
ngx_buf_tag_t tag;
ssize_t busy_size;
off_t read_length;
off_t length;
off_t max_temp_file_size;
ssize_t temp_file_write_size;
ngx_msec_t read_timeout;
ngx_msec_t send_timeout;
ssize_t send_lowat;
ngx_pool_t *pool;
ngx_log_t *log;
ngx_chain_t *preread_bufs;
size_t preread_size;
ngx_buf_t *buf_to_file;
size_t limit_rate;
time_t start_sec;
ngx_temp_file_t *temp_file;
/* STUB */ int num;
};
ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write);
ngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf);
ngx_int_t ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b);
#endif /* _NGX_EVENT_PIPE_H_INCLUDED_ */
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
ngx_queue_t ngx_posted_accept_events;
ngx_queue_t ngx_posted_events;
void
ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted)
{
ngx_queue_t *q;
ngx_event_t *ev;
while (!ngx_queue_empty(posted)) {
q = ngx_queue_head(posted);
ev = ngx_queue_data(q, ngx_event_t, queue);
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
"posted event %p", ev);
ngx_delete_posted_event(ev);
ev->handler(ev);
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_POSTED_H_INCLUDED_
#define _NGX_EVENT_POSTED_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#define ngx_post_event(ev, q) \
\
if (!(ev)->posted) { \
(ev)->posted = 1; \
ngx_queue_insert_tail(q, &(ev)->queue); \
\
ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0, "post event %p", ev);\
\
} else { \
ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0, \
"update posted event %p", ev); \
}
#define ngx_delete_posted_event(ev) \
\
(ev)->posted = 0; \
ngx_queue_remove(&(ev)->queue); \
\
ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0, \
"delete posted event %p", ev);
void ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted);
extern ngx_queue_t ngx_posted_accept_events;
extern ngx_queue_t ngx_posted_events;
#endif /* _NGX_EVENT_POSTED_H_INCLUDED_ */
+126
View File
@@ -0,0 +1,126 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
ngx_rbtree_t ngx_event_timer_rbtree;
static ngx_rbtree_node_t ngx_event_timer_sentinel;
/*
* the event timer rbtree may contain the duplicate keys, however,
* it should not be a problem, because we use the rbtree to find
* a minimum timer value only
*/
ngx_int_t
ngx_event_timer_init(ngx_log_t *log)
{
ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,
ngx_rbtree_insert_timer_value);
return NGX_OK;
}
ngx_msec_t
ngx_event_find_timer(void)
{
ngx_msec_int_t timer;
ngx_rbtree_node_t *node, *root, *sentinel;
if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) {
return NGX_TIMER_INFINITE;
}
root = ngx_event_timer_rbtree.root;
sentinel = ngx_event_timer_rbtree.sentinel;
node = ngx_rbtree_min(root, sentinel);
timer = (ngx_msec_int_t) (node->key - ngx_current_msec);
return (ngx_msec_t) (timer > 0 ? timer : 0);
}
void
ngx_event_expire_timers(void)
{
ngx_event_t *ev;
ngx_rbtree_node_t *node, *root, *sentinel;
sentinel = ngx_event_timer_rbtree.sentinel;
for ( ;; ) {
root = ngx_event_timer_rbtree.root;
if (root == sentinel) {
return;
}
node = ngx_rbtree_min(root, sentinel);
/* node->key > ngx_current_msec */
if ((ngx_msec_int_t) (node->key - ngx_current_msec) > 0) {
return;
}
ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"event timer del: %d: %M",
ngx_event_ident(ev->data), ev->timer.key);
ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);
#if (NGX_DEBUG)
ev->timer.left = NULL;
ev->timer.right = NULL;
ev->timer.parent = NULL;
#endif
ev->timer_set = 0;
ev->timedout = 1;
ev->handler(ev);
}
}
ngx_int_t
ngx_event_no_timers_left(void)
{
ngx_event_t *ev;
ngx_rbtree_node_t *node, *root, *sentinel;
sentinel = ngx_event_timer_rbtree.sentinel;
root = ngx_event_timer_rbtree.root;
if (root == sentinel) {
return NGX_OK;
}
for (node = ngx_rbtree_min(root, sentinel);
node;
node = ngx_rbtree_next(&ngx_event_timer_rbtree, node))
{
ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
if (!ev->cancelable) {
return NGX_AGAIN;
}
}
/* only cancelable timers left */
return NGX_OK;
}
+90
View File
@@ -0,0 +1,90 @@
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_EVENT_TIMER_H_INCLUDED_
#define _NGX_EVENT_TIMER_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#define NGX_TIMER_INFINITE (ngx_msec_t) -1
#define NGX_TIMER_LAZY_DELAY 300
ngx_int_t ngx_event_timer_init(ngx_log_t *log);
ngx_msec_t ngx_event_find_timer(void);
void ngx_event_expire_timers(void);
ngx_int_t ngx_event_no_timers_left(void);
extern ngx_rbtree_t ngx_event_timer_rbtree;
static ngx_inline void
ngx_event_del_timer(ngx_event_t *ev)
{
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"event timer del: %d: %M",
ngx_event_ident(ev->data), ev->timer.key);
ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);
#if (NGX_DEBUG)
ev->timer.left = NULL;
ev->timer.right = NULL;
ev->timer.parent = NULL;
#endif
ev->timer_set = 0;
}
static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
ngx_msec_t key;
ngx_msec_int_t diff;
key = ngx_current_msec + timer;
if (ev->timer_set) {
/*
* Use a previous timer value if difference between it and a new
* value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows
* to minimize the rbtree operations for fast connections.
*/
diff = (ngx_msec_int_t) (key - ev->timer.key);
if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) {
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"event timer: %d, old: %M, new: %M",
ngx_event_ident(ev->data), ev->timer.key, key);
return;
}
ngx_del_timer(ev);
}
ev->timer.key = key;
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"event timer add: %d: %M:%M",
ngx_event_ident(ev->data), timer, ev->timer.key);
ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
ev->timer_set = 1;
}
#endif /* _NGX_EVENT_TIMER_H_INCLUDED_ */