xref: /unit/src/nxt_socket.c (revision 564:762f8c976ead)
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
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
54 nxt_socket_close(nxt_task_t *task, nxt_socket_t s)
55 {
56     if (nxt_fast_path(close(s) == 0)) {
57         nxt_debug(task, "socket close(%d)", s);
58 
59     } else {
60         nxt_alert(task, "socket close(%d) failed %E", s, nxt_socket_errno);
61     }
62 }
63 
64 
65 void
66 nxt_socket_defer_accept(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
67 {
68 #if (NXT_HAVE_UNIX_DOMAIN)
69 
70     if (sa->u.sockaddr.sa_family == AF_UNIX) {
71         /* Deferred accept() is not supported on AF_UNIX sockets. */
72         return;
73     }
74 
75 #endif
76 
77 #ifdef TCP_DEFER_ACCEPT
78 
79     /* Defer Linux accept() up to for 1 second. */
80     (void) nxt_socket_setsockopt(task, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
81 
82 #endif
83 }
84 
85 
86 nxt_int_t
87 nxt_socket_getsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
88     nxt_uint_t sockopt)
89 {
90     int        val;
91     socklen_t  len;
92 
93     len = sizeof(val);
94 
95     if (nxt_fast_path(getsockopt(s, level, sockopt, &val, &len) == 0)) {
96         nxt_debug(task, "getsockopt(%d, %ui, %s): %d",
97                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
98         return val;
99     }
100 
101     nxt_alert(task, "getsockopt(%d, %ui, %s) failed %E",
102               s, level, nxt_socket_sockopt_name(level, sockopt),
103               nxt_socket_errno);
104 
105     return -1;
106 }
107 
108 
109 nxt_int_t
110 nxt_socket_setsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
111     nxt_uint_t sockopt, int val)
112 {
113     socklen_t  len;
114 
115     len = sizeof(val);
116 
117     if (nxt_fast_path(setsockopt(s, level, sockopt, &val, len) == 0)) {
118         nxt_debug(task, "setsockopt(%d, %ui, %s): %d",
119                   s, level, nxt_socket_sockopt_name(level, sockopt), val);
120         return NXT_OK;
121     }
122 
123     nxt_alert(task, "setsockopt(%d, %ui, %s, %d) failed %E",
124               s, level, nxt_socket_sockopt_name(level, sockopt), val,
125               nxt_socket_errno);
126 
127     return NXT_ERROR;
128 }
129 
130 
131 static const char *
132 nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
133 {
134     switch (level) {
135 
136     case SOL_SOCKET:
137         switch (sockopt) {
138 
139         case SO_SNDBUF:
140             return "SO_SNDBUF";
141 
142         case SO_RCVBUF:
143             return "SO_RCVBUF";
144 
145         case SO_REUSEADDR:
146             return "SO_REUSEADDR";
147 
148         case SO_TYPE:
149             return "SO_TYPE";
150         }
151 
152         break;
153 
154     case IPPROTO_TCP:
155         switch (sockopt) {
156 
157         case TCP_NODELAY:
158             return "TCP_NODELAY";
159 
160 #ifdef TCP_DEFER_ACCEPT
161         case TCP_DEFER_ACCEPT:
162             return "TCP_DEFER_ACCEPT";
163 #endif
164         }
165 
166         break;
167 
168 #if (NXT_INET6)
169     case IPPROTO_IPV6:
170 
171         switch (sockopt) {
172 
173         case IPV6_V6ONLY:
174             return "IPV6_V6ONLY";
175         }
176 
177         break;
178 #endif
179 
180     }
181 
182     return "";
183 }
184 
185 
186 nxt_int_t
187 nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa,
188     nxt_bool_t test)
189 {
190     nxt_err_t  err;
191 
192     nxt_debug(task, "bind(%d, %*s)", s, (size_t) sa->length,
193               nxt_sockaddr_start(sa));
194 
195     if (nxt_fast_path(bind(s, &sa->u.sockaddr, sa->socklen) == 0)) {
196         return NXT_OK;
197     }
198 
199     err = nxt_socket_errno;
200 
201     if (err == NXT_EADDRINUSE && test) {
202         return NXT_DECLINED;
203     }
204 
205     nxt_alert(task, "bind(%d, %*s) failed %E",
206               s, (size_t) sa->length, nxt_sockaddr_start(sa), err);
207 
208     return NXT_ERROR;
209 }
210 
211 
212 nxt_int_t
213 nxt_socket_connect(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
214 {
215     nxt_err_t   err;
216     nxt_int_t   ret;
217     nxt_uint_t  level;
218 
219     nxt_debug(task, "connect(%d, %*s)",
220               s, (size_t) sa->length, nxt_sockaddr_start(sa));
221 
222     if (connect(s, &sa->u.sockaddr, sa->socklen) == 0) {
223         return NXT_OK;
224     }
225 
226     err = nxt_socket_errno;
227 
228     switch (err) {
229 
230     case NXT_EINPROGRESS:
231         nxt_debug(task, "connect(%d, %*s) in progress",
232                   s, (size_t) sa->length, nxt_sockaddr_start(sa));
233         return NXT_AGAIN;
234 
235     case NXT_ECONNREFUSED:
236 #if (NXT_LINUX)
237     case NXT_EAGAIN:
238         /*
239          * Linux returns EAGAIN instead of ECONNREFUSED
240          * for UNIX sockets if a listen queue is full.
241          */
242 #endif
243         level = NXT_LOG_ERR;
244         ret = NXT_DECLINED;
245         break;
246 
247     case NXT_ECONNRESET:
248     case NXT_ENETDOWN:
249     case NXT_ENETUNREACH:
250     case NXT_EHOSTDOWN:
251     case NXT_EHOSTUNREACH:
252         level = NXT_LOG_ERR;
253         ret = NXT_ERROR;
254         break;
255 
256     default:
257         level = NXT_LOG_ALERT;
258         ret = NXT_ERROR;
259     }
260 
261     nxt_log(task, level, "connect(%d, %*s) failed %E",
262             s, (size_t) sa->length, nxt_sockaddr_start(sa), err);
263 
264     return ret;
265 }
266 
267 
268 void
269 nxt_socket_shutdown(nxt_task_t *task, nxt_socket_t s, nxt_uint_t how)
270 {
271     nxt_err_t   err;
272     nxt_uint_t  level;
273 
274     if (nxt_fast_path(shutdown(s, how) == 0)) {
275         nxt_debug(task, "shutdown(%d, %ui)", s, how);
276         return;
277     }
278 
279     err = nxt_socket_errno;
280 
281     switch (err) {
282 
283     case NXT_ENOTCONN:
284         level = NXT_LOG_INFO;
285         break;
286 
287     case NXT_ECONNRESET:
288     case NXT_ENETDOWN:
289     case NXT_ENETUNREACH:
290     case NXT_EHOSTDOWN:
291     case NXT_EHOSTUNREACH:
292         level = NXT_LOG_ERR;
293         break;
294 
295     default:
296         level = NXT_LOG_ALERT;
297     }
298 
299     nxt_log(task, level, "shutdown(%d, %ui) failed %E", s, how, err);
300 }
301 
302 
303 nxt_uint_t
304 nxt_socket_error_level(nxt_err_t err)
305 {
306     switch (err) {
307 
308     case NXT_EPIPE:
309     case NXT_ECONNRESET:
310     case NXT_ENOTCONN:
311     case NXT_ETIMEDOUT:
312     case NXT_ENETDOWN:
313     case NXT_ENETUNREACH:
314     case NXT_EHOSTDOWN:
315     case NXT_EHOSTUNREACH:
316         return NXT_LOG_ERR;
317 
318     default:
319         return NXT_LOG_ALERT;
320     }
321 }
322