summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_socket.c
blob: 9ac8ecd2241d6ae365f38af9d51325de66e04429 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) NGINX, Inc.
 */

#include <nxt_main.h>


static const char *nxt_socket_sockopt_name(nxt_uint_t level,
    nxt_uint_t sockopt);


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)
{
    nxt_socket_t  s;

#if (NXT_HAVE_SOCK_NONBLOCK)

    if (flags & NXT_NONBLOCK) {
        type |= SOCK_NONBLOCK;
    }

#endif

    s = socket(domain, type, protocol);

    if (nxt_slow_path(s == -1)) {
        nxt_alert(task, "socket(%ui, 0x%uXi, %ui) failed %E",
                  domain, type, protocol, nxt_socket_errno);
        return s;
    }

    nxt_debug(task, "socket(): %d", s);

#if !(NXT_HAVE_SOCK_NONBLOCK)

    if (flags & NXT_NONBLOCK) {
        if (nxt_slow_path(nxt_socket_nonblocking(task, s) != NXT_OK)) {
            nxt_socket_close(task, s);
            return -1;
        }
    }

#endif

    return s;
}


void
nxt_socket_defer_accept(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
{
#if (NXT_HAVE_UNIX_DOMAIN)

    if (sa->u.sockaddr.sa_family == AF_UNIX) {
        /* Deferred accept() is not supported on AF_UNIX sockets. */
        return;
    }

#endif

#ifdef TCP_DEFER_ACCEPT

    /* Defer Linux accept() up to for 1 second. */
    (void) nxt_socket_setsockopt(task, s, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);

#endif
}


nxt_int_t
nxt_socket_getsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
    nxt_uint_t sockopt)
{
    int        val;
    socklen_t  len;

    len = sizeof(val);

    if (nxt_fast_path(getsockopt(s, level, sockopt, &val, &len) == 0)) {
        nxt_debug(task, "getsockopt(%d, %ui, %s): %d",
                  s, level, nxt_socket_sockopt_name(level, sockopt), val);
        return val;
    }

    nxt_alert(task, "getsockopt(%d, %ui, %s) failed %E",
              s, level, nxt_socket_sockopt_name(level, sockopt),
              nxt_socket_errno);

    return -1;
}


nxt_int_t
nxt_socket_setsockopt(nxt_task_t *task, nxt_socket_t s, nxt_uint_t level,
    nxt_uint_t sockopt, int val)
{
    socklen_t  len;

    len = sizeof(val);

    if (nxt_fast_path(setsockopt(s, level, sockopt, &val, len) == 0)) {
        nxt_debug(task, "setsockopt(%d, %ui, %s): %d",
                  s, level, nxt_socket_sockopt_name(level, sockopt), val);
        return NXT_OK;
    }

    nxt_alert(task, "setsockopt(%d, %ui, %s, %d) failed %E",
              s, level, nxt_socket_sockopt_name(level, sockopt), val,
              nxt_socket_errno);

    return NXT_ERROR;
}


static const char *
nxt_socket_sockopt_name(nxt_uint_t level, nxt_uint_t sockopt)
{
    switch (level) {

    case SOL_SOCKET:
        switch (sockopt) {

        case SO_SNDBUF:
            return "SO_SNDBUF";

        case SO_RCVBUF:
            return "SO_RCVBUF";

        case SO_REUSEADDR:
            return "SO_REUSEADDR";

        case SO_TYPE:
            return "SO_TYPE";
        }

        break;

    case IPPROTO_TCP:
        switch (sockopt) {

        case TCP_NODELAY:
            return "TCP_NODELAY";

#ifdef TCP_DEFER_ACCEPT
        case TCP_DEFER_ACCEPT:
            return "TCP_DEFER_ACCEPT";
#endif
        }

        break;

#if (NXT_INET6)
    case IPPROTO_IPV6:

        switch (sockopt) {

        case IPV6_V6ONLY:
            return "IPV6_V6ONLY";
        }

        break;
#endif

    }

    return "";
}


