Mercurial > hg > nginx
annotate src/stream/ngx_stream_handler.c @ 6272:b6a665bf858a
HTTP/2: fix indirect reprioritization.
Previously, streams that were indirectly reprioritized (either because of
a new exclusive dependency on their parent or because of removal of their
parent from the dependency tree), didn't have their pointer to the parent
node updated.
This broke detection of circular dependencies and, as a result, nginx
worker would crash due to stack overflow whenever such dependency was
introduced.
Found with afl-fuzz.
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
author | Piotr Sikora <piotrsikora@google.com> |
---|---|
date | Thu, 01 Oct 2015 20:25:55 -0700 |
parents | d1f94042c29c |
children | 8f038068f4bc |
rev | line source |
---|---|
6115 | 1 |
2 /* | |
3 * Copyright (C) Roman Arutyunyan | |
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_stream.h> | |
12 | |
13 | |
14 static u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len); | |
15 static void ngx_stream_init_session(ngx_connection_t *c); | |
16 | |
17 #if (NGX_STREAM_SSL) | |
18 static void ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); | |
19 static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c); | |
20 #endif | |
21 | |
22 | |
23 void | |
24 ngx_stream_init_connection(ngx_connection_t *c) | |
25 { | |
6221
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
26 int tcp_nodelay; |
6175 | 27 u_char text[NGX_SOCKADDR_STRLEN]; |
28 size_t len; | |
29 ngx_int_t rc; | |
30 ngx_uint_t i; | |
31 struct sockaddr *sa; | |
32 ngx_stream_port_t *port; | |
33 struct sockaddr_in *sin; | |
34 ngx_stream_in_addr_t *addr; | |
35 ngx_stream_session_t *s; | |
36 ngx_stream_addr_conf_t *addr_conf; | |
6115 | 37 #if (NGX_HAVE_INET6) |
6175 | 38 struct sockaddr_in6 *sin6; |
39 ngx_stream_in6_addr_t *addr6; | |
6115 | 40 #endif |
6175 | 41 ngx_stream_core_srv_conf_t *cscf; |
42 ngx_stream_core_main_conf_t *cmcf; | |
6115 | 43 |
44 /* find the server configuration for the address:port */ | |
45 | |
46 port = c->listening->servers; | |
47 | |
48 if (port->naddrs > 1) { | |
49 | |
50 /* | |
51 * There are several addresses on this port and one of them | |
52 * is the "*:port" wildcard so getsockname() is needed to determine | |
53 * the server address. | |
54 * | |
55 * AcceptEx() already gave this address. | |
56 */ | |
57 | |
58 if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { | |
59 ngx_stream_close_connection(c); | |
60 return; | |
61 } | |
62 | |
63 sa = c->local_sockaddr; | |
64 | |
65 switch (sa->sa_family) { | |
66 | |
67 #if (NGX_HAVE_INET6) | |
68 case AF_INET6: | |
69 sin6 = (struct sockaddr_in6 *) sa; | |
70 | |
71 addr6 = port->addrs; | |
72 | |
73 /* the last address is "*" */ | |
74 | |
75 for (i = 0; i < port->naddrs - 1; i++) { | |
76 if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { | |
77 break; | |
78 } | |
79 } | |
80 | |
81 addr_conf = &addr6[i].conf; | |
82 | |
83 break; | |
84 #endif | |
85 | |
86 default: /* AF_INET */ | |
87 sin = (struct sockaddr_in *) sa; | |
88 | |
89 addr = port->addrs; | |
90 | |
91 /* the last address is "*" */ | |
92 | |
93 for (i = 0; i < port->naddrs - 1; i++) { | |
94 if (addr[i].addr == sin->sin_addr.s_addr) { | |
95 break; | |
96 } | |
97 } | |
98 | |
99 addr_conf = &addr[i].conf; | |
100 | |
101 break; | |
102 } | |
103 | |
104 } else { | |
105 switch (c->local_sockaddr->sa_family) { | |
106 | |
107 #if (NGX_HAVE_INET6) | |
108 case AF_INET6: | |
109 addr6 = port->addrs; | |
110 addr_conf = &addr6[0].conf; | |
111 break; | |
112 #endif | |
113 | |
114 default: /* AF_INET */ | |
115 addr = port->addrs; | |
116 addr_conf = &addr[0].conf; | |
117 break; | |
118 } | |
119 } | |
120 | |
121 s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t)); | |
122 if (s == NULL) { | |
123 ngx_stream_close_connection(c); | |
124 return; | |
125 } | |
126 | |
127 s->signature = NGX_STREAM_MODULE; | |
128 s->main_conf = addr_conf->ctx->main_conf; | |
129 s->srv_conf = addr_conf->ctx->srv_conf; | |
130 | |
131 s->connection = c; | |
132 c->data = s; | |
133 | |
134 cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); | |
135 | |
6129
187aa751ad62
Core: the ngx_set_connection_log() macro.
Vladimir Homutov <vl@nginx.com>
parents:
6115
diff
changeset
|
136 ngx_set_connection_log(c, cscf->error_log); |
6115 | 137 |
138 len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); | |
139 | |
140 ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V", | |
141 c->number, len, text, &addr_conf->addr_text); | |
142 | |
143 c->log->connection = c->number; | |
144 c->log->handler = ngx_stream_log_error; | |
145 c->log->data = s; | |
146 c->log->action = "initializing connection"; | |
147 c->log_error = NGX_ERROR_INFO; | |
148 | |
6175 | 149 cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); |
150 | |
6197
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
151 if (cmcf->limit_conn_handler) { |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
152 rc = cmcf->limit_conn_handler(s); |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
153 |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
154 if (rc != NGX_DECLINED) { |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
155 ngx_stream_close_connection(c); |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
156 return; |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
157 } |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
158 } |
0dcef374b8bb
Stream: connection limiting module.
Vladimir Homutov <vl@nginx.com>
parents:
6175
diff
changeset
|
159 |
6175 | 160 if (cmcf->access_handler) { |
161 rc = cmcf->access_handler(s); | |
162 | |
163 if (rc != NGX_OK && rc != NGX_DECLINED) { | |
164 ngx_stream_close_connection(c); | |
165 return; | |
166 } | |
167 } | |
168 | |
6221
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
169 if (cscf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
170 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "tcp_nodelay"); |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
171 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
172 tcp_nodelay = 1; |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
173 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
174 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
175 (const void *) &tcp_nodelay, sizeof(int)) == -1) |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
176 { |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
177 ngx_connection_error(c, ngx_socket_errno, |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
178 "setsockopt(TCP_NODELAY) failed"); |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
179 ngx_stream_close_connection(c); |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
180 return; |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
181 } |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
182 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
183 c->tcp_nodelay = NGX_TCP_NODELAY_SET; |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
184 } |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
185 |
7565e056fad6
Stream: the "tcp_nodelay" directive.
Vladimir Homutov <vl@nginx.com>
parents:
6197
diff
changeset
|
186 |
6115 | 187 #if (NGX_STREAM_SSL) |
188 { | |
189 ngx_stream_ssl_conf_t *sslcf; | |
190 | |
191 sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); | |
192 | |
193 if (addr_conf->ssl) { | |
194 c->log->action = "SSL handshaking"; | |
195 | |
196 if (sslcf->ssl.ctx == NULL) { | |
197 ngx_log_error(NGX_LOG_ERR, c->log, 0, | |
198 "no \"ssl_certificate\" is defined " | |
199 "in server listening on SSL port"); | |
200 ngx_stream_close_connection(c); | |
201 return; | |
202 } | |
203 | |
204 ngx_stream_ssl_init_connection(&sslcf->ssl, c); | |
205 return; | |
206 } | |
207 } | |
208 #endif | |
209 | |
210 ngx_stream_init_session(c); | |
211 } | |
212 | |
213 | |
214 static void | |
215 ngx_stream_init_session(ngx_connection_t *c) | |
216 { | |
217 ngx_stream_session_t *s; | |
218 ngx_stream_core_srv_conf_t *cscf; | |
219 | |
220 s = c->data; | |
221 c->log->action = "handling client connection"; | |
222 | |
223 cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); | |
224 | |
225 s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); | |
226 if (s->ctx == NULL) { | |
227 ngx_stream_close_connection(c); | |
228 return; | |
229 } | |
230 | |
231 cscf->handler(s); | |
232 } | |
233 | |
234 | |
235 #if (NGX_STREAM_SSL) | |
236 | |
237 static void | |
238 ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) | |
239 { | |
240 ngx_stream_session_t *s; | |
241 ngx_stream_ssl_conf_t *sslcf; | |
242 | |
243 if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) { | |
244 ngx_stream_close_connection(c); | |
245 return; | |
246 } | |
247 | |
248 if (ngx_ssl_handshake(c) == NGX_AGAIN) { | |
249 | |
250 s = c->data; | |
251 | |
252 sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); | |
253 | |
254 ngx_add_timer(c->read, sslcf->handshake_timeout); | |
255 | |
256 c->ssl->handler = ngx_stream_ssl_handshake_handler; | |
257 | |
258 return; | |
259 } | |
260 | |
261 ngx_stream_ssl_handshake_handler(c); | |
262 } | |
263 | |
264 | |
265 static void | |
266 ngx_stream_ssl_handshake_handler(ngx_connection_t *c) | |
267 { | |
268 if (!c->ssl->handshaked) { | |
269 ngx_stream_close_connection(c); | |
270 return; | |
271 } | |
272 | |
273 if (c->read->timer_set) { | |
274 ngx_del_timer(c->read); | |
275 } | |
276 | |
277 ngx_stream_init_session(c); | |
278 } | |
279 | |
280 #endif | |
281 | |
282 | |
283 void | |
284 ngx_stream_close_connection(ngx_connection_t *c) | |
285 { | |
286 ngx_pool_t *pool; | |
287 | |
288 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, | |
289 "close stream connection: %d", c->fd); | |
290 | |
291 #if (NGX_STREAM_SSL) | |
292 | |
293 if (c->ssl) { | |
294 if (ngx_ssl_shutdown(c) == NGX_AGAIN) { | |
295 c->ssl->handler = ngx_stream_close_connection; | |
296 return; | |
297 } | |
298 } | |
299 | |
300 #endif | |
301 | |
302 #if (NGX_STAT_STUB) | |
303 (void) ngx_atomic_fetch_add(ngx_stat_active, -1); | |
304 #endif | |
305 | |
306 pool = c->pool; | |
307 | |
308 ngx_close_connection(c); | |
309 | |
310 ngx_destroy_pool(pool); | |
311 } | |
312 | |
313 | |
314 static u_char * | |
315 ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len) | |
316 { | |
317 u_char *p; | |
318 ngx_stream_session_t *s; | |
319 | |
320 if (log->action) { | |
321 p = ngx_snprintf(buf, len, " while %s", log->action); | |
322 len -= p - buf; | |
323 buf = p; | |
324 } | |
325 | |
326 s = log->data; | |
327 | |
328 p = ngx_snprintf(buf, len, ", client: %V, server: %V", | |
329 &s->connection->addr_text, | |
330 &s->connection->listening->addr_text); | |
6223
d1f94042c29c
Stream: fixed potential error log buffer overrun.
Vladimir Homutov <vl@nginx.com>
parents:
6221
diff
changeset
|
331 len -= p - buf; |
d1f94042c29c
Stream: fixed potential error log buffer overrun.
Vladimir Homutov <vl@nginx.com>
parents:
6221
diff
changeset
|
332 buf = p; |
6115 | 333 |
334 if (s->log_handler) { | |
6223
d1f94042c29c
Stream: fixed potential error log buffer overrun.
Vladimir Homutov <vl@nginx.com>
parents:
6221
diff
changeset
|
335 p = s->log_handler(log, buf, len); |
6115 | 336 } |
337 | |
338 return p; | |
339 } |