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