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