blob: c5bb80681f5f3da7133dd77ea47f4e6059e2037c [file] [log] [blame]
Igor Sysoev6b863e32003-05-12 15:52:24 +00001
Igor Sysoevd90282d2004-09-28 08:34:51 +00002/*
Igor Sysoevff8da912004-09-29 16:00:49 +00003 * Copyright (C) Igor Sysoev
Maxim Konovalovf8d59e32012-01-18 15:07:43 +00004 * Copyright (C) Nginx, Inc.
Igor Sysoevd90282d2004-09-28 08:34:51 +00005 */
6
7
Igor Sysoev87a01ea2003-10-02 05:39:37 +00008#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_event.h>
Igor Sysoev6b863e32003-05-12 15:52:24 +000011#include <ngx_event_connect.h>
12
Igor Sysoevae5c59c2003-08-14 06:00:28 +000013
Roman Arutyunyan055c4602015-12-18 19:05:27 +030014#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
Igor Sysoevaa828612005-02-09 14:31:07 +000020ngx_int_t
21ngx_event_connect_peer(ngx_peer_connection_t *pc)
Igor Sysoev6b863e32003-05-12 15:52:24 +000022{
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +030023 int rc, type;
Roman Arutyunyana5117ea2016-06-20 12:48:47 +030024#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
25 in_port_t port;
26#endif
Igor Sysoev708fe2e2007-09-10 09:08:12 +000027 ngx_int_t event;
Igor Sysoev31eb8c02005-09-23 11:02:22 +000028 ngx_err_t err;
Igor Sysoev3d2fd182006-12-04 16:46:13 +000029 ngx_uint_t level;
Igor Sysoev31eb8c02005-09-23 11:02:22 +000030 ngx_socket_t s;
31 ngx_event_t *rev, *wev;
32 ngx_connection_t *c;
Igor Sysoev3a081182003-07-23 13:10:12 +000033
Igor Sysoev3d2fd182006-12-04 16:46:13 +000034 rc = pc->get(pc, pc->data);
35 if (rc != NGX_OK) {
36 return rc;
Igor Sysoev72f2e362003-07-22 19:53:10 +000037 }
38
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +030039 type = (pc->type ? pc->type : SOCK_STREAM);
Igor Sysoev890fc962003-07-20 21:15:59 +000040
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +030041 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);
Igor Sysoev899b44e2005-05-12 14:58:06 +000045
Maxim Dounin48d96ce2013-09-04 20:48:28 +040046 if (s == (ngx_socket_t) -1) {
Igor Sysoev72f2e362003-07-22 19:53:10 +000047 ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
Igor Sysoev6b863e32003-05-12 15:52:24 +000048 ngx_socket_n " failed");
Igor Sysoev890fc962003-07-20 21:15:59 +000049 return NGX_ERROR;
Igor Sysoev6b863e32003-05-12 15:52:24 +000050 }
51
Igor Sysoev732a2712004-04-21 18:54:33 +000052
Igor Sysoev31eb8c02005-09-23 11:02:22 +000053 c = ngx_get_connection(s, pc->log);
Igor Sysoev732a2712004-04-21 18:54:33 +000054
Igor Sysoev31eb8c02005-09-23 11:02:22 +000055 if (c == NULL) {
Igor Sysoev732a2712004-04-21 18:54:33 +000056 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
Igor Sysoev732a2712004-04-21 18:54:33 +000061 return NGX_ERROR;
62 }
63
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +030064 c->type = type;
65
Igor Sysoev72f2e362003-07-22 19:53:10 +000066 if (pc->rcvbuf) {
Igor Sysoev6b863e32003-05-12 15:52:24 +000067 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
Igor Sysoevc2068d02005-10-19 12:33:58 +000068 (const void *) &pc->rcvbuf, sizeof(int)) == -1)
69 {
Igor Sysoev72f2e362003-07-22 19:53:10 +000070 ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
Igor Sysoev6b863e32003-05-12 15:52:24 +000071 "setsockopt(SO_RCVBUF) failed");
Igor Sysoev72e92872009-11-02 15:24:02 +000072 goto failed;
Igor Sysoev6b863e32003-05-12 15:52:24 +000073 }
74 }
75
76 if (ngx_nonblocking(s) == -1) {
Igor Sysoev72f2e362003-07-22 19:53:10 +000077 ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
Igor Sysoev6b863e32003-05-12 15:52:24 +000078 ngx_nonblocking_n " failed");
79
Igor Sysoev72e92872009-11-02 15:24:02 +000080 goto failed;
81 }
Igor Sysoev31eb8c02005-09-23 11:02:22 +000082
Igor Sysoev72e92872009-11-02 15:24:02 +000083 if (pc->local) {
Roman Arutyunyan055c4602015-12-18 19:05:27 +030084
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
Roman Arutyunyana5117ea2016-06-20 12:48:47 +030093#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)
Ruslan Ermilovb003bf22016-08-22 11:40:10 +030094 port = ngx_inet_get_port(pc->local->sockaddr);
Roman Arutyunyana5117ea2016-06-20 12:48:47 +030095#endif
96
Andrei Belov92b23b52016-06-20 10:41:17 +030097#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT)
98
Roman Arutyunyana5117ea2016-06-20 12:48:47 +030099 if (pc->sockaddr->sa_family != AF_UNIX && port == 0) {
Andrei Belov92b23b52016-06-20 10:41:17 +0300100 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
Roman Arutyunyana5117ea2016-06-20 12:48:47 +0300123#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
Igor Sysoev72e92872009-11-02 15:24:02 +0000140 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;
Igor Sysoev6b863e32003-05-12 15:52:24 +0000145 }
Igor Sysoev6b863e32003-05-12 15:52:24 +0000146 }
147
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +0300148 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;
Igor Sysoeve5a222c2005-01-25 12:27:35 +0000153
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +0300154 c->sendfile = 1;
Igor Sysoeve1013382007-08-14 20:44:09 +0000155
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +0300156 if (pc->sockaddr->sa_family == AF_UNIX) {
157 c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
158 c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
Igor Sysoevd3283ff2005-12-05 13:18:09 +0000159
160#if (NGX_SOLARIS)
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +0300161 /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
162 c->sendfile = 0;
Igor Sysoevd3283ff2005-12-05 13:18:09 +0000163#endif
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +0300164 }
165
166 } else { /* type == SOCK_DGRAM */
167 c->recv = ngx_udp_recv;
168 c->send = ngx_send;
Roman Arutyunyan17003b52016-09-15 14:55:46 +0300169 c->send_chain = ngx_udp_send_chain;
Igor Sysoev72f2e362003-07-22 19:53:10 +0000170 }
Igor Sysoev890fc962003-07-20 21:15:59 +0000171
Roman Arutyunyan850dfaa2016-01-20 19:52:12 +0300172 c->log_error = pc->log_error;
173
Igor Sysoev78452232005-10-12 13:50:36 +0000174 rev = c->read;
175 wev = c->write;
Igor Sysoev6b863e32003-05-12 15:52:24 +0000176
Igor Sysoev160d7742003-11-19 16:26:41 +0000177 rev->log = pc->log;
178 wev->log = pc->log;
179
Igor Sysoev72f2e362003-07-22 19:53:10 +0000180 pc->connection = c;
181
Igor Sysoev78452232005-10-12 13:50:36 +0000182 c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
Igor Sysoev54498db2004-02-11 17:08:49 +0000183
Igor Sysoev72f2e362003-07-22 19:53:10 +0000184 if (ngx_add_conn) {
185 if (ngx_add_conn(c) == NGX_ERROR) {
Igor Sysoev72e92872009-11-02 15:24:02 +0000186 goto failed;
Igor Sysoev72f2e362003-07-22 19:53:10 +0000187 }
Igor Sysoev0e5dc5c2005-11-15 13:30:52 +0000188 }
Igor Sysoev72f2e362003-07-22 19:53:10 +0000189
Igor Sysoev31eb8c02005-09-23 11:02:22 +0000190 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,
Sergey Kandaurovbd3516e2014-03-06 18:25:59 +0400191 "connect to %V, fd:%d #%uA", pc->name, s, c->number);
Igor Sysoev425a42c2003-10-27 16:16:17 +0000192
Igor Sysoev3d2fd182006-12-04 16:46:13 +0000193 rc = connect(s, pc->sockaddr, pc->socklen);
Igor Sysoev3a081182003-07-23 13:10:12 +0000194
195 if (rc == -1) {
196 err = ngx_socket_errno;
Igor Sysoevf2e676a2003-11-16 21:49:42 +0000197
Igor Sysoevf2e676a2003-11-16 21:49:42 +0000198
Igor Sysoev58e9f222008-07-09 15:42:13 +0000199 if (err != NGX_EINPROGRESS
200#if (NGX_WIN32)
201 /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
202 && err != NGX_EAGAIN
203#endif
204 )
205 {
Igor Sysoev02d8e8e2008-01-25 14:57:35 +0000206 if (err == NGX_ECONNREFUSED
Igor Sysoev58e9f222008-07-09 15:42:13 +0000207#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
Igor Sysoev288e5032009-02-25 14:27:34 +0000214 || err == NGX_ECONNRESET
Igor Sysoev02d8e8e2008-01-25 14:57:35 +0000215 || err == NGX_ENETDOWN
216 || err == NGX_ENETUNREACH
217 || err == NGX_EHOSTDOWN
218 || err == NGX_EHOSTUNREACH)
219 {
Igor Sysoeve5733802005-09-08 14:36:09 +0000220 level = NGX_LOG_ERR;
Igor Sysoev02d8e8e2008-01-25 14:57:35 +0000221
Igor Sysoeve5733802005-09-08 14:36:09 +0000222 } else {
223 level = NGX_LOG_CRIT;
Igor Sysoevaa828612005-02-09 14:31:07 +0000224 }
Igor Sysoeve5733802005-09-08 14:36:09 +0000225
226 ngx_log_error(level, c->log, err, "connect() to %V failed",
Igor Sysoev3d2fd182006-12-04 16:46:13 +0000227 pc->name);
Igor Sysoev931a4002003-10-07 15:30:05 +0000228
Maxim Dounin4a23bc52012-01-30 11:12:52 +0000229 ngx_close_connection(c);
230 pc->connection = NULL;
231
Igor Sysoev31eb8c02005-09-23 11:02:22 +0000232 return NGX_DECLINED;
Igor Sysoev3a081182003-07-23 13:10:12 +0000233 }
234 }
235
Igor Sysoev67f450d2004-06-01 06:04:46 +0000236 if (ngx_add_conn) {
237 if (rc == -1) {
Igor Sysoevaa828612005-02-09 14:31:07 +0000238
Igor Sysoev67f450d2004-06-01 06:04:46 +0000239 /* NGX_EINPROGRESS */
Igor Sysoevaa828612005-02-09 14:31:07 +0000240
Igor Sysoev67f450d2004-06-01 06:04:46 +0000241 return NGX_AGAIN;
242 }
Igor Sysoev0e5dc5c2005-11-15 13:30:52 +0000243
Igor Sysoev67f450d2004-06-01 06:04:46 +0000244 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
Igor Sysoeve5a222c2005-01-25 12:27:35 +0000245
246 wev->ready = 1;
247
Igor Sysoev67f450d2004-06-01 06:04:46 +0000248 return NGX_OK;
249 }
250
Ruslan Ermilovd0755322015-04-22 18:57:32 +0300251 if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
Igor Sysoevb5faed22003-10-29 08:30:44 +0000252
Igor Sysoevc0edbcc2004-10-21 15:34:38 +0000253 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,
254 "connect(): %d", rc);
255
Igor Sysoevb5910d42003-10-30 16:51:33 +0000256 if (ngx_blocking(s) == -1) {
257 ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
258 ngx_blocking_n " failed");
Igor Sysoev72e92872009-11-02 15:24:02 +0000259 goto failed;
Igor Sysoevb5910d42003-10-30 16:51:33 +0000260 }
261
262 /*
Igor Sysoevaa828612005-02-09 14:31:07 +0000263 * FreeBSD's aio allows to post an operation on non-connected socket.
Igor Sysoevf2e676a2003-11-16 21:49:42 +0000264 * NT does not support it.
Igor Sysoev0e5dc5c2005-11-15 13:30:52 +0000265 *
Igor Sysoevfe0f5cc2003-10-31 16:05:33 +0000266 * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT
Igor Sysoevb5910d42003-10-30 16:51:33 +0000267 */
Igor Sysoev0e5dc5c2005-11-15 13:30:52 +0000268
Igor Sysoev68ee8f12003-10-30 08:51:06 +0000269 rev->ready = 1;
Igor Sysoevb5faed22003-10-29 08:30:44 +0000270 wev->ready = 1;
Igor Sysoev68ee8f12003-10-30 08:51:06 +0000271
Igor Sysoevb5faed22003-10-29 08:30:44 +0000272 return NGX_OK;
Igor Sysoevb5faed22003-10-29 08:30:44 +0000273 }
274
Igor Sysoevaa828612005-02-09 14:31:07 +0000275 if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
276
277 /* kqueue */
278
Igor Sysoeve6779222003-10-03 15:50:53 +0000279 event = NGX_CLEAR_EVENT;
280
Igor Sysoevaa828612005-02-09 14:31:07 +0000281 } else {
282
283 /* select, poll, /dev/poll */
284
Igor Sysoeve6779222003-10-03 15:50:53 +0000285 event = NGX_LEVEL_EVENT;
286 }
287
Igor Sysoev2b0c76c2003-10-27 21:01:00 +0000288 if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
Igor Sysoev72e92872009-11-02 15:24:02 +0000289 goto failed;
Igor Sysoev2b0c76c2003-10-27 21:01:00 +0000290 }
291
Igor Sysoeve6779222003-10-03 15:50:53 +0000292 if (rc == -1) {
293
294 /* NGX_EINPROGRESS */
295
296 if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
Igor Sysoev72e92872009-11-02 15:24:02 +0000297 goto failed;
Igor Sysoeve6779222003-10-03 15:50:53 +0000298 }
299
300 return NGX_AGAIN;
301 }
302
Igor Sysoev54498db2004-02-11 17:08:49 +0000303 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
Igor Sysoev222a2ad2003-11-18 16:49:00 +0000304
Igor Sysoevd404c972003-10-16 20:19:16 +0000305 wev->ready = 1;
306
Igor Sysoev87a01ea2003-10-02 05:39:37 +0000307 return NGX_OK;
Igor Sysoev72e92872009-11-02 15:24:02 +0000308
309failed:
310
Maxim Dounin4a23bc52012-01-30 11:12:52 +0000311 ngx_close_connection(c);
312 pc->connection = NULL;
Igor Sysoev72e92872009-11-02 15:24:02 +0000313
314 return NGX_ERROR;
Igor Sysoev890fc962003-07-20 21:15:59 +0000315}
Igor Sysoevae5c59c2003-08-14 06:00:28 +0000316
317
Roman Arutyunyan055c4602015-12-18 19:05:27 +0300318#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
Igor Sysoev3d2fd182006-12-04 16:46:13 +0000406ngx_int_t
407ngx_event_get_peer(ngx_peer_connection_t *pc, void *data)
Igor Sysoevae5c59c2003-08-14 06:00:28 +0000408{
Igor Sysoev3d2fd182006-12-04 16:46:13 +0000409 return NGX_OK;
Igor Sysoevae5c59c2003-08-14 06:00:28 +0000410}