ngx_event_connect.c revision e18a033b
1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_event.h>
11#include <ngx_event_connect.h>
12
13
14#if (NGX_HAVE_TRANSPARENT_PROXY)
15static ngx_int_t ngx_event_connect_set_transparent(ngx_peer_connection_t *pc,
16    ngx_socket_t s);
17#endif
18
19
20ngx_int_t
21ngx_event_connect_peer(ngx_peer_connection_t *pc)
22{
23    int                rc, type;
24#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
25    in_port_t          port;
26#endif
27    ngx_int_t          event;
28    ngx_err_t          err;
29    ngx_uint_t         level;
30    ngx_socket_t       s;
31    ngx_event_t       *rev, *wev;
32    ngx_connection_t  *c;
33
34    rc = pc->get(pc, pc->data);
35    if (rc != NGX_OK) {
36        return rc;
37    }
38
39    type = (pc->type ? pc->type : SOCK_STREAM);
40
41    s = ngx_socket(pc->sockaddr->sa_family, type, 0);
42
43    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, "%s socket %d",
44                   (type == SOCK_STREAM) ? "stream" : "dgram", s);
45
46    if (s == (ngx_socket_t) -1) {
47        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
48                      ngx_socket_n " failed");
49        return NGX_ERROR;
50    }
51
52
53    c = ngx_get_connection(s, pc->log);
54
55    if (c == NULL) {
56        if (ngx_close_socket(s) == -1) {
57            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
58                          ngx_close_socket_n "failed");
59        }
60
61        return NGX_ERROR;
62    }
63
64    c->type = type;
65
66    if (pc->rcvbuf) {
67        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
68                       (const void *) &pc->rcvbuf, sizeof(int)) == -1)
69        {
70            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
71                          "setsockopt(SO_RCVBUF) failed");
72            goto failed;
73        }
74    }
75
76    if (ngx_nonblocking(s) == -1) {
77        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
78                      ngx_nonblocking_n " failed");
79
80        goto failed;
81    }
82
83    if (pc->local) {
84
85#if (NGX_HAVE_TRANSPARENT_PROXY)
86        if (pc->transparent) {
87            if (ngx_event_connect_set_transparent(pc, s) != NGX_OK) {
88                goto failed;
89            }
90        }
91#endif
92
93#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
94        port = ngx_inet_get_port(pc->local->sockaddr);
95#endif
96
97#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT)
98
99        if (pc->sockaddr->sa_family != AF_UNIX && port == 0) {
100            static int  bind_address_no_port = 1;
101
102            if (bind_address_no_port) {
103                if (setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT,
104                               (const void *) &bind_address_no_port,
105                               sizeof(int)) == -1)
106                {
107                    err = ngx_socket_errno;
108
109                    if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) {
110                        ngx_log_error(NGX_LOG_ALERT, pc->log, err,
111                                      "setsockopt(IP_BIND_ADDRESS_NO_PORT) "
112                                      "failed, ignored");
113
114                    } else {
115                        bind_address_no_port = 0;
116                    }
117                }
118            }
119        }
120
121#endif
122
123#if (NGX_LINUX)
124
125        if (pc->type == SOCK_DGRAM && port != 0) {
126            int  reuse_addr = 1;
127
128            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
129                           (const void *) &reuse_addr, sizeof(int))
130                 == -1)
131            {
132                ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
133                              "setsockopt(SO_REUSEADDR) failed");
134                goto failed;
135            }
136        }
137
138#endif
139
140        if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {
141            ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,
142                          "bind(%V) failed", &pc->local->name);
143
144            goto failed;
145        }
146    }
147
148    if (type == SOCK_STREAM) {
149        c->recv = ngx_recv;
150        c->send = ngx_send;
151        c->recv_chain = ngx_recv_chain;
152        c->send_chain = ngx_send_chain;
153
154        c->sendfile = 1;
155
156        if (pc->sockaddr->sa_family == AF_UNIX) {
157            c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
158            c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
159
160#if (NGX_SOLARIS)
161            /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
162            c->sendfile = 0;
163#endif
164        }
165
166    } else { /* type == SOCK_DGRAM */
167        c->recv = ngx_udp_recv;
168        c->send = ngx_send;
169        c->send_chain = ngx_udp_send_chain;
170    }
171
172    c->log_error = pc->log_error;
173
174    rev = c->read;
175    wev = c->write;
176
177    rev->log = pc->log;
178    wev->log = pc->log;
179
180    pc->connection = c;
181
182    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
183
184    if (ngx_add_conn) {
185        if (ngx_add_conn(c) == NGX_ERROR) {
186            goto failed;
187        }
188    }
189
190    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,
191                   "connect to %V, fd:%d #%uA", pc->name, s, c->number);
192
193    rc = connect(s, pc->sockaddr, pc->socklen);
194
195    if (rc == -1) {
196        err = ngx_socket_errno;
197
198
199        if (err != NGX_EINPROGRESS
200#if (NGX_WIN32)
201            /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
202            && err != NGX_EAGAIN
203#endif
204            )
205        {
206            if (err == NGX_ECONNREFUSED
207#if (NGX_LINUX)
208                /*
209                 * Linux returns EAGAIN instead of ECONNREFUSED
210                 * for unix sockets if listen queue is full
211                 */
212                || err == NGX_EAGAIN
213#endif
214                || err == NGX_ECONNRESET
215                || err == NGX_ENETDOWN
216                || err == NGX_ENETUNREACH
217                || err == NGX_EHOSTDOWN
218                || err == NGX_EHOSTUNREACH)
219            {
220                level = NGX_LOG_ERR;
221
222            } else {
223                level = NGX_LOG_CRIT;
224            }
225
226            ngx_log_error(level, c->log, err, "connect() to %V failed",
227                          pc->name);
228
229            ngx_close_connection(c);
230            pc->connection = NULL;
231
232            return NGX_DECLINED;
233        }
234    }
235
236    if (ngx_add_conn) {
237        if (rc == -1) {
238
239            /* NGX_EINPROGRESS */
240
241            return NGX_AGAIN;
242        }
243
244        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
245
246        wev->ready = 1;
247
248        return NGX_OK;
249    }
250
251    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
252
253        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,
254                       "connect(): %d", rc);
255
256        if (ngx_blocking(s) == -1) {
257            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
258                          ngx_blocking_n " failed");
259            goto failed;
260        }
261
262        /*
263         * FreeBSD's aio allows to post an operation on non-connected socket.
264         * NT does not support it.
265         *
266         * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT
267         */
268
269        rev->ready = 1;
270        wev->ready = 1;
271
272        return NGX_OK;
273    }
274
275    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
276
277        /* kqueue */
278
279        event = NGX_CLEAR_EVENT;
280
281    } else {
282
283        /* select, poll, /dev/poll */
284
285        event = NGX_LEVEL_EVENT;
286    }
287
288    if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
289        goto failed;
290    }
291
292    if (rc == -1) {
293
294        /* NGX_EINPROGRESS */
295
296        if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
297            goto failed;
298        }
299
300        return NGX_AGAIN;
301    }
302
303    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
304
305    wev->ready = 1;
306
307    return NGX_OK;
308
309failed:
310
311    ngx_close_connection(c);
312    pc->connection = NULL;
313
314    return NGX_ERROR;
315}
316
317
318#if (NGX_HAVE_TRANSPARENT_PROXY)
319
320static ngx_int_t
321ngx_event_connect_set_transparent(ngx_peer_connection_t *pc, ngx_socket_t s)
322{
323    int  value;
324
325    value = 1;
326
327#if defined(SO_BINDANY)
328
329    if (setsockopt(s, SOL_SOCKET, SO_BINDANY,
330                   (const void *) &value, sizeof(int)) == -1)
331    {
332        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
333                      "setsockopt(SO_BINDANY) failed");
334        return NGX_ERROR;
335    }
336
337#else
338
339    switch (pc->local->sockaddr->sa_family) {
340
341    case AF_INET:
342
343#if defined(IP_TRANSPARENT)
344
345        if (setsockopt(s, IPPROTO_IP, IP_TRANSPARENT,
346                       (const void *) &value, sizeof(int)) == -1)
347        {
348            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
349                          "setsockopt(IP_TRANSPARENT) failed");
350            return NGX_ERROR;
351        }
352
353#elif defined(IP_BINDANY)
354
355        if (setsockopt(s, IPPROTO_IP, IP_BINDANY,
356                       (const void *) &value, sizeof(int)) == -1)
357        {
358            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
359                          "setsockopt(IP_BINDANY) failed");
360            return NGX_ERROR;
361        }
362
363#endif
364
365        break;
366
367#if (NGX_HAVE_INET6)
368
369    case AF_INET6:
370
371#if defined(IPV6_TRANSPARENT)
372
373        if (setsockopt(s, IPPROTO_IPV6, IPV6_TRANSPARENT,
374                       (const void *) &value, sizeof(int)) == -1)
375        {
376            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
377                          "setsockopt(IPV6_TRANSPARENT) failed");
378            return NGX_ERROR;
379        }
380
381#elif defined(IPV6_BINDANY)
382
383        if (setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY,
384                       (const void *) &value, sizeof(int)) == -1)
385        {
386            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
387                          "setsockopt(IPV6_BINDANY) failed");
388            return NGX_ERROR;
389        }
390
391#endif
392        break;
393
394#endif /* NGX_HAVE_INET6 */
395
396    }
397
398#endif /* SO_BINDANY */
399
400    return NGX_OK;
401}
402
403#endif
404
405
406ngx_int_t
407ngx_event_get_peer(ngx_peer_connection_t *pc, void *data)
408{
409    return NGX_OK;
410}
411