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