nxt_int_t
nxt_socket_bind(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
{
    nxt_debug(task, "bind(%d, %*s)", s, (size_t) sa->length,
              nxt_sockaddr_start(sa));

    if (nxt_fast_path(bind(s, &sa->u.sockaddr, sa->socklen) == 0)) {
        return NXT_OK;
    }

    nxt_alert(task, "bind(%d, %*s) failed %E",
              s, (size_t) sa->length, nxt_sockaddr_start(sa), nxt_socket_errno);

    return NXT_ERROR;
}


nxt_int_t
nxt_socket_connect(nxt_task_t *task, nxt_socket_t s, nxt_sockaddr_t *sa)
{
    nxt_err_t   err;
    nxt_int_t   ret;
    nxt_uint_t  level;

    nxt_debug(task, "connect(%d, %*s)",
              s, (size_t) sa->length, nxt_sockaddr_start(sa));

    if (connect(s, &sa->u.sockaddr, sa->socklen) == 0) {
        return NXT_OK;
    }

    err = nxt_socket_errno;

    switch (err) {

    case NXT_EINPROGRESS:
        nxt_debug(task, "connect(%d, %*s) in progress",
                  s, (size_t) sa->length, nxt_sockaddr_start(sa));
        return NXT_AGAIN;

    case NXT_ECONNREFUSED:
#if (NXT_LINUX)
    case NXT_EAGAIN:
        /*
         * Linux returns EAGAIN instead of ECONNREFUSED
         * for UNIX sockets if a listen queue is full.
         */
#endif
        level = NXT_LOG_ERR;
        ret = NXT_DECLINED;
        break;

    case NXT_ECONNRESET:
    case NXT_ENETDOWN:
    case NXT_ENETUNREACH:
    case NXT_EHOSTDOWN:
    case NXT_EHOSTUNREACH:
        level = NXT_LOG_ERR;
        ret = NXT_ERROR;
        break;

    default:
        level = NXT_LOG_ALERT;
        ret = NXT_ERROR;
    }

    nxt_log(task, level, "connect(%d, %*s) failed %E",
            s, (size_t) sa->length, nxt_sockaddr_start(sa), err);

    return ret;
}


void
nxt_socket_shutdown(nxt_task_t *task, nxt_socket_t s, nxt_uint_t how)
{
    nxt_err_t   err;
    nxt_uint_t  level;

    if (nxt_fast_path(shutdown(s, how) == 0)) {
        nxt_debug(task, "shutdown(%d, %ui)", s, how);
        return;
    }

    err = nxt_socket_errno;

    switch (err) {

    case NXT_ENOTCONN:
        level = NXT_LOG_DEBUG;
        break;

    case NXT_ECONNRESET:
    case NXT_ENETDOWN:
    case NXT_ENETUNREACH:
    case NXT_EHOSTDOWN:
    case NXT_EHOSTUNREACH:
        level = NXT_LOG_ERR;
        break;

    default:
        level = NXT_LOG_ALERT;
    }

    nxt_log(task, level, "shutdown(%d, %ui) failed %E", s, how, err);
}


void
nxt_socket_close(nxt_task_t *task, nxt_socket_t s)
{
    nxt_err_t   err;
    nxt_uint_t  level;

    if (nxt_fast_path(close(s) == 0)) {
        nxt_debug(task, "socket close(%d)", s);
        return;
    }

    err = nxt_socket_errno;

    switch (err) {

    case NXT_ENOTCONN:
        level = NXT_LOG_DEBUG;
        break;

    case NXT_ECONNRESET:
    case NXT_ENETDOWN:
    case NXT_ENETUNREACH:
    case NXT_EHOSTDOWN:
    case NXT_EHOSTUNREACH:
        level = NXT_LOG_ERR;
        break;

    default:
        level = NXT_LOG_ALERT;
    }

    nxt_log(task, level, "socket close(%d) failed %E", s, err);
}


nxt_err_t
nxt_socket_error(nxt_socket_t s)
{
    int        ret, err;
    socklen_t  len;

    err = 0;
    len = sizeof(int);
    /*
     * Linux and BSDs return 0 and store a pending error in the err argument;
     * Solaris returns -1 and sets the errno.
     */
    ret = getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &err, &len);

    if (nxt_slow_path(ret == -1)) {
        err = nxt_errno;
    }

    return err;
}


nxt_uint_t
nxt_socket_error_level(nxt_err_t err)
{
    switch (err) {

    case NXT_EPIPE:
    case NXT_ECONNRESET:
    case NXT_ENOTCONN:
    case NXT_ETIMEDOUT:
    case NXT_ENETDOWN:
    case NXT_ENETUNREACH:
    case NXT_EHOSTDOWN:
    case NXT_EHOSTUNREACH:
        return NXT_LOG_INFO;

    case NXT_ECONNREFUSED:
        return NXT_LOG_ERR;

    default:
        return NXT_LOG_ALERT;
    }
}