xref: /unit/src/nxt_socket.c (revision 1082)
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)) {
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
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 {
60564Svbart@nginx.com         nxt_alert(task, "socket close(%d) failed %E", s, nxt_socket_errno);
610Sigor@sysoev.ru     }
620Sigor@sysoev.ru }
630Sigor@sysoev.ru 
640Sigor@sysoev.ru 
65229Sigor@sysoev.ru void
66229Sigor@sysoev.ru nxt_socket_defer_accept(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
67229Sigor@sysoev.ru {
68229Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
69229Sigor@sysoev.ru 
70229Sigor@sysoev.ru     if (sa->u.sockaddr.sa_family == AF_UNIX) {
71229Sigor@sysoev.ru         /* Deferred accept() is not supported on AF_UNIX sockets. */
72229Sigor@sysoev.ru         return;
73229Sigor@sysoev.ru     }
74229Sigor@sysoev.ru 
75229Sigor@sysoev.ru #endif
76229Sigor@sysoev.ru 
77229Sigor@sysoev.ru #ifdef TCP_DEFER_ACCEPT
78229Sigor@sysoev.ru 
79229Sigor@sysoev.ru     /* Defer Linux accept() up to for 1 second. */
80229Sigor@sysoev.ru     (void) nxt_socket_setsockopt(task, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
81229Sigor@sysoev.ru 
82229Sigor@sysoev.ru #endif
83229Sigor@sysoev.ru }
84229Sigor@sysoev.ru 
85229Sigor@sysoev.ru 
860Sigor@sysoev.ru nxt_int_t
8713Sigor@sysoev.ru nxt_socket_getsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
8813Sigor@sysoev.ru     nxt_uint_t sockopt)
890Sigor@sysoev.ru {
900Sigor@sysoev.ru     int        val;
910Sigor@sysoev.ru     socklen_t  len;
920Sigor@sysoev.ru 
930Sigor@sysoev.ru     len = sizeof(val);
940Sigor@sysoev.ru 
950Sigor@sysoev.ru     if (nxt_fast_path(getsockopt(s, level, sockopt, &val, &len) == 0)) {
9613Sigor@sysoev.ru         nxt_debug(task, "getsockopt(%d, %ui, %s): %d",
9713Sigor@sysoev.ru                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
980Sigor@sysoev.ru         return val;
990Sigor@sysoev.ru     }
1000Sigor@sysoev.ru 
101564Svbart@nginx.com     nxt_alert(task, "getsockopt(%d, %ui, %s) failed %E",
102564Svbart@nginx.com               s, level, nxt_socket_sockopt_name(level, sockopt),
103564Svbart@nginx.com               nxt_socket_errno);
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru     return -1;
1060Sigor@sysoev.ru }
1070Sigor@sysoev.ru 
1080Sigor@sysoev.ru 
1090Sigor@sysoev.ru nxt_int_t
11013Sigor@sysoev.ru nxt_socket_setsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
11113Sigor@sysoev.ru     nxt_uint_t sockopt, int val)
1120Sigor@sysoev.ru {
1130Sigor@sysoev.ru     socklen_t  len;
1140Sigor@sysoev.ru 
1150Sigor@sysoev.ru     len = sizeof(val);
1160Sigor@sysoev.ru 
1170Sigor@sysoev.ru     if (nxt_fast_path(setsockopt(s, level, sockopt, &val, len) == 0)) {
11813Sigor@sysoev.ru         nxt_debug(task, "setsockopt(%d, %ui, %s): %d",
11913Sigor@sysoev.ru                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
1200Sigor@sysoev.ru         return NXT_OK;
1210Sigor@sysoev.ru     }
1220Sigor@sysoev.ru 
123564Svbart@nginx.com     nxt_alert(task, "setsockopt(%d, %ui, %s, %d) failed %E",
124564Svbart@nginx.com               s, level, nxt_socket_sockopt_name(level, sockopt), val,
125564Svbart@nginx.com               nxt_socket_errno);
1260Sigor@sysoev.ru 
1270Sigor@sysoev.ru     return NXT_ERROR;
1280Sigor@sysoev.ru }
1290Sigor@sysoev.ru 
1300Sigor@sysoev.ru 
1310Sigor@sysoev.ru static const char *
1320Sigor@sysoev.ru nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
1330Sigor@sysoev.ru {
1340Sigor@sysoev.ru     switch (level) {
1350Sigor@sysoev.ru 
1360Sigor@sysoev.ru     case SOL_SOCKET:
1370Sigor@sysoev.ru         switch (sockopt) {
1380Sigor@sysoev.ru 
1390Sigor@sysoev.ru         case SO_SNDBUF:
1400Sigor@sysoev.ru             return "SO_SNDBUF";
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru         case SO_RCVBUF:
1430Sigor@sysoev.ru             return "SO_RCVBUF";
1440Sigor@sysoev.ru 
1450Sigor@sysoev.ru         case SO_REUSEADDR:
14613Sigor@sysoev.ru             return "SO_REUSEADDR";
1470Sigor@sysoev.ru 
1480Sigor@sysoev.ru         case SO_TYPE:
1490Sigor@sysoev.ru             return "SO_TYPE";
1500Sigor@sysoev.ru         }
1510Sigor@sysoev.ru 
1520Sigor@sysoev.ru         break;
1530Sigor@sysoev.ru 
1540Sigor@sysoev.ru     case IPPROTO_TCP:
1550Sigor@sysoev.ru         switch (sockopt) {
1560Sigor@sysoev.ru 
1570Sigor@sysoev.ru         case TCP_NODELAY:
1580Sigor@sysoev.ru             return "TCP_NODELAY";
1590Sigor@sysoev.ru 
1600Sigor@sysoev.ru #ifdef TCP_DEFER_ACCEPT
1610Sigor@sysoev.ru         case TCP_DEFER_ACCEPT:
1620Sigor@sysoev.ru             return "TCP_DEFER_ACCEPT";
1630Sigor@sysoev.ru #endif
1640Sigor@sysoev.ru         }
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru         break;
1670Sigor@sysoev.ru 
168115Sigor@sysoev.ru #if (NXT_INET6)
1690Sigor@sysoev.ru     case IPPROTO_IPV6:
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru         switch (sockopt) {
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru         case IPV6_V6ONLY:
1740Sigor@sysoev.ru             return "IPV6_V6ONLY";
1750Sigor@sysoev.ru         }
1760Sigor@sysoev.ru 
1770Sigor@sysoev.ru         break;
1780Sigor@sysoev.ru #endif
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru     }
1810Sigor@sysoev.ru 
1820Sigor@sysoev.ru     return "";
1830Sigor@sysoev.ru }
1840Sigor@sysoev.ru 
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru nxt_int_t
18713Sigor@sysoev.ru nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa,
18813Sigor@sysoev.ru     nxt_bool_t test)
1890Sigor@sysoev.ru {
1900Sigor@sysoev.ru     nxt_err_t  err;
1910Sigor@sysoev.ru 
192493Spluknet@nginx.com     nxt_debug(task, "bind(%d, %*s)", s, (size_t) sa->length,
193493Spluknet@nginx.com               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 
205564Svbart@nginx.com     nxt_alert(task, "bind(%d, %*s) failed %E",
206564Svbart@nginx.com               s, (size_t) 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 
219493Spluknet@nginx.com     nxt_debug(task, "connect(%d, %*s)",
220493Spluknet@nginx.com               s, (size_t) sa->length, nxt_sockaddr_start(sa));
2210Sigor@sysoev.ru 
22213Sigor@sysoev.ru     if (connect(s, &sa->u.sockaddr, sa->socklen) == 0) {
2230Sigor@sysoev.ru         return NXT_OK;
2240Sigor@sysoev.ru     }
2250Sigor@sysoev.ru 
2260Sigor@sysoev.ru     err = nxt_socket_errno;
2270Sigor@sysoev.ru 
2280Sigor@sysoev.ru     switch (err) {
2290Sigor@sysoev.ru 
2300Sigor@sysoev.ru     case NXT_EINPROGRESS:
23113Sigor@sysoev.ru         nxt_debug(task, "connect(%d, %*s) in progress",
232493Spluknet@nginx.com                   s, (size_t) sa->length, nxt_sockaddr_start(sa));
2330Sigor@sysoev.ru         return NXT_AGAIN;
2340Sigor@sysoev.ru 
2350Sigor@sysoev.ru     case NXT_ECONNREFUSED:
2360Sigor@sysoev.ru #if (NXT_LINUX)
2370Sigor@sysoev.ru     case NXT_EAGAIN:
2380Sigor@sysoev.ru         /*
2390Sigor@sysoev.ru          * Linux returns EAGAIN instead of ECONNREFUSED
2400Sigor@sysoev.ru          * for UNIX sockets if a listen queue is full.
2410Sigor@sysoev.ru          */
2420Sigor@sysoev.ru #endif
2430Sigor@sysoev.ru         level = NXT_LOG_ERR;
2440Sigor@sysoev.ru         ret = NXT_DECLINED;
2450Sigor@sysoev.ru         break;
2460Sigor@sysoev.ru 
2470Sigor@sysoev.ru     case NXT_ECONNRESET:
2480Sigor@sysoev.ru     case NXT_ENETDOWN:
2490Sigor@sysoev.ru     case NXT_ENETUNREACH:
2500Sigor@sysoev.ru     case NXT_EHOSTDOWN:
2510Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
2520Sigor@sysoev.ru         level = NXT_LOG_ERR;
2530Sigor@sysoev.ru         ret = NXT_ERROR;
2540Sigor@sysoev.ru         break;
2550Sigor@sysoev.ru 
2560Sigor@sysoev.ru     default:
257564Svbart@nginx.com         level = NXT_LOG_ALERT;
2580Sigor@sysoev.ru         ret = NXT_ERROR;
2590Sigor@sysoev.ru     }
2600Sigor@sysoev.ru 
26113Sigor@sysoev.ru     nxt_log(task, level, "connect(%d, %*s) failed %E",
262493Spluknet@nginx.com             s, (size_t) sa->length, nxt_sockaddr_start(sa), err);
2630Sigor@sysoev.ru 
2640Sigor@sysoev.ru     return ret;
2650Sigor@sysoev.ru }
2660Sigor@sysoev.ru 
2670Sigor@sysoev.ru 
2680Sigor@sysoev.ru void
26913Sigor@sysoev.ru nxt_socket_shutdown(nxt_task_t *task, nxt_socket_t s, nxt_uint_t how)
2700Sigor@sysoev.ru {
2710Sigor@sysoev.ru     nxt_err_t   err;
2720Sigor@sysoev.ru     nxt_uint_t  level;
2730Sigor@sysoev.ru 
2740Sigor@sysoev.ru     if (nxt_fast_path(shutdown(s, how) == 0)) {
27513Sigor@sysoev.ru         nxt_debug(task, "shutdown(%d, %ui)", s, how);
2760Sigor@sysoev.ru         return;
2770Sigor@sysoev.ru     }
2780Sigor@sysoev.ru 
2790Sigor@sysoev.ru     err = nxt_socket_errno;
2800Sigor@sysoev.ru 
2810Sigor@sysoev.ru     switch (err) {
2820Sigor@sysoev.ru 
2830Sigor@sysoev.ru     case NXT_ENOTCONN:
284*1082Sigor@sysoev.ru         level = NXT_LOG_DEBUG;
2850Sigor@sysoev.ru         break;
2860Sigor@sysoev.ru 
2870Sigor@sysoev.ru     case NXT_ECONNRESET:
2880Sigor@sysoev.ru     case NXT_ENETDOWN:
2890Sigor@sysoev.ru     case NXT_ENETUNREACH:
2900Sigor@sysoev.ru     case NXT_EHOSTDOWN:
2910Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
2920Sigor@sysoev.ru         level = NXT_LOG_ERR;
2930Sigor@sysoev.ru         break;
2940Sigor@sysoev.ru 
2950Sigor@sysoev.ru     default:
296564Svbart@nginx.com         level = NXT_LOG_ALERT;
2970Sigor@sysoev.ru     }
2980Sigor@sysoev.ru 
29913Sigor@sysoev.ru     nxt_log(task, level, "shutdown(%d, %ui) failed %E", s, how, err);
3000Sigor@sysoev.ru }
3010Sigor@sysoev.ru 
3020Sigor@sysoev.ru 
3030Sigor@sysoev.ru nxt_uint_t
30413Sigor@sysoev.ru nxt_socket_error_level(nxt_err_t err)
3050Sigor@sysoev.ru {
3060Sigor@sysoev.ru     switch (err) {
3070Sigor@sysoev.ru 
30813Sigor@sysoev.ru     case NXT_EPIPE:
3090Sigor@sysoev.ru     case NXT_ECONNRESET:
3100Sigor@sysoev.ru     case NXT_ENOTCONN:
3110Sigor@sysoev.ru     case NXT_ETIMEDOUT:
3120Sigor@sysoev.ru     case NXT_ENETDOWN:
3130Sigor@sysoev.ru     case NXT_ENETUNREACH:
3140Sigor@sysoev.ru     case NXT_EHOSTDOWN:
3150Sigor@sysoev.ru     case NXT_EHOSTUNREACH:
316*1082Sigor@sysoev.ru         return NXT_LOG_INFO;
3170Sigor@sysoev.ru 
3180Sigor@sysoev.ru     default:
319564Svbart@nginx.com         return NXT_LOG_ALERT;
3200Sigor@sysoev.ru     }
3210Sigor@sysoev.ru }
322