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