xref: /unit/src/nxt_socket.c (revision 1504:737b84721dae)
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 const char *nxt_socket_sockopt_name(nxt_uint_t level,
110Sigor@sysoev.ru     nxt_uint_t sockopt);
120Sigor@sysoev.ru 
130Sigor@sysoev.ru 
140Sigor@sysoev.ru nxt_socket_t
nxt_socket_create(nxt_task_t * task,nxt_uint_t domain,nxt_uint_t type,nxt_uint_t protocol,nxt_uint_t flags)1513Sigor@sysoev.ru nxt_socket_create(nxt_task_t *task, nxt_uint_t domain, nxt_uint_t type,
1613Sigor@sysoev.ru     nxt_uint_t protocol, nxt_uint_t flags)
170Sigor@sysoev.ru {
180Sigor@sysoev.ru     nxt_socket_t  s;
190Sigor@sysoev.ru 
200Sigor@sysoev.ru #if (NXT_HAVE_SOCK_NONBLOCK)
210Sigor@sysoev.ru 
220Sigor@sysoev.ru     if (flags & NXT_NONBLOCK) {
230Sigor@sysoev.ru         type |= SOCK_NONBLOCK;
240Sigor@sysoev.ru     }
250Sigor@sysoev.ru 
260Sigor@sysoev.ru #endif
270Sigor@sysoev.ru 
280Sigor@sysoev.ru     s = socket(domain, type, protocol);
290Sigor@sysoev.ru 
300Sigor@sysoev.ru     if (nxt_slow_path(s == -1)) {
31564Svbart@nginx.com         nxt_alert(task, "socket(%ui, 0x%uXi, %ui) failed %E",
32564Svbart@nginx.com                   domain, type, protocol, nxt_socket_errno);
330Sigor@sysoev.ru         return s;
340Sigor@sysoev.ru     }
350Sigor@sysoev.ru 
3613Sigor@sysoev.ru     nxt_debug(task, "socket(): %d", s);
370Sigor@sysoev.ru 
380Sigor@sysoev.ru #if !(NXT_HAVE_SOCK_NONBLOCK)
390Sigor@sysoev.ru 
400Sigor@sysoev.ru     if (flags & NXT_NONBLOCK) {
4113Sigor@sysoev.ru         if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) {
4213Sigor@sysoev.ru             nxt_socket_close(task, s);
430Sigor@sysoev.ru             return -1;
440Sigor@sysoev.ru         }
450Sigor@sysoev.ru     }
460Sigor@sysoev.ru 
470Sigor@sysoev.ru #endif
480Sigor@sysoev.ru 
490Sigor@sysoev.ru     return s;
500Sigor@sysoev.ru }
510Sigor@sysoev.ru 
520Sigor@sysoev.ru 
530Sigor@sysoev.ru void
nxt_socket_defer_accept(nxt_task_t * task,nxt_socket_t s,nxt_sockaddr_t * sa)54229Sigor@sysoev.ru nxt_socket_defer_accept(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
55229Sigor@sysoev.ru {
56229Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
57229Sigor@sysoev.ru 
58229Sigor@sysoev.ru     if (sa->u.sockaddr.sa_family == AF_UNIX) {
59229Sigor@sysoev.ru         /* Deferred accept() is not supported on AF_UNIX sockets. */
60229Sigor@sysoev.ru         return;
61229Sigor@sysoev.ru     }
62229Sigor@sysoev.ru 
63229Sigor@sysoev.ru #endif
64229Sigor@sysoev.ru 
65229Sigor@sysoev.ru #ifdef TCP_DEFER_ACCEPT
66229Sigor@sysoev.ru 
67229Sigor@sysoev.ru     /* Defer Linux accept() up to for 1 second. */
68229Sigor@sysoev.ru     (void) nxt_socket_setsockopt(task, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
69229Sigor@sysoev.ru 
70229Sigor@sysoev.ru #endif
71229Sigor@sysoev.ru }
72229Sigor@sysoev.ru 
73229Sigor@sysoev.ru 
740Sigor@sysoev.ru nxt_int_t
nxt_socket_getsockopt(nxt_task_t * task,nxt_socket_t s,nxt_uint_t level,nxt_uint_t sockopt)7513Sigor@sysoev.ru nxt_socket_getsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
7613Sigor@sysoev.ru     nxt_uint_t sockopt)
770Sigor@sysoev.ru {
780Sigor@sysoev.ru     int        val;
790Sigor@sysoev.ru     socklen_t  len;
800Sigor@sysoev.ru 
810Sigor@sysoev.ru     len = sizeof(val);
820Sigor@sysoev.ru 
830Sigor@sysoev.ru     if (nxt_fast_path(getsockopt(s, level, sockopt, &val, &len) == 0)) {
8413Sigor@sysoev.ru         nxt_debug(task, "getsockopt(%d, %ui, %s): %d",
8513Sigor@sysoev.ru                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
860Sigor@sysoev.ru         return val;
870Sigor@sysoev.ru     }
880Sigor@sysoev.ru 
89564Svbart@nginx.com     nxt_alert(task, "getsockopt(%d, %ui, %s) failed %E",
90564Svbart@nginx.com               s, level, nxt_socket_sockopt_name(level, sockopt),
91564Svbart@nginx.com               nxt_socket_errno);
920Sigor@sysoev.ru 
930Sigor@sysoev.ru     return -1;
940Sigor@sysoev.ru }
950Sigor@sysoev.ru 
960Sigor@sysoev.ru 
970Sigor@sysoev.ru nxt_int_t
nxt_socket_setsockopt(nxt_task_t * task,nxt_socket_t s,nxt_uint_t level,nxt_uint_t sockopt,int val)9813Sigor@sysoev.ru nxt_socket_setsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
9913Sigor@sysoev.ru     nxt_uint_t sockopt, int val)
1000Sigor@sysoev.ru {
1010Sigor@sysoev.ru     socklen_t  len;
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru     len = sizeof(val);
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru     if (nxt_fast_path(setsockopt(s, level, sockopt, &val, len) == 0)) {
10613Sigor@sysoev.ru         nxt_debug(task, "setsockopt(%d, %ui, %s): %d",
10713Sigor@sysoev.ru                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
1080Sigor@sysoev.ru         return NXT_OK;
1090Sigor@sysoev.ru     }
1100Sigor@sysoev.ru 
111564Svbart@nginx.com     nxt_alert(task, "setsockopt(%d, %ui, %s, %d) failed %E",
112564Svbart@nginx.com               s, level, nxt_socket_sockopt_name(level, sockopt), val,
113564Svbart@nginx.com               nxt_socket_errno);
1140Sigor@sysoev.ru 
1150Sigor@sysoev.ru     return NXT_ERROR;
1160Sigor@sysoev.ru }
1170Sigor@sysoev.ru 
1180Sigor@sysoev.ru 
1190Sigor@sysoev.ru static const char *
nxt_socket_sockopt_name(nxt_uint_t level,nxt_uint_t sockopt)1200Sigor@sysoev.ru nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
1210Sigor@sysoev.ru {
1220Sigor@sysoev.ru     switch (level) {
1230Sigor@sysoev.ru 
1240Sigor@sysoev.ru     case SOL_SOCKET:
1250Sigor@sysoev.ru         switch (sockopt) {
1260Sigor@sysoev.ru 
1270Sigor@sysoev.ru         case SO_SNDBUF:
1280Sigor@sysoev.ru             return "SO_SNDBUF";
1290Sigor@sysoev.ru 
1300Sigor@sysoev.ru         case SO_RCVBUF:
1310Sigor@sysoev.ru             return "SO_RCVBUF";
1320Sigor@sysoev.ru 
1330Sigor@sysoev.ru         case SO_REUSEADDR:
13413Sigor@sysoev.ru             return "SO_REUSEADDR";
1350Sigor@sysoev.ru 
1360Sigor@sysoev.ru         case SO_TYPE:
1370Sigor@sysoev.ru             return "SO_TYPE";
1380Sigor@sysoev.ru         }
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru         break;
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru     case IPPROTO_TCP:
1430Sigor@sysoev.ru         switch (sockopt) {
1440Sigor@sysoev.ru 
1450Sigor@sysoev.ru         case TCP_NODELAY:
1460Sigor@sysoev.ru             return "TCP_NODELAY";
1470Sigor@sysoev.ru 
1480Sigor@sysoev.ru #ifdef TCP_DEFER_ACCEPT
1490Sigor@sysoev.ru         case TCP_DEFER_ACCEPT:
1500Sigor@sysoev.ru             return "TCP_DEFER_ACCEPT";
1510Sigor@sysoev.ru #endif
1520Sigor@sysoev.ru         }
1530Sigor@sysoev.ru 
1540Sigor@sysoev.ru         break;
1550Sigor@sysoev.ru 
156115Sigor@sysoev.ru #if (NXT_INET6)
1570Sigor@sysoev.ru     case IPPROTO_IPV6:
1580Sigor@sysoev.ru 
1590Sigor@sysoev.ru         switch (sockopt) {
1600Sigor@sysoev.ru 
1610Sigor@sysoev.ru         case IPV6_V6ONLY:
1620Sigor@sysoev.ru             return "IPV6_V6ONLY";
1630Sigor@sysoev.ru         }
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru         break;
1660Sigor@sysoev.ru #endif
1670Sigor@sysoev.ru 
1680Sigor@sysoev.ru     }
1690Sigor@sysoev.ru 
1700Sigor@sysoev.ru     return "";
1710Sigor@sysoev.ru }
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru nxt_int_t
nxt_socket_bind(nxt_task_t * task,nxt_socket_t s,nxt_sockaddr_t * sa)1751449Svbart@nginx.com nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
1760Sigor@sysoev.ru {
177493Spluknet@nginx.com     nxt_debug(task, "bind(%d, %*s)", s, (size_t) sa->length,
178493Spluknet@nginx.com               nxt_sockaddr_start(sa));
1790Sigor@sysoev.ru 
18013Sigor@sysoev.ru     if (nxt_fast_path(bind(s, &sa->u.sockaddr, sa->socklen) == 0)) {
1810Sigor@sysoev.ru         return NXT_OK;
1820Sigor@sysoev.ru     }
1830Sigor@sysoev.ru 
184564Svbart@nginx.com     nxt_alert(task, "bind(%d, %*s) failed %E",
1851449Svbart@nginx.com               s, (size_t) sa->length, nxt_sockaddr_start(sa), nxt_socket_errno);
1860Sigor@sysoev.ru 
1870Sigor@sysoev.ru     return NXT_ERROR;
1880Sigor@sysoev.ru }
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru 
1910Sigor@sysoev.ru nxt_int_t
nxt_socket_connect(nxt_task_t * task,nxt_socket_t s,nxt_sockaddr_t * sa)19213Sigor@sysoev.ru nxt_socket_connect(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
1930Sigor@sysoev.ru {
1940Sigor@sysoev.ru     nxt_err_t   err;
1950Sigor@sysoev.ru     nxt_int_t   ret;
1960Sigor@sysoev.ru     nxt_uint_t  level;
1970Sigor@sysoev.ru 
198493Spluknet@nginx.com     nxt_debug(task, "connect(%d, %*s)",
199493Spluknet@nginx.com               s, (size_t) sa->length, nxt_sockaddr_start(sa));
2000Sigor@sysoev.ru 
20113Sigor@sysoev.ru     if (connect(s, &sa->u.sockaddr, sa->socklen) == 0) {
2020Sigor@sysoev.ru         return NXT_OK;
2030Sigor@sysoev.ru     }
2040Sigor@sysoev.ru 
2050Sigor@sysoev.ru     err = nxt_socket_errno;
2060Sigor@sysoev.ru 
2070Sigor@sysoev.ru     switch (err) {
2080Sigor@sysoev.ru 
2090Sigor@sysoev.ru     case NXT_EINPROGRESS:
21013Sigor@sysoev.ru         nxt_debug(task, "connect(%d, %*s) in progress",
211493Spluknet@nginx.com                   s, (size_t) sa->length, nxt_sockaddr_start(sa));
2120Sigor@sysoev.ru         return NXT_AGAIN;
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru     case NXT_ECONNREFUSED:
2150Sigor@sysoev.ru #if (NXT_LINUX)
2160Sigor@sysoev.ru     case NXT_EAGAIN:
2170Sigor@sysoev.ru         /*
2180Sigor@sysoev.ru          * Linux returns EAGAIN instead of ECONNREFUSED
2190Sigor@sysoev.ru          * for UNIX sockets if a listen queue is full.
2200Sigor@sysoev.ru          */
2210Sigor@sysoev.ru #endif
2220Sigor@sysoev.ru         level = NXT_LOG_ERR;
2230Sigor@sysoev.ru         ret = NXT_DECLINED;
2240Sigor@sysoev.ru         break;
2250Sigor@sysoev.ru 
2260Sigor@sysoev.ru     case NXT_ECONNRESET:
2270Sigor@sysoev.ru     case NXT_ENETDOWN:
2280Sigor@sysoev.ru     case NXT_ENETUNREACH:
2290Sigor@sysoev.ru     case NXT_EHOSTDOWN:
2300Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
2310Sigor@sysoev.ru         level = NXT_LOG_ERR;
2320Sigor@sysoev.ru         ret = NXT_ERROR;
2330Sigor@sysoev.ru         break;
2340Sigor@sysoev.ru 
2350Sigor@sysoev.ru     default:
236564Svbart@nginx.com         level = NXT_LOG_ALERT;
2370Sigor@sysoev.ru         ret = NXT_ERROR;
2380Sigor@sysoev.ru     }
2390Sigor@sysoev.ru 
24013Sigor@sysoev.ru     nxt_log(task, level, "connect(%d, %*s) failed %E",
241493Spluknet@nginx.com             s, (size_t) sa->length, nxt_sockaddr_start(sa), err);
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru     return ret;
2440Sigor@sysoev.ru }
2450Sigor@sysoev.ru 
2460Sigor@sysoev.ru 
2470Sigor@sysoev.ru void
nxt_socket_shutdown(nxt_task_t * task,nxt_socket_t s,nxt_uint_t how)24813Sigor@sysoev.ru nxt_socket_shutdown(nxt_task_t *task, nxt_socket_t s, nxt_uint_t how)
2490Sigor@sysoev.ru {
2500Sigor@sysoev.ru     nxt_err_t   err;
2510Sigor@sysoev.ru     nxt_uint_t  level;
2520Sigor@sysoev.ru 
2530Sigor@sysoev.ru     if (nxt_fast_path(shutdown(s, how) == 0)) {
25413Sigor@sysoev.ru         nxt_debug(task, "shutdown(%d, %ui)", s, how);
2550Sigor@sysoev.ru         return;
2560Sigor@sysoev.ru     }
2570Sigor@sysoev.ru 
2580Sigor@sysoev.ru     err = nxt_socket_errno;
2590Sigor@sysoev.ru 
2600Sigor@sysoev.ru     switch (err) {
2610Sigor@sysoev.ru 
2620Sigor@sysoev.ru     case NXT_ENOTCONN:
2631082Sigor@sysoev.ru         level = NXT_LOG_DEBUG;
2640Sigor@sysoev.ru         break;
2650Sigor@sysoev.ru 
2660Sigor@sysoev.ru     case NXT_ECONNRESET:
2670Sigor@sysoev.ru     case NXT_ENETDOWN:
2680Sigor@sysoev.ru     case NXT_ENETUNREACH:
2690Sigor@sysoev.ru     case NXT_EHOSTDOWN:
2700Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
2710Sigor@sysoev.ru         level = NXT_LOG_ERR;
2720Sigor@sysoev.ru         break;
2730Sigor@sysoev.ru 
2740Sigor@sysoev.ru     default:
275564Svbart@nginx.com         level = NXT_LOG_ALERT;
2760Sigor@sysoev.ru     }
2770Sigor@sysoev.ru 
27813Sigor@sysoev.ru     nxt_log(task, level, "shutdown(%d, %ui) failed %E", s, how, err);
2790Sigor@sysoev.ru }
2800Sigor@sysoev.ru 
2810Sigor@sysoev.ru 
282*1504Sigor@sysoev.ru void
nxt_socket_close(nxt_task_t * task,nxt_socket_t s)283*1504Sigor@sysoev.ru nxt_socket_close(nxt_task_t *task, nxt_socket_t s)
284*1504Sigor@sysoev.ru {
285*1504Sigor@sysoev.ru     nxt_err_t   err;
286*1504Sigor@sysoev.ru     nxt_uint_t  level;
287*1504Sigor@sysoev.ru 
288*1504Sigor@sysoev.ru     if (nxt_fast_path(close(s) == 0)) {
289*1504Sigor@sysoev.ru         nxt_debug(task, "socket close(%d)", s);
290*1504Sigor@sysoev.ru         return;
291*1504Sigor@sysoev.ru     }
292*1504Sigor@sysoev.ru 
293*1504Sigor@sysoev.ru     err = nxt_socket_errno;
294*1504Sigor@sysoev.ru 
295*1504Sigor@sysoev.ru     switch (err) {
296*1504Sigor@sysoev.ru 
297*1504Sigor@sysoev.ru     case NXT_ENOTCONN:
298*1504Sigor@sysoev.ru         level = NXT_LOG_DEBUG;
299*1504Sigor@sysoev.ru         break;
300*1504Sigor@sysoev.ru 
301*1504Sigor@sysoev.ru     case NXT_ECONNRESET:
302*1504Sigor@sysoev.ru     case NXT_ENETDOWN:
303*1504Sigor@sysoev.ru     case NXT_ENETUNREACH:
304*1504Sigor@sysoev.ru     case NXT_EHOSTDOWN:
305*1504Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
306*1504Sigor@sysoev.ru         level = NXT_LOG_ERR;
307*1504Sigor@sysoev.ru         break;
308*1504Sigor@sysoev.ru 
309*1504Sigor@sysoev.ru     default:
310*1504Sigor@sysoev.ru         level = NXT_LOG_ALERT;
311*1504Sigor@sysoev.ru     }
312*1504Sigor@sysoev.ru 
313*1504Sigor@sysoev.ru     nxt_log(task, level, "socket close(%d) failed %E", s, err);
314*1504Sigor@sysoev.ru }
315*1504Sigor@sysoev.ru 
316*1504Sigor@sysoev.ru 
3171263Sigor@sysoev.ru nxt_err_t
nxt_socket_error(nxt_socket_t s)3181263Sigor@sysoev.ru nxt_socket_error(nxt_socket_t s)
3191263Sigor@sysoev.ru {
3201263Sigor@sysoev.ru     int        ret, err;
3211263Sigor@sysoev.ru     socklen_t  len;
3221263Sigor@sysoev.ru 
3231263Sigor@sysoev.ru     err = 0;
3241263Sigor@sysoev.ru     len = sizeof(int);
3251263Sigor@sysoev.ru     /*
3261263Sigor@sysoev.ru      * Linux and BSDs return 0 and store a pending error in the err argument;
3271263Sigor@sysoev.ru      * Solaris returns -1 and sets the errno.
3281263Sigor@sysoev.ru      */
3291263Sigor@sysoev.ru     ret = getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &err, &len);
3301263Sigor@sysoev.ru 
3311263Sigor@sysoev.ru     if (nxt_slow_path(ret == -1)) {
3321263Sigor@sysoev.ru         err = nxt_errno;
3331263Sigor@sysoev.ru     }
3341263Sigor@sysoev.ru 
3351263Sigor@sysoev.ru     return err;
3361263Sigor@sysoev.ru }
3371263Sigor@sysoev.ru 
3381263Sigor@sysoev.ru 
3390Sigor@sysoev.ru nxt_uint_t
nxt_socket_error_level(nxt_err_t err)34013Sigor@sysoev.ru nxt_socket_error_level(nxt_err_t err)
3410Sigor@sysoev.ru {
3420Sigor@sysoev.ru     switch (err) {
3430Sigor@sysoev.ru 
34413Sigor@sysoev.ru     case NXT_EPIPE:
3450Sigor@sysoev.ru     case NXT_ECONNRESET:
3460Sigor@sysoev.ru     case NXT_ENOTCONN:
3470Sigor@sysoev.ru     case NXT_ETIMEDOUT:
3480Sigor@sysoev.ru     case NXT_ENETDOWN:
3490Sigor@sysoev.ru     case NXT_ENETUNREACH:
3500Sigor@sysoev.ru     case NXT_EHOSTDOWN:
3510Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
3521082Sigor@sysoev.ru         return NXT_LOG_INFO;
3530Sigor@sysoev.ru 
3541270Sigor@sysoev.ru     case NXT_ECONNREFUSED:
3551270Sigor@sysoev.ru         return NXT_LOG_ERR;
3561270Sigor@sysoev.ru 
3570Sigor@sysoev.ru     default:
358564Svbart@nginx.com         return NXT_LOG_ALERT;
3590Sigor@sysoev.ru     }
3600Sigor@sysoev.ru }
361