xref: /unit/src/nxt_socket.c (revision 229)
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
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)) {
3113Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "socket(%ui, 0x%uXi, %ui) failed %E",
3213Sigor@sysoev.ru                 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
5413Sigor@sysoev.ru nxt_socket_close(nxt_task_t *task, nxt_socket_t s)
550Sigor@sysoev.ru {
560Sigor@sysoev.ru     if (nxt_fast_path(close(s) == 0)) {
5713Sigor@sysoev.ru         nxt_debug(task, "socket close(%d)", s);
580Sigor@sysoev.ru 
590Sigor@sysoev.ru     } else {
6013Sigor@sysoev.ru         nxt_log(task, NXT_LOG_CRIT, "socket close(%d) failed %E",
6113Sigor@sysoev.ru                 s, nxt_socket_errno);
620Sigor@sysoev.ru     }
630Sigor@sysoev.ru }
640Sigor@sysoev.ru 
650Sigor@sysoev.ru 
66*229Sigor@sysoev.ru void
67*229Sigor@sysoev.ru nxt_socket_defer_accept(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
68*229Sigor@sysoev.ru {
69*229Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
70*229Sigor@sysoev.ru 
71*229Sigor@sysoev.ru     if (sa->u.sockaddr.sa_family == AF_UNIX) {
72*229Sigor@sysoev.ru         /* Deferred accept() is not supported on AF_UNIX sockets. */
73*229Sigor@sysoev.ru         return;
74*229Sigor@sysoev.ru     }
75*229Sigor@sysoev.ru 
76*229Sigor@sysoev.ru #endif
77*229Sigor@sysoev.ru 
78*229Sigor@sysoev.ru #ifdef TCP_DEFER_ACCEPT
79*229Sigor@sysoev.ru 
80*229Sigor@sysoev.ru     /* Defer Linux accept() up to for 1 second. */
81*229Sigor@sysoev.ru     (void) nxt_socket_setsockopt(task, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
82*229Sigor@sysoev.ru 
83*229Sigor@sysoev.ru #endif
84*229Sigor@sysoev.ru }
85*229Sigor@sysoev.ru 
86*229Sigor@sysoev.ru 
870Sigor@sysoev.ru nxt_int_t
8813Sigor@sysoev.ru nxt_socket_getsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
8913Sigor@sysoev.ru     nxt_uint_t sockopt)
900Sigor@sysoev.ru {
910Sigor@sysoev.ru     int        val;
920Sigor@sysoev.ru     socklen_t  len;
930Sigor@sysoev.ru 
940Sigor@sysoev.ru     len = sizeof(val);
950Sigor@sysoev.ru 
960Sigor@sysoev.ru     if (nxt_fast_path(getsockopt(s, level, sockopt, &val, &len) == 0)) {
9713Sigor@sysoev.ru         nxt_debug(task, "getsockopt(%d, %ui, %s): %d",
9813Sigor@sysoev.ru                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
990Sigor@sysoev.ru         return val;
1000Sigor@sysoev.ru     }
1010Sigor@sysoev.ru 
10213Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, "getsockopt(%d, %ui, %s) failed %E",
10313Sigor@sysoev.ru             s, level, nxt_socket_sockopt_name(level, sockopt),
10413Sigor@sysoev.ru             val, nxt_socket_errno);
1050Sigor@sysoev.ru 
1060Sigor@sysoev.ru     return -1;
1070Sigor@sysoev.ru }
1080Sigor@sysoev.ru 
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru nxt_int_t
11113Sigor@sysoev.ru nxt_socket_setsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
11213Sigor@sysoev.ru     nxt_uint_t sockopt, int val)
1130Sigor@sysoev.ru {
1140Sigor@sysoev.ru     socklen_t  len;
1150Sigor@sysoev.ru 
1160Sigor@sysoev.ru     len = sizeof(val);
1170Sigor@sysoev.ru 
1180Sigor@sysoev.ru     if (nxt_fast_path(setsockopt(s, level, sockopt, &val, len) == 0)) {
11913Sigor@sysoev.ru         nxt_debug(task, "setsockopt(%d, %ui, %s): %d",
12013Sigor@sysoev.ru                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
1210Sigor@sysoev.ru         return NXT_OK;
1220Sigor@sysoev.ru     }
1230Sigor@sysoev.ru 
12413Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, "setsockopt(%d, %ui, %s, %d) failed %E",
12513Sigor@sysoev.ru             s, level, nxt_socket_sockopt_name(level, sockopt),
12613Sigor@sysoev.ru             val, nxt_socket_errno);
1270Sigor@sysoev.ru 
1280Sigor@sysoev.ru     return NXT_ERROR;
1290Sigor@sysoev.ru }
1300Sigor@sysoev.ru 
1310Sigor@sysoev.ru 
1320Sigor@sysoev.ru static const char *
1330Sigor@sysoev.ru nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
1340Sigor@sysoev.ru {
1350Sigor@sysoev.ru     switch (level) {
1360Sigor@sysoev.ru 
1370Sigor@sysoev.ru     case SOL_SOCKET:
1380Sigor@sysoev.ru         switch (sockopt) {
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru         case SO_SNDBUF:
1410Sigor@sysoev.ru             return "SO_SNDBUF";
1420Sigor@sysoev.ru 
1430Sigor@sysoev.ru         case SO_RCVBUF:
1440Sigor@sysoev.ru             return "SO_RCVBUF";
1450Sigor@sysoev.ru 
1460Sigor@sysoev.ru         case SO_REUSEADDR:
14713Sigor@sysoev.ru             return "SO_REUSEADDR";
1480Sigor@sysoev.ru 
1490Sigor@sysoev.ru         case SO_TYPE:
1500Sigor@sysoev.ru             return "SO_TYPE";
1510Sigor@sysoev.ru         }
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru         break;
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru     case IPPROTO_TCP:
1560Sigor@sysoev.ru         switch (sockopt) {
1570Sigor@sysoev.ru 
1580Sigor@sysoev.ru         case TCP_NODELAY:
1590Sigor@sysoev.ru             return "TCP_NODELAY";
1600Sigor@sysoev.ru 
1610Sigor@sysoev.ru #ifdef TCP_DEFER_ACCEPT
1620Sigor@sysoev.ru         case TCP_DEFER_ACCEPT:
1630Sigor@sysoev.ru             return "TCP_DEFER_ACCEPT";
1640Sigor@sysoev.ru #endif
1650Sigor@sysoev.ru         }
1660Sigor@sysoev.ru 
1670Sigor@sysoev.ru         break;
1680Sigor@sysoev.ru 
169115Sigor@sysoev.ru #if (NXT_INET6)
1700Sigor@sysoev.ru     case IPPROTO_IPV6:
1710Sigor@sysoev.ru 
1720Sigor@sysoev.ru         switch (sockopt) {
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru         case IPV6_V6ONLY:
1750Sigor@sysoev.ru             return "IPV6_V6ONLY";
1760Sigor@sysoev.ru         }
1770Sigor@sysoev.ru 
1780Sigor@sysoev.ru         break;
1790Sigor@sysoev.ru #endif
1800Sigor@sysoev.ru 
1810Sigor@sysoev.ru     }
1820Sigor@sysoev.ru 
1830Sigor@sysoev.ru     return "";
1840Sigor@sysoev.ru }
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru 
1870Sigor@sysoev.ru nxt_int_t
18813Sigor@sysoev.ru nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa,
18913Sigor@sysoev.ru     nxt_bool_t test)
1900Sigor@sysoev.ru {
1910Sigor@sysoev.ru     nxt_err_t  err;
1920Sigor@sysoev.ru 
19313Sigor@sysoev.ru     nxt_debug(task, "bind(%d, %*s)", s, sa->length, nxt_sockaddr_start(sa));
1940Sigor@sysoev.ru 
19513Sigor@sysoev.ru     if (nxt_fast_path(bind(s, &sa->u.sockaddr, sa->socklen) == 0)) {
1960Sigor@sysoev.ru         return NXT_OK;
1970Sigor@sysoev.ru     }
1980Sigor@sysoev.ru 
1990Sigor@sysoev.ru     err = nxt_socket_errno;
2000Sigor@sysoev.ru 
2010Sigor@sysoev.ru     if (err == NXT_EADDRINUSE && test) {
2020Sigor@sysoev.ru         return NXT_DECLINED;
2030Sigor@sysoev.ru     }
2040Sigor@sysoev.ru 
20513Sigor@sysoev.ru     nxt_log(task, NXT_LOG_CRIT, "bind(%d, %*s) failed %E",
20613Sigor@sysoev.ru             s, sa->length, nxt_sockaddr_start(sa), err);
2070Sigor@sysoev.ru 
2080Sigor@sysoev.ru     return NXT_ERROR;
2090Sigor@sysoev.ru }
2100Sigor@sysoev.ru 
2110Sigor@sysoev.ru 
2120Sigor@sysoev.ru nxt_int_t
21313Sigor@sysoev.ru nxt_socket_connect(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
2140Sigor@sysoev.ru {
2150Sigor@sysoev.ru     nxt_err_t   err;
2160Sigor@sysoev.ru     nxt_int_t   ret;
2170Sigor@sysoev.ru     nxt_uint_t  level;
2180Sigor@sysoev.ru 
21913Sigor@sysoev.ru     nxt_debug(task, "connect(%d, %*s)", s, sa->length, nxt_sockaddr_start(sa));
2200Sigor@sysoev.ru 
22113Sigor@sysoev.ru     if (connect(s, &sa->u.sockaddr, sa->socklen) == 0) {
2220Sigor@sysoev.ru         return NXT_OK;
2230Sigor@sysoev.ru     }
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru     err = nxt_socket_errno;
2260Sigor@sysoev.ru 
2270Sigor@sysoev.ru     switch (err) {
2280Sigor@sysoev.ru 
2290Sigor@sysoev.ru     case NXT_EINPROGRESS:
23013Sigor@sysoev.ru         nxt_debug(task, "connect(%d, %*s) in progress",
23113Sigor@sysoev.ru                   s, sa->length, nxt_sockaddr_start(sa));
2320Sigor@sysoev.ru         return NXT_AGAIN;
2330Sigor@sysoev.ru 
2340Sigor@sysoev.ru     case NXT_ECONNREFUSED:
2350Sigor@sysoev.ru #if (NXT_LINUX)
2360Sigor@sysoev.ru     case NXT_EAGAIN:
2370Sigor@sysoev.ru         /*
2380Sigor@sysoev.ru          * Linux returns EAGAIN instead of ECONNREFUSED
2390Sigor@sysoev.ru          * for UNIX sockets if a listen queue is full.
2400Sigor@sysoev.ru          */
2410Sigor@sysoev.ru #endif
2420Sigor@sysoev.ru         level = NXT_LOG_ERR;
2430Sigor@sysoev.ru         ret = NXT_DECLINED;
2440Sigor@sysoev.ru         break;
2450Sigor@sysoev.ru 
2460Sigor@sysoev.ru     case NXT_ECONNRESET:
2470Sigor@sysoev.ru     case NXT_ENETDOWN:
2480Sigor@sysoev.ru     case NXT_ENETUNREACH:
2490Sigor@sysoev.ru     case NXT_EHOSTDOWN:
2500Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
2510Sigor@sysoev.ru         level = NXT_LOG_ERR;
2520Sigor@sysoev.ru         ret = NXT_ERROR;
2530Sigor@sysoev.ru         break;
2540Sigor@sysoev.ru 
2550Sigor@sysoev.ru     default:
2560Sigor@sysoev.ru         level = NXT_LOG_CRIT;
2570Sigor@sysoev.ru         ret = NXT_ERROR;
2580Sigor@sysoev.ru     }
2590Sigor@sysoev.ru 
26013Sigor@sysoev.ru     nxt_log(task, level, "connect(%d, %*s) failed %E",
26113Sigor@sysoev.ru             s, sa->length, nxt_sockaddr_start(sa), err);
2620Sigor@sysoev.ru 
2630Sigor@sysoev.ru     return ret;
2640Sigor@sysoev.ru }
2650Sigor@sysoev.ru 
2660Sigor@sysoev.ru 
2670Sigor@sysoev.ru void
26813Sigor@sysoev.ru nxt_socket_shutdown(nxt_task_t *task, nxt_socket_t s, nxt_uint_t how)
2690Sigor@sysoev.ru {
2700Sigor@sysoev.ru     nxt_err_t   err;
2710Sigor@sysoev.ru     nxt_uint_t  level;
2720Sigor@sysoev.ru 
2730Sigor@sysoev.ru     if (nxt_fast_path(shutdown(s, how) == 0)) {
27413Sigor@sysoev.ru         nxt_debug(task, "shutdown(%d, %ui)", s, how);
2750Sigor@sysoev.ru         return;
2760Sigor@sysoev.ru     }
2770Sigor@sysoev.ru 
2780Sigor@sysoev.ru     err = nxt_socket_errno;
2790Sigor@sysoev.ru 
2800Sigor@sysoev.ru     switch (err) {
2810Sigor@sysoev.ru 
2820Sigor@sysoev.ru     case NXT_ENOTCONN:
2830Sigor@sysoev.ru         level = NXT_LOG_INFO;
2840Sigor@sysoev.ru         break;
2850Sigor@sysoev.ru 
2860Sigor@sysoev.ru     case NXT_ECONNRESET:
2870Sigor@sysoev.ru     case NXT_ENETDOWN:
2880Sigor@sysoev.ru     case NXT_ENETUNREACH:
2890Sigor@sysoev.ru     case NXT_EHOSTDOWN:
2900Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
2910Sigor@sysoev.ru         level = NXT_LOG_ERR;
2920Sigor@sysoev.ru         break;
2930Sigor@sysoev.ru 
2940Sigor@sysoev.ru     default:
2950Sigor@sysoev.ru         level = NXT_LOG_CRIT;
2960Sigor@sysoev.ru     }
2970Sigor@sysoev.ru 
29813Sigor@sysoev.ru     nxt_log(task, level, "shutdown(%d, %ui) failed %E", s, how, err);
2990Sigor@sysoev.ru }
3000Sigor@sysoev.ru 
3010Sigor@sysoev.ru 
3020Sigor@sysoev.ru nxt_uint_t
30313Sigor@sysoev.ru nxt_socket_error_level(nxt_err_t err)
3040Sigor@sysoev.ru {
3050Sigor@sysoev.ru     switch (err) {
3060Sigor@sysoev.ru 
30713Sigor@sysoev.ru     case NXT_EPIPE:
3080Sigor@sysoev.ru     case NXT_ECONNRESET:
3090Sigor@sysoev.ru     case NXT_ENOTCONN:
3100Sigor@sysoev.ru     case NXT_ETIMEDOUT:
3110Sigor@sysoev.ru     case NXT_ENETDOWN:
3120Sigor@sysoev.ru     case NXT_ENETUNREACH:
3130Sigor@sysoev.ru     case NXT_EHOSTDOWN:
3140Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
3150Sigor@sysoev.ru         return NXT_LOG_ERR;
3160Sigor@sysoev.ru 
3170Sigor@sysoev.ru     default:
31813Sigor@sysoev.ru         return NXT_LOG_CRIT;
3190Sigor@sysoev.ru     }
3200Sigor@sysoev.ru }
321