xref: /unit/src/nxt_listen_socket.c (revision 1451:0a8840921fd0)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru static u_char *nxt_listen_socket_log_handler(void *ctx, u_char *pos,
110Sigor@sysoev.ru     u_char *last);
120Sigor@sysoev.ru 
130Sigor@sysoev.ru 
14115Sigor@sysoev.ru nxt_int_t
nxt_listen_socket(nxt_task_t * task,nxt_socket_t s,int backlog)15115Sigor@sysoev.ru nxt_listen_socket(nxt_task_t *task, nxt_socket_t s, int backlog)
16115Sigor@sysoev.ru {
17115Sigor@sysoev.ru     nxt_debug(task, "listen(%d, %d)", s, backlog);
18115Sigor@sysoev.ru 
19115Sigor@sysoev.ru     if (nxt_fast_path(listen(s, backlog) == 0)) {
20115Sigor@sysoev.ru         return NXT_OK;
21115Sigor@sysoev.ru     }
22115Sigor@sysoev.ru 
23564Svbart@nginx.com     nxt_alert(task, "listen(%d, %d) failed %E", s, backlog, nxt_socket_errno);
24115Sigor@sysoev.ru 
25115Sigor@sysoev.ru     return NXT_ERROR;
26115Sigor@sysoev.ru }
27115Sigor@sysoev.ru 
28115Sigor@sysoev.ru 
290Sigor@sysoev.ru nxt_int_t
nxt_listen_socket_create(nxt_task_t * task,nxt_mp_t * mp,nxt_listen_socket_t * ls)30*1451Svbart@nginx.com nxt_listen_socket_create(nxt_task_t *task, nxt_mp_t *mp,
31*1451Svbart@nginx.com     nxt_listen_socket_t *ls)
320Sigor@sysoev.ru {
33*1451Svbart@nginx.com     nxt_log_t          log, *old;
34*1451Svbart@nginx.com     nxt_uint_t         family;
35*1451Svbart@nginx.com     nxt_socket_t       s;
36*1451Svbart@nginx.com     nxt_thread_t       *thr;
37*1451Svbart@nginx.com     nxt_sockaddr_t     *sa;
38*1451Svbart@nginx.com #if (NXT_HAVE_UNIX_DOMAIN)
39*1451Svbart@nginx.com     int                ret;
40*1451Svbart@nginx.com     u_char             *p;
41*1451Svbart@nginx.com     nxt_err_t          err;
42*1451Svbart@nginx.com     nxt_socket_t       ts;
43*1451Svbart@nginx.com     nxt_sockaddr_t     *orig_sa;
44*1451Svbart@nginx.com     nxt_file_name_t    *name, *tmp;
45*1451Svbart@nginx.com     nxt_file_access_t  access;
46*1451Svbart@nginx.com #endif
470Sigor@sysoev.ru 
480Sigor@sysoev.ru     sa = ls->sockaddr;
490Sigor@sysoev.ru 
500Sigor@sysoev.ru     thr = nxt_thread();
510Sigor@sysoev.ru     old = thr->log;
520Sigor@sysoev.ru     log = *thr->log;
530Sigor@sysoev.ru     log.ctx_handler = nxt_listen_socket_log_handler;
540Sigor@sysoev.ru     log.ctx = sa;
550Sigor@sysoev.ru     thr->log = &log;
560Sigor@sysoev.ru 
570Sigor@sysoev.ru     family = sa->u.sockaddr.sa_family;
580Sigor@sysoev.ru 
5913Sigor@sysoev.ru     s = nxt_socket_create(task, family, sa->type, 0, ls->flags);
600Sigor@sysoev.ru     if (s == -1) {
611450Svbart@nginx.com         goto fail;
620Sigor@sysoev.ru     }
630Sigor@sysoev.ru 
6413Sigor@sysoev.ru     if (nxt_socket_setsockopt(task, s, SOL_SOCKET, SO_REUSEADDR, 1) != NXT_OK) {
650Sigor@sysoev.ru         goto fail;
660Sigor@sysoev.ru     }
670Sigor@sysoev.ru 
680Sigor@sysoev.ru #if (NXT_INET6 && defined IPV6_V6ONLY)
690Sigor@sysoev.ru 
700Sigor@sysoev.ru     if (family == AF_INET6 && ls->ipv6only) {
710Sigor@sysoev.ru         int  ipv6only;
720Sigor@sysoev.ru 
730Sigor@sysoev.ru         ipv6only = (ls->ipv6only == 1);
740Sigor@sysoev.ru 
750Sigor@sysoev.ru         /* Ignore possible error. TODO: why? */
7613Sigor@sysoev.ru         (void) nxt_socket_setsockopt(task, s, IPPROTO_IPV6, IPV6_V6ONLY,
7713Sigor@sysoev.ru                                      ipv6only);
780Sigor@sysoev.ru     }
790Sigor@sysoev.ru 
800Sigor@sysoev.ru #endif
810Sigor@sysoev.ru 
820Sigor@sysoev.ru #if 0
830Sigor@sysoev.ru 
840Sigor@sysoev.ru     /* Ignore possible error. TODO: why? */
8513Sigor@sysoev.ru     (void) nxt_socket_setsockopt(task, s, SOL_SOCKET, SO_SNDBUF, 8192);
860Sigor@sysoev.ru 
870Sigor@sysoev.ru #endif
880Sigor@sysoev.ru 
890Sigor@sysoev.ru     if (ls->read_after_accept) {
90229Sigor@sysoev.ru         nxt_socket_defer_accept(task, s, sa);
910Sigor@sysoev.ru     }
920Sigor@sysoev.ru 
93*1451Svbart@nginx.com #if (NXT_HAVE_UNIX_DOMAIN)
94*1451Svbart@nginx.com 
95*1451Svbart@nginx.com     if (family == AF_UNIX
96*1451Svbart@nginx.com         && sa->type == SOCK_STREAM
97*1451Svbart@nginx.com         && sa->u.sockaddr_un.sun_path[0] != '\0')
98*1451Svbart@nginx.com     {
99*1451Svbart@nginx.com         orig_sa = sa;
100*1451Svbart@nginx.com 
101*1451Svbart@nginx.com         sa = nxt_sockaddr_alloc(mp, sa->socklen + 4, sa->length + 4);
102*1451Svbart@nginx.com         if (sa == NULL) {
103*1451Svbart@nginx.com             goto fail;
104*1451Svbart@nginx.com         }
105*1451Svbart@nginx.com 
106*1451Svbart@nginx.com         sa->type = SOCK_STREAM;
107*1451Svbart@nginx.com         sa->u.sockaddr_un.sun_family = AF_UNIX;
108*1451Svbart@nginx.com 
109*1451Svbart@nginx.com         p = nxt_cpystr((u_char *) sa->u.sockaddr_un.sun_path,
110*1451Svbart@nginx.com                        (u_char *) orig_sa->u.sockaddr_un.sun_path);
111*1451Svbart@nginx.com         nxt_memcpy(p, ".tmp", 4);
112*1451Svbart@nginx.com 
113*1451Svbart@nginx.com         nxt_sockaddr_text(sa);
114*1451Svbart@nginx.com 
115*1451Svbart@nginx.com         (void) unlink(sa->u.sockaddr_un.sun_path);
116*1451Svbart@nginx.com 
117*1451Svbart@nginx.com     } else {
118*1451Svbart@nginx.com         orig_sa = NULL;
119*1451Svbart@nginx.com     }
120*1451Svbart@nginx.com 
121*1451Svbart@nginx.com #endif
122*1451Svbart@nginx.com 
1231449Svbart@nginx.com     if (nxt_socket_bind(task, s, sa) != NXT_OK) {
1240Sigor@sysoev.ru         goto fail;
1250Sigor@sysoev.ru     }
1260Sigor@sysoev.ru 
1270Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru     if (family == AF_UNIX) {
1300Sigor@sysoev.ru         name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path;
1310Sigor@sysoev.ru 
132234Sigor@sysoev.ru         access = (S_IRUSR | S_IWUSR);
1330Sigor@sysoev.ru 
1340Sigor@sysoev.ru         if (nxt_file_set_access(name, access) != NXT_OK) {
1351450Svbart@nginx.com             goto listen_fail;
1360Sigor@sysoev.ru         }
1370Sigor@sysoev.ru     }
1380Sigor@sysoev.ru 
1390Sigor@sysoev.ru #endif
1400Sigor@sysoev.ru 
14113Sigor@sysoev.ru     nxt_debug(task, "listen(%d, %d)", s, ls->backlog);
1420Sigor@sysoev.ru 
1430Sigor@sysoev.ru     if (listen(s, ls->backlog) != 0) {
144564Svbart@nginx.com         nxt_alert(task, "listen(%d, %d) failed %E",
145564Svbart@nginx.com                   s, ls->backlog, nxt_socket_errno);
1461450Svbart@nginx.com         goto listen_fail;
1470Sigor@sysoev.ru     }
1480Sigor@sysoev.ru 
149*1451Svbart@nginx.com #if (NXT_HAVE_UNIX_DOMAIN)
150*1451Svbart@nginx.com 
151*1451Svbart@nginx.com     if (orig_sa != NULL) {
152*1451Svbart@nginx.com         ts = nxt_socket_create(task, AF_UNIX, SOCK_STREAM, 0, 0);
153*1451Svbart@nginx.com         if (ts == -1) {
154*1451Svbart@nginx.com             goto listen_fail;
155*1451Svbart@nginx.com         }
156*1451Svbart@nginx.com 
157*1451Svbart@nginx.com         ret = connect(ts, &orig_sa->u.sockaddr, orig_sa->socklen);
158*1451Svbart@nginx.com 
159*1451Svbart@nginx.com         err = nxt_socket_errno;
160*1451Svbart@nginx.com 
161*1451Svbart@nginx.com         nxt_socket_close(task, ts);
162*1451Svbart@nginx.com 
163*1451Svbart@nginx.com         if (ret == 0) {
164*1451Svbart@nginx.com             nxt_alert(task, "connect(%d, %*s) succeed, address already in use",
165*1451Svbart@nginx.com                       ts, (size_t) orig_sa->length,
166*1451Svbart@nginx.com                       nxt_sockaddr_start(orig_sa));
167*1451Svbart@nginx.com 
168*1451Svbart@nginx.com             goto listen_fail;
169*1451Svbart@nginx.com         }
170*1451Svbart@nginx.com 
171*1451Svbart@nginx.com         if (err != NXT_ENOENT && err != NXT_ECONNREFUSED) {
172*1451Svbart@nginx.com             nxt_alert(task, "connect(%d, %*s) failed %E",
173*1451Svbart@nginx.com                       ts, (size_t) orig_sa->length,
174*1451Svbart@nginx.com                       nxt_sockaddr_start(orig_sa), err);
175*1451Svbart@nginx.com 
176*1451Svbart@nginx.com             goto listen_fail;
177*1451Svbart@nginx.com         }
178*1451Svbart@nginx.com 
179*1451Svbart@nginx.com         tmp = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path;
180*1451Svbart@nginx.com         name = (nxt_file_name_t *) orig_sa->u.sockaddr_un.sun_path;
181*1451Svbart@nginx.com 
182*1451Svbart@nginx.com         if (nxt_file_rename(tmp, name) != NXT_OK) {
183*1451Svbart@nginx.com             goto listen_fail;
184*1451Svbart@nginx.com         }
185*1451Svbart@nginx.com     }
186*1451Svbart@nginx.com 
187*1451Svbart@nginx.com #endif
188*1451Svbart@nginx.com 
1890Sigor@sysoev.ru     ls->socket = s;
1900Sigor@sysoev.ru     thr->log = old;
1910Sigor@sysoev.ru 
1920Sigor@sysoev.ru     return NXT_OK;
1930Sigor@sysoev.ru 
1941450Svbart@nginx.com listen_fail:
1951450Svbart@nginx.com 
1961450Svbart@nginx.com #if (NXT_HAVE_UNIX_DOMAIN)
1971450Svbart@nginx.com 
1981450Svbart@nginx.com     if (family == AF_UNIX) {
1991450Svbart@nginx.com         name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path;
2001450Svbart@nginx.com 
2011450Svbart@nginx.com         (void) nxt_file_delete(name);
2021450Svbart@nginx.com     }
2031450Svbart@nginx.com 
2041450Svbart@nginx.com #endif
2051450Svbart@nginx.com 
2060Sigor@sysoev.ru fail:
2070Sigor@sysoev.ru 
2081450Svbart@nginx.com     if (s != -1) {
2091450Svbart@nginx.com         nxt_socket_close(task, s);
2101450Svbart@nginx.com     }
2110Sigor@sysoev.ru 
2120Sigor@sysoev.ru     thr->log = old;
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru     return NXT_ERROR;
2150Sigor@sysoev.ru }
2160Sigor@sysoev.ru 
2170Sigor@sysoev.ru 
2180Sigor@sysoev.ru nxt_int_t
nxt_listen_socket_update(nxt_task_t * task,nxt_listen_socket_t * ls,nxt_listen_socket_t * prev)21913Sigor@sysoev.ru nxt_listen_socket_update(nxt_task_t *task, nxt_listen_socket_t *ls,
22013Sigor@sysoev.ru     nxt_listen_socket_t *prev)
2210Sigor@sysoev.ru {
2220Sigor@sysoev.ru     nxt_log_t     log, *old;
2230Sigor@sysoev.ru     nxt_thread_t  *thr;
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru     ls->socket = prev->socket;
2260Sigor@sysoev.ru 
2270Sigor@sysoev.ru     thr = nxt_thread();
2280Sigor@sysoev.ru     old = thr->log;
2290Sigor@sysoev.ru     log = *thr->log;
2300Sigor@sysoev.ru     log.ctx_handler = nxt_listen_socket_log_handler;
2310Sigor@sysoev.ru     log.ctx = ls->sockaddr;
2320Sigor@sysoev.ru     thr->log = &log;
2330Sigor@sysoev.ru 
23413Sigor@sysoev.ru     nxt_debug(task, "listen(%d, %d)", ls->socket, ls->backlog);
2350Sigor@sysoev.ru 
2360Sigor@sysoev.ru     if (listen(ls->socket, ls->backlog) != 0) {
237564Svbart@nginx.com         nxt_alert(task, "listen(%d, %d) failed %E",
238564Svbart@nginx.com                   ls->socket, ls->backlog, nxt_socket_errno);
2390Sigor@sysoev.ru         goto fail;
2400Sigor@sysoev.ru     }
2410Sigor@sysoev.ru 
2420Sigor@sysoev.ru     thr->log = old;
2430Sigor@sysoev.ru 
2440Sigor@sysoev.ru     return NXT_OK;
2450Sigor@sysoev.ru 
2460Sigor@sysoev.ru fail:
2470Sigor@sysoev.ru 
2480Sigor@sysoev.ru     thr->log = old;
2490Sigor@sysoev.ru 
2500Sigor@sysoev.ru     return NXT_ERROR;
2510Sigor@sysoev.ru }
2520Sigor@sysoev.ru 
2530Sigor@sysoev.ru 
254312Sigor@sysoev.ru void
nxt_listen_socket_remote_size(nxt_listen_socket_t * ls)255359Sigor@sysoev.ru nxt_listen_socket_remote_size(nxt_listen_socket_t *ls)
256312Sigor@sysoev.ru {
257359Sigor@sysoev.ru     switch (ls->sockaddr->u.sockaddr.sa_family) {
258312Sigor@sysoev.ru 
259312Sigor@sysoev.ru #if (NXT_INET6)
260312Sigor@sysoev.ru 
261312Sigor@sysoev.ru     case AF_INET6:
262312Sigor@sysoev.ru         ls->socklen = sizeof(struct sockaddr_in6);
263312Sigor@sysoev.ru         ls->address_length = NXT_INET6_ADDR_STR_LEN;
264312Sigor@sysoev.ru 
265312Sigor@sysoev.ru         break;
266312Sigor@sysoev.ru 
267312Sigor@sysoev.ru #endif
268312Sigor@sysoev.ru 
269312Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
270312Sigor@sysoev.ru 
271312Sigor@sysoev.ru     case AF_UNIX:
272312Sigor@sysoev.ru         /*
273312Sigor@sysoev.ru          * A remote socket is usually unbound and thus has unspecified Unix
274312Sigor@sysoev.ru          * domain sockaddr_un which can be shortcut to 3 bytes.  To handle
275312Sigor@sysoev.ru          * a bound remote socket correctly ls->socklen should be larger, see
276312Sigor@sysoev.ru          * comment in nxt_socket.h.
277312Sigor@sysoev.ru          */
278312Sigor@sysoev.ru         ls->socklen = offsetof(struct sockaddr_un, sun_path) + 1;
279703Svbart@nginx.com         ls->address_length = nxt_length("unix:");
280312Sigor@sysoev.ru 
281312Sigor@sysoev.ru         break;
282312Sigor@sysoev.ru 
283312Sigor@sysoev.ru #endif
284312Sigor@sysoev.ru 
285312Sigor@sysoev.ru     default:
286312Sigor@sysoev.ru     case AF_INET:
287312Sigor@sysoev.ru         ls->socklen = sizeof(struct sockaddr_in);
288312Sigor@sysoev.ru         ls->address_length = NXT_INET_ADDR_STR_LEN;
289312Sigor@sysoev.ru 
290312Sigor@sysoev.ru         break;
291312Sigor@sysoev.ru     }
292312Sigor@sysoev.ru }
293312Sigor@sysoev.ru 
294312Sigor@sysoev.ru 
2950Sigor@sysoev.ru size_t
nxt_listen_socket_pool_min_size(nxt_listen_socket_t * ls)2960Sigor@sysoev.ru nxt_listen_socket_pool_min_size(nxt_listen_socket_t *ls)
2970Sigor@sysoev.ru {
2980Sigor@sysoev.ru     size_t  size;
2990Sigor@sysoev.ru 
3000Sigor@sysoev.ru     /*
3010Sigor@sysoev.ru      * The first nxt_sockaddr_t is intended for mandatory remote sockaddr
3020Sigor@sysoev.ru      * and textual representaion with port.  The second nxt_sockaddr_t
3030Sigor@sysoev.ru      * is intended for local sockaddr without textual representaion which
3040Sigor@sysoev.ru      * may be required to get specific address of connection received on
3050Sigor@sysoev.ru      * wildcard AF_INET and AF_INET6 addresses.  For AF_UNIX addresses
3060Sigor@sysoev.ru      * the local sockaddr is not required.
3070Sigor@sysoev.ru      */
3080Sigor@sysoev.ru 
3090Sigor@sysoev.ru     switch (ls->sockaddr->u.sockaddr.sa_family) {
3100Sigor@sysoev.ru 
3110Sigor@sysoev.ru #if (NXT_INET6)
3120Sigor@sysoev.ru 
3130Sigor@sysoev.ru     case AF_INET6:
3140Sigor@sysoev.ru         ls->socklen = sizeof(struct sockaddr_in6);
31513Sigor@sysoev.ru         ls->address_length = NXT_INET6_ADDR_STR_LEN;
3160Sigor@sysoev.ru 
3170Sigor@sysoev.ru         size = offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in6)
318703Svbart@nginx.com                + NXT_INET6_ADDR_STR_LEN + nxt_length(":65535");
3190Sigor@sysoev.ru 
3200Sigor@sysoev.ru         if (IN6_IS_ADDR_UNSPECIFIED(&ls->sockaddr->u.sockaddr_in6.sin6_addr)) {
3210Sigor@sysoev.ru             size += offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in6);
3220Sigor@sysoev.ru         }
3230Sigor@sysoev.ru 
3240Sigor@sysoev.ru         break;
3250Sigor@sysoev.ru 
3260Sigor@sysoev.ru #endif
3270Sigor@sysoev.ru 
3280Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
3290Sigor@sysoev.ru 
3300Sigor@sysoev.ru     case AF_UNIX:
3310Sigor@sysoev.ru         /*
3320Sigor@sysoev.ru          * A remote socket is usually unbound and thus has unspecified Unix
3330Sigor@sysoev.ru          * domain sockaddr_un which can be shortcut to 3 bytes.  To handle
3340Sigor@sysoev.ru          * a bound remote socket correctly ls->socklen should be at least
335211Sru@nginx.com          * sizeof(struct sockaddr_un), see comment in nxt_socket.h.
3360Sigor@sysoev.ru          */
3370Sigor@sysoev.ru         ls->socklen = 3;
338703Svbart@nginx.com         size = ls->socklen + nxt_length("unix:");
339703Svbart@nginx.com         ls->address_length = nxt_length("unix:");
3400Sigor@sysoev.ru 
3410Sigor@sysoev.ru         break;
3420Sigor@sysoev.ru 
3430Sigor@sysoev.ru #endif
3440Sigor@sysoev.ru 
3450Sigor@sysoev.ru     default:
3460Sigor@sysoev.ru         ls->socklen = sizeof(struct sockaddr_in);
34713Sigor@sysoev.ru         ls->address_length = NXT_INET_ADDR_STR_LEN;
3480Sigor@sysoev.ru 
3490Sigor@sysoev.ru         size = offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in)
350703Svbart@nginx.com                + NXT_INET_ADDR_STR_LEN + nxt_length(":65535");
3510Sigor@sysoev.ru 
3520Sigor@sysoev.ru         if (ls->sockaddr->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) {
3530Sigor@sysoev.ru             size += offsetof(nxt_sockaddr_t, u) + sizeof(struct sockaddr_in);
3540Sigor@sysoev.ru         }
3550Sigor@sysoev.ru 
3560Sigor@sysoev.ru         break;
3570Sigor@sysoev.ru     }
3580Sigor@sysoev.ru 
359771Sigor@sysoev.ru #if (NXT_TLS)
3600Sigor@sysoev.ru 
361771Sigor@sysoev.ru     if (ls->tls) {
362771Sigor@sysoev.ru         size += 4 * sizeof(void *)       /* SSL/TLS connection */
3630Sigor@sysoev.ru                 + sizeof(nxt_buf_mem_t)
364771Sigor@sysoev.ru                 + sizeof(nxt_work_t);    /* nxt_mp_cleanup */
3650Sigor@sysoev.ru     }
3660Sigor@sysoev.ru 
3670Sigor@sysoev.ru #endif
3680Sigor@sysoev.ru 
36965Sigor@sysoev.ru     return size // + sizeof(nxt_mem_pool_t)
37062Sigor@sysoev.ru                 + sizeof(nxt_conn_t)
3710Sigor@sysoev.ru                 + sizeof(nxt_log_t);
3720Sigor@sysoev.ru }
3730Sigor@sysoev.ru 
3740Sigor@sysoev.ru 
3750Sigor@sysoev.ru static u_char *
nxt_listen_socket_log_handler(void * ctx,u_char * pos,u_char * end)3760Sigor@sysoev.ru nxt_listen_socket_log_handler(void *ctx, u_char *pos, u_char *end)
3770Sigor@sysoev.ru {
3780Sigor@sysoev.ru     nxt_sockaddr_t  *sa;
3790Sigor@sysoev.ru 
3800Sigor@sysoev.ru     sa = ctx;
3810Sigor@sysoev.ru 
3820Sigor@sysoev.ru     return nxt_sprintf(pos, end, " while creating listening socket on %*s",
383494Spluknet@nginx.com                        (size_t) sa->length, nxt_sockaddr_start(sa));
3840Sigor@sysoev.ru }
385