Mercurial > hg > nginx-mail
view src/core/ngx_connection.c @ 436:9b19e26b2660
Mail: smtp pipelining support.
Basically, this does the following two changes (and corresponding
modifications of related code):
1. Does not reset session buffer unless it's reached it's end, and always
wait for LF to terminate command (even if we detected invalid command).
2. Record command name as the first argument to make it available for
handlers (since now we can't assume that command starts from s->buffer->start).
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Thu, 11 Sep 2008 15:26:25 +0400 |
parents | 6ebbca3d5ed7 |
children | b246022ef454 |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> ngx_os_io_t ngx_io; ngx_listening_t * ngx_listening_inet_stream_socket(ngx_conf_t *cf, in_addr_t addr, in_port_t port) { size_t len; ngx_listening_t *ls; struct sockaddr_in *sin; ls = ngx_array_push(&cf->cycle->listening); if (ls == NULL) { return NULL; } ngx_memzero(ls, sizeof(ngx_listening_t)); sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in)); if (sin == NULL) { return NULL; } sin->sin_family = AF_INET; sin->sin_addr.s_addr = addr; sin->sin_port = htons(port); ls->addr_text.data = ngx_pnalloc(cf->pool, NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1); if (ls->addr_text.data == NULL) { return NULL; } len = ngx_inet_ntop(AF_INET, &addr, ls->addr_text.data, NGX_INET_ADDRSTRLEN); ls->addr_text.len = ngx_sprintf(ls->addr_text.data + len, ":%d", port) - ls->addr_text.data; ls->fd = (ngx_socket_t) -1; ls->type = SOCK_STREAM; ls->sockaddr = (struct sockaddr *) sin; ls->socklen = sizeof(struct sockaddr_in); ls->addr = offsetof(struct sockaddr_in, sin_addr); ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; return ls; } ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle) { size_t len; ngx_uint_t i; ngx_listening_t *ls; struct sockaddr_in *sin; socklen_t olen; #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) ngx_err_t err; struct accept_filter_arg af; #endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) int timeout; #endif ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { /* AF_INET only */ ls[i].sockaddr = ngx_palloc(cycle->pool, sizeof(struct sockaddr_in)); if (ls[i].sockaddr == NULL) { return NGX_ERROR; } ls[i].socklen = sizeof(struct sockaddr_in); if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) { ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, "getsockname() of the inherited " "socket #%d failed", ls[i].fd); ls[i].ignore = 1; continue; } sin = (struct sockaddr_in *) ls[i].sockaddr; if (sin->sin_family != AF_INET) { ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, "the inherited socket #%d has " "unsupported family", ls[i].fd); ls[i].ignore = 1; continue; } ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN; ls[i].addr_text.data = ngx_pnalloc(cycle->pool, NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1); if (ls[i].addr_text.data == NULL) { return NGX_ERROR; } len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, NGX_INET_ADDRSTRLEN); if (len == 0) { return NGX_ERROR; } ls[i].addr_text.len = ngx_sprintf(ls[i].addr_text.data + len, ":%d", ntohs(sin->sin_port)) - ls[i].addr_text.data; ls[i].backlog = NGX_LISTEN_BACKLOG; olen = sizeof(int); if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf, &olen) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, "getsockopt(SO_RCVBUF) %V failed, ignored", &ls[i].addr_text); ls[i].rcvbuf = -1; } olen = sizeof(int); if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf, &olen) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, "getsockopt(SO_SNDBUF) %V failed, ignored", &ls[i].addr_text); ls[i].sndbuf = -1; } #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) ngx_memzero(&af, sizeof(struct accept_filter_arg)); olen = sizeof(struct accept_filter_arg); if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen) == -1) { err = ngx_errno; if (err == NGX_EINVAL) { continue; } ngx_log_error(NGX_LOG_NOTICE, cycle->log, err, "getsockopt(SO_ACCEPTFILTER) for %V failed, ignored", &ls[i].addr_text); continue; } if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\0') { continue; } ls[i].accept_filter = ngx_palloc(cycle->pool, 16); if (ls[i].accept_filter == NULL) { return NGX_ERROR; } (void) ngx_cpystrn((u_char *) ls[i].accept_filter, (u_char *) af.af_name, 16); #endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) timeout = 0; olen = sizeof(int); if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen) == -1) { ngx_log_error(NGX_LOG_NOTICE, cycle->log, ngx_errno, "getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored", &ls[i].addr_text); continue; } if (olen < sizeof(int) || timeout == 0) { continue; } ls[i].deferred_accept = 1; #endif } return NGX_OK; } ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle) { int reuseaddr; ngx_uint_t i, tries, failed; ngx_err_t err; ngx_log_t *log; ngx_socket_t s; ngx_listening_t *ls; reuseaddr = 1; #if (NGX_SUPPRESS_WARN) failed = 0; #endif log = cycle->log; /* TODO: configurable try number */ for (tries = 5; tries; tries--) { failed = 0; /* for each listening socket */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { if (ls[i].ignore) { continue; } if (ls[i].fd != -1) { continue; } if (ls[i].inherited) { /* TODO: close on exit */ /* TODO: nonblocking */ /* TODO: deferred accept */ continue; } s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0); if (s == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_socket_n " %V failed", &ls[i].addr_text); return NGX_ERROR; } if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &reuseaddr, sizeof(int)) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, "setsockopt(SO_REUSEADDR) %V failed", &ls[i].addr_text); if (ngx_close_socket(s) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } return NGX_ERROR; } /* TODO: close on exit */ if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) { if (ngx_nonblocking(s) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_nonblocking_n " %V failed", &ls[i].addr_text); if (ngx_close_socket(s) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } return NGX_ERROR; } } ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, "bind() %V #%d ", &ls[i].addr_text, s); if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) { err = ngx_socket_errno; if (err == NGX_EADDRINUSE && ngx_test_config) { continue; } ngx_log_error(NGX_LOG_EMERG, log, err, "bind() to %V failed", &ls[i].addr_text); if (ngx_close_socket(s) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } if (err != NGX_EADDRINUSE) { return NGX_ERROR; } failed = 1; continue; } if (listen(s, ls[i].backlog) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, "listen() to %V, backlog %d failed", &ls[i].addr_text, ls[i].backlog); if (ngx_close_socket(s) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } return NGX_ERROR; } ls[i].listen = 1; ls[i].fd = s; } if (!failed) { break; } /* TODO: delay configurable */ ngx_log_error(NGX_LOG_NOTICE, log, 0, "try again to bind() after 500ms"); ngx_msleep(500); } if (failed) { ngx_log_error(NGX_LOG_EMERG, log, 0, "still could not bind()"); return NGX_ERROR; } return NGX_OK; } void ngx_configure_listening_socket(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_listening_t *ls; #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) struct accept_filter_arg af; #endif #if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) int timeout; #endif ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { if (ls[i].rcvbuf != -1) { if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (const void *) &ls[i].rcvbuf, sizeof(int)) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, "setsockopt(SO_RCVBUF, %d) %V failed, ignored", ls[i].rcvbuf, &ls[i].addr_text); } } if (ls[i].sndbuf != -1) { if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (const void *) &ls[i].sndbuf, sizeof(int)) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, "setsockopt(SO_SNDBUF, %d) %V failed, ignored", ls[i].sndbuf, &ls[i].addr_text); } } #if 0 if (1) { int tcp_nodelay = 1; if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &tcp_nodelay, sizeof(int)) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, "setsockopt(TCP_NODELAY) %V failed, ignored", &ls[i].addr_text); } } #endif if (ls[i].listen) { /* change backlog via listen() */ if (listen(ls[i].fd, ls[i].backlog) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, "listen() to %V, backlog %d failed, ignored", &ls[i].addr_text, ls[i].backlog); } } /* * setting deferred mode should be last operation on socket, * because code may prematurely continue cycle on failure */ #if (NGX_HAVE_DEFERRED_ACCEPT) #ifdef SO_ACCEPTFILTER if (ls[i].delete_deferred) { if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setsockopt(SO_ACCEPTFILTER, NULL) " "for %V failed, ignored", &ls[i].addr_text); if (ls[i].accept_filter) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "could not change the accept filter " "to \"%s\" for %V, ignored", ls[i].accept_filter, &ls[i].addr_text); } continue; } ls[i].deferred_accept = 0; } if (ls[i].add_deferred) { ngx_memzero(&af, sizeof(struct accept_filter_arg)); (void) ngx_cpystrn((u_char *) af.af_name, (u_char *) ls[i].accept_filter, 16); if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, sizeof(struct accept_filter_arg)) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setsockopt(SO_ACCEPTFILTER, \"%s\") " " for %V failed, ignored", ls[i].accept_filter, &ls[i].addr_text); continue; } ls[i].deferred_accept = 1; } #endif #ifdef TCP_DEFER_ACCEPT if (ls[i].add_deferred || ls[i].delete_deferred) { if (ls[i].add_deferred) { timeout = (int) (ls[i].post_accept_timeout / 1000); } else { timeout = 0; } if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(int)) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "setsockopt(TCP_DEFER_ACCEPT, %d) for %V failed, " "ignored", timeout, &ls[i].addr_text); continue; } } if (ls[i].add_deferred) { ls[i].deferred_accept = 1; } #endif #endif /* NGX_HAVE_DEFERRED_ACCEPT */ } return; } void ngx_close_listening_sockets(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_listening_t *ls; ngx_connection_t *c; if (ngx_event_flags & NGX_USE_IOCP_EVENT) { return; } ngx_accept_mutex_held = 0; ngx_use_accept_mutex = 0; ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { c = ls[i].connection; if (c->read->active) { if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { ngx_del_conn(c, NGX_CLOSE_EVENT); } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { /* * it seems that Linux-2.6.x OpenVZ sends events * for closed shared listening sockets unless * the events was explicity deleted */ ngx_del_event(c->read, NGX_READ_EVENT, 0); } else { ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); } } ngx_free_connection(c); c->fd = (ngx_socket_t) -1; ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, "close listening %V #%d ", &ls[i].addr_text, ls[i].fd); if (ngx_close_socket(ls[i].fd) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[i].addr_text); } } } ngx_connection_t * ngx_get_connection(ngx_socket_t s, ngx_log_t *log) { ngx_uint_t instance; ngx_event_t *rev, *wev; ngx_connection_t *c; /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */ if (ngx_cycle->files && (ngx_uint_t) s >= ngx_cycle->files_n) { ngx_log_error(NGX_LOG_ALERT, log, 0, "the new socket has number %d, " "but only %ui files are available", s, ngx_cycle->files_n); return NULL; } /* ngx_mutex_lock */ c = ngx_cycle->free_connections; if (c == NULL) { ngx_log_error(NGX_LOG_ALERT, log, 0, "%ui worker_connections is not enough", ngx_cycle->connection_n); /* ngx_mutex_unlock */ return NULL; } ngx_cycle->free_connections = c->data; ngx_cycle->free_connection_n--; /* ngx_mutex_unlock */ if (ngx_cycle->files) { ngx_cycle->files[s] = c; } rev = c->read; wev = c->write; ngx_memzero(c, sizeof(ngx_connection_t)); c->read = rev; c->write = wev; c->fd = s; c->log = log; instance = rev->instance; ngx_memzero(rev, sizeof(ngx_event_t)); ngx_memzero(wev, sizeof(ngx_event_t)); rev->instance = !instance; wev->instance = !instance; rev->index = NGX_INVALID_INDEX; wev->index = NGX_INVALID_INDEX; rev->data = c; wev->data = c; wev->write = 1; return c; } void ngx_free_connection(ngx_connection_t *c) { /* ngx_mutex_lock */ c->data = ngx_cycle->free_connections; ngx_cycle->free_connections = c; ngx_cycle->free_connection_n++; /* ngx_mutex_unlock */ if (ngx_cycle->files) { ngx_cycle->files[c->fd] = NULL; } } void ngx_close_connection(ngx_connection_t *c) { ngx_err_t err; ngx_uint_t log_error, level; ngx_socket_t fd; if (c->fd == -1) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed"); return; } if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { ngx_del_timer(c->write); } if (ngx_del_conn) { ngx_del_conn(c, NGX_CLOSE_EVENT); } else { if (c->read->active || c->read->disabled) { ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); } if (c->write->active || c->write->disabled) { ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); } } #if (NGX_THREADS) /* * we have to clean the connection information before the closing * because another thread may reopen the same file descriptor * before we clean the connection */ ngx_mutex_lock(ngx_posted_events_mutex); if (c->read->prev) { ngx_delete_posted_event(c->read); } if (c->write->prev) { ngx_delete_posted_event(c->write); } c->read->closed = 1; c->write->closed = 1; if (c->single_connection) { ngx_unlock(&c->lock); c->read->locked = 0; c->write->locked = 0; } ngx_mutex_unlock(ngx_posted_events_mutex); #else if (c->read->prev) { ngx_delete_posted_event(c->read); } if (c->write->prev) { ngx_delete_posted_event(c->write); } c->read->closed = 1; c->write->closed = 1; #endif log_error = c->log_error; ngx_free_connection(c); fd = c->fd; c->fd = (ngx_socket_t) -1; if (ngx_close_socket(fd) == -1) { err = ngx_socket_errno; if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) { switch (log_error) { case NGX_ERROR_INFO: level = NGX_LOG_INFO; break; case NGX_ERROR_ERR: level = NGX_LOG_ERR; break; default: level = NGX_LOG_CRIT; } } else { level = NGX_LOG_CRIT; } /* we use ngx_cycle->log because c->log was in c->pool */ ngx_log_error(level, ngx_cycle->log, err, ngx_close_socket_n " %d failed", fd); } } ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text) { ngx_uint_t level; if (err == NGX_ECONNRESET && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) { return 0; } if (err == 0 || err == NGX_ECONNRESET #if !(NGX_WIN32) || err == NGX_EPIPE #endif || 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: level = NGX_LOG_ALERT; } } else { level = NGX_LOG_ALERT; } ngx_log_error(level, c->log, err, text); return NGX_ERROR; }