xref: /unit/src/nxt_socket.c (revision 2617:18a10bb7346d)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 static const char *nxt_socket_sockopt_name(nxt_uint_t level,
11     nxt_uint_t sockopt);
12 
13 
14 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)15 nxt_socket_create(nxt_task_t *task, nxt_uint_t domain, nxt_uint_t type,
16     nxt_uint_t protocol, nxt_uint_t flags)
17 {
18     nxt_socket_t  s;
19 
20 #if (NXT_HAVE_SOCK_NONBLOCK)
21 
22     if (flags & NXT_NONBLOCK) {
23         type |= SOCK_NONBLOCK;
24     }
25 
26 #endif
27 
28     s = socket(domain, type, protocol);
29 
30     if (nxt_slow_path(s == -1)) {
31         nxt_alert(task, "socket(%ui, 0x%uXi, %ui) failed %E",
32                   domain, type, protocol, nxt_socket_errno);
33         return s;
34     }
35 
36     nxt_debug(task, "socket(): %d", s);
37 
38 #if !(NXT_HAVE_SOCK_NONBLOCK)
39 
40     if (flags & NXT_NONBLOCK) {
41         if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) {
42             nxt_socket_close(task, s);
43             return -1;
44         }
45     }
46 
47 #endif
48 
49     return s;
50 }
51 
52 
53 void
nxt_socket_defer_accept(nxt_task_t * task,nxt_socket_t s,nxt_sockaddr_t * sa)54 nxt_socket_defer_accept(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
55 {
56 #if (NXT_HAVE_UNIX_DOMAIN)
57 
58     if (sa->u.sockaddr.sa_family == AF_UNIX) {
59         /* Deferred accept() is not supported on AF_UNIX sockets. */
60         return;
61     }
62 
63 #endif
64 
65 #ifdef TCP_DEFER_ACCEPT
66 
67     /* Defer Linux accept() up to for 1 second. */
68     (void) nxt_socket_setsockopt(task, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
69 
70 #endif
71 }
72 
73 
74 nxt_int_t
nxt_socket_getsockopt(nxt_task_t * task,nxt_socket_t s,nxt_uint_t level,nxt_uint_t sockopt)75 nxt_socket_getsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
76     nxt_uint_t sockopt)
77 {
78     int        val;
79     socklen_t  len;
80 
81     len = sizeof(val);
82 
83     if (nxt_fast_path(getsockopt(s, level, sockopt, &val, &len) == 0)) {
84         nxt_debug(task, "getsockopt(%d, %ui, %s): %d",
85                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
86         return val;
87     }
88 
89     nxt_alert(task, "getsockopt(%d, %ui, %s) failed %E",
90               s, level, nxt_socket_sockopt_name(level, sockopt),
91               nxt_socket_errno);
92 
93     return -1;
94 }
95 
96 
97 nxt_int_t
nxt_socket_setsockopt(nxt_task_t * task,nxt_socket_t s,nxt_uint_t level,nxt_uint_t sockopt,int val)98 nxt_socket_setsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
99     nxt_uint_t sockopt, int val)
100 {
101     socklen_t  len;
102 
103     len = sizeof(val);
104 
105     if (nxt_fast_path(setsockopt(s, level, sockopt, &val, len) == 0)) {
106         nxt_debug(task, "setsockopt(%d, %ui, %s): %d",
107                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
108         return NXT_OK;
109     }
110 
111     nxt_alert(task, "setsockopt(%d, %ui, %s, %d) failed %E",
112               s, level, nxt_socket_sockopt_name(level, sockopt), val,
113               nxt_socket_errno);
114 
115     return NXT_ERROR;
116 }
117 
118 
119 static const char *
nxt_socket_sockopt_name(nxt_uint_t level,nxt_uint_t sockopt)120 nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
121 {
122     switch (level) {
123 
124     case SOL_SOCKET:
125         switch (sockopt) {
126 
127         case SO_SNDBUF:
128             return "SO_SNDBUF";
129 
130         case SO_RCVBUF:
131             return "SO_RCVBUF";
132 
133         case SO_REUSEADDR:
134             return "SO_REUSEADDR";
135 
136         case SO_TYPE:
137             return "SO_TYPE";
138         }
139 
140         break;
141 
142     case IPPROTO_TCP:
143         switch (sockopt) {
144 
145         case TCP_NODELAY:
146             return "TCP_NODELAY";
147 
148 #ifdef TCP_DEFER_ACCEPT
149         case TCP_DEFER_ACCEPT:
150             return "TCP_DEFER_ACCEPT";
151 #endif
152         }
153 
154         break;
155 
156 #if (NXT_INET6)
157     case IPPROTO_IPV6:
158 
159         switch (sockopt) {
160 
161         case IPV6_V6ONLY:
162             return "IPV6_V6ONLY";
163         }
164 
165         break;
166 #endif
167 
168     }
169 
170     return "";
171 }
172 
173 
174 nxt_int_t
nxt_socket_bind(nxt_task_t * task,nxt_socket_t s,nxt_sockaddr_t * sa)175 nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
176 {
177     nxt_debug(task, "bind(%d, %*s)", s, (size_t) sa->length,
178               nxt_sockaddr_start(sa));
179 
180     if (nxt_fast_path(bind(s, &sa->u.sockaddr, sa->socklen) == 0)) {
181         return NXT_OK;
182     }
183 
184     nxt_alert(task, "bind(%d, %*s) failed %E",
185               s, (size_t) sa->length, nxt_sockaddr_start(sa), nxt_socket_errno);
186 
187     return NXT_ERROR;
188 }
189 
190 
191 nxt_int_t
nxt_socket_connect(nxt_task_t * task,nxt_socket_t s,nxt_sockaddr_t * sa)192 nxt_socket_connect(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
193 {
194     nxt_err_t   err;
195     nxt_int_t   ret;
196     nxt_uint_t  level;
197 
198     nxt_debug(task, "connect(%d, %*s)",
199               s, (size_t) sa->length, nxt_sockaddr_start(sa));
200 
201     if (connect(s, &sa->u.sockaddr, sa->socklen) == 0) {
202         return NXT_OK;
203     }
204 
205     err = nxt_socket_errno;
206 
207     switch (err) {
208 
209     case NXT_EINPROGRESS:
210         nxt_debug(task, "connect(%d, %*s) in progress",
211                   s, (size_t) sa->length, nxt_sockaddr_start(sa));
212         return NXT_AGAIN;
213 
214     case NXT_ECONNREFUSED:
215 #if (NXT_LINUX)
216     case NXT_EAGAIN:
217         /*
218          * Linux returns EAGAIN instead of ECONNREFUSED
219          * for UNIX sockets if a listen queue is full.
220          */
221 #endif
222         level = NXT_LOG_ERR;
223         ret = NXT_DECLINED;
224         break;
225 
226     case NXT_ECONNRESET:
227     case NXT_ENETDOWN:
228     case NXT_ENETUNREACH:
229     case NXT_EHOSTDOWN:
230     case NXT_EHOSTUNREACH:
231         level = NXT_LOG_ERR;
232         ret = NXT_ERROR;
233         break;
234 
235     default:
236         level = NXT_LOG_ALERT;
237         ret = NXT_ERROR;
238     }
239 
240     nxt_log(task, level, "connect(%d, %*s) failed %E",
241             s, (size_t) sa->length, nxt_sockaddr_start(sa), err);
242 
243     return ret;
244 }
245 
246 
247 void
nxt_socket_shutdown(nxt_task_t * task,nxt_socket_t s,nxt_uint_t how)248 nxt_socket_shutdown(nxt_task_t *task, nxt_socket_t s, nxt_uint_t how)
249 {
250     nxt_err_t   err;
251     nxt_uint_t  level;
252 
253     if (nxt_fast_path(shutdown(s, how) == 0)) {
254         nxt_debug(task, "shutdown(%d, %ui)", s, how);
255         return;
256     }
257 
258     err = nxt_socket_errno;
259 
260     switch (err) {
261 
262     case NXT_ENOTCONN:
263         level = NXT_LOG_DEBUG;
264         break;
265 
266     case NXT_ECONNRESET:
267     case NXT_ENETDOWN:
268     case NXT_ENETUNREACH:
269     case NXT_EHOSTDOWN:
270     case NXT_EHOSTUNREACH:
271         level = NXT_LOG_ERR;
272         break;
273 
274     default:
275         level = NXT_LOG_ALERT;
276     }
277 
278     nxt_log(task, level, "shutdown(%d, %ui) failed %E", s, how, err);
279 }
280 
281 
282 void
nxt_socket_close(nxt_task_t * task,nxt_socket_t s)283 nxt_socket_close(nxt_task_t *task, nxt_socket_t s)
284 {
285     nxt_err_t   err;
286     nxt_uint_t  level;
287 
288     if (nxt_fast_path(close(s) == 0)) {
289         nxt_debug(task, "socket close(%d)", s);
290         return;
291     }
292 
293     err = nxt_socket_errno;
294 
295     switch (err) {
296 
297     case NXT_ENOTCONN:
298         level = NXT_LOG_DEBUG;
299         break;
300 
301     case NXT_ECONNRESET:
302     case NXT_ENETDOWN:
303     case NXT_ENETUNREACH:
304     case NXT_EHOSTDOWN:
305     case NXT_EHOSTUNREACH:
306         level = NXT_LOG_ERR;
307         break;
308 
309     default:
310         level = NXT_LOG_ALERT;
311     }
312 
313     nxt_log(task, level, "socket close(%d) failed %E", s, err);
314 }
315 
316 
317 nxt_err_t
nxt_socket_error(nxt_socket_t s)318 nxt_socket_error(nxt_socket_t s)
319 {
320     int        ret, err;
321     socklen_t  len;
322 
323     err = 0;
324     len = sizeof(int);
325     /*
326      * Linux and BSDs return 0 and store a pending error in the err argument;
327      * Solaris returns -1 and sets the errno.
328      */
329     ret = getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &err, &len);
330 
331     if (nxt_slow_path(ret == -1)) {
332         err = nxt_errno;
333     }
334 
335     return err;
336 }
337 
338 
339 nxt_uint_t
nxt_socket_error_level(nxt_err_t err)340 nxt_socket_error_level(nxt_err_t err)
341 {
342     switch (err) {
343 
344     case NXT_EPIPE:
345     case NXT_ECONNRESET:
346     case NXT_ENOTCONN:
347     case NXT_ETIMEDOUT:
348     case NXT_ENETDOWN:
349     case NXT_ENETUNREACH:
350     case NXT_EHOSTDOWN:
351     case NXT_EHOSTUNREACH:
352         return NXT_LOG_INFO;
353 
354     case NXT_ECONNREFUSED:
355         return NXT_LOG_ERR;
356 
357     default:
358         return NXT_LOG_ALERT;
359     }
360 }
361