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