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