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