Coverage Report

Created: 2025-10-28 06:05

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