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