Mercurial > hg > nginx-quic
annotate src/http/modules/ngx_http_upstream_ip_hash_module.c @ 4664:356c91151658 stable-1.2
Merge of r4618: rewrite escaping fix (ticket #162).
The following code resulted in incorrect escaping of uri and possible
segfault:
location / {
rewrite ^(.*) $1?c=$1;
return 200 "$uri";
}
If there were arguments in a rewrite's replacement string, and length was
actually calculated (due to duplicate captures as in the example above,
or variables present), the is_args flag was set and incorrectly copied
after length calculation. This resulted in escaping applied to the uri part
of the replacement, resulting in incorrect escaping. Additionally, buffer
was allocated without escaping expected, thus this also resulted in buffer
overrun and possible segfault.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 04 Jun 2012 11:07:19 +0000 |
parents | d620f497c50f |
children | 382c523d253a 0141b4aec0e4 |
rev | line source |
---|---|
884 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4412 | 4 * Copyright (C) Nginx, Inc. |
884 | 5 */ |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_http.h> | |
11 | |
12 | |
13 typedef struct { | |
14 /* the round robin data must be first */ | |
15 ngx_http_upstream_rr_peer_data_t rrp; | |
16 | |
17 ngx_uint_t hash; | |
18 | |
19 u_char addr[3]; | |
20 | |
21 u_char tries; | |
22 | |
23 ngx_event_get_peer_pt get_rr_peer; | |
24 } ngx_http_upstream_ip_hash_peer_data_t; | |
25 | |
26 | |
27 static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r, | |
28 ngx_http_upstream_srv_conf_t *us); | |
29 static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, | |
30 void *data); | |
31 static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, | |
32 void *conf); | |
33 | |
34 | |
35 static ngx_command_t ngx_http_upstream_ip_hash_commands[] = { | |
36 | |
37 { ngx_string("ip_hash"), | |
38 NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS, | |
39 ngx_http_upstream_ip_hash, | |
40 0, | |
41 0, | |
42 NULL }, | |
43 | |
44 ngx_null_command | |
45 }; | |
46 | |
47 | |
48 static ngx_http_module_t ngx_http_upstream_ip_hash_module_ctx = { | |
49 NULL, /* preconfiguration */ | |
50 NULL, /* postconfiguration */ | |
51 | |
52 NULL, /* create main configuration */ | |
53 NULL, /* init main configuration */ | |
54 | |
55 NULL, /* create server configuration */ | |
56 NULL, /* merge server configuration */ | |
57 | |
58 NULL, /* create location configuration */ | |
59 NULL /* merge location configuration */ | |
60 }; | |
61 | |
62 | |
63 ngx_module_t ngx_http_upstream_ip_hash_module = { | |
64 NGX_MODULE_V1, | |
65 &ngx_http_upstream_ip_hash_module_ctx, /* module context */ | |
66 ngx_http_upstream_ip_hash_commands, /* module directives */ | |
67 NGX_HTTP_MODULE, /* module type */ | |
68 NULL, /* init master */ | |
69 NULL, /* init module */ | |
70 NULL, /* init process */ | |
71 NULL, /* init thread */ | |
72 NULL, /* exit thread */ | |
73 NULL, /* exit process */ | |
74 NULL, /* exit master */ | |
75 NGX_MODULE_V1_PADDING | |
76 }; | |
77 | |
78 | |
79 ngx_int_t | |
80 ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) | |
81 { | |
82 if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) { | |
83 return NGX_ERROR; | |
84 } | |
85 | |
86 us->peer.init = ngx_http_upstream_init_ip_hash_peer; | |
87 | |
88 return NGX_OK; | |
89 } | |
90 | |
91 | |
92 static ngx_int_t | |
93 ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r, | |
94 ngx_http_upstream_srv_conf_t *us) | |
95 { | |
1144
324a5a3fe27f
ip_hash used wrong byte order
Igor Sysoev <igor@sysoev.ru>
parents:
1129
diff
changeset
|
96 u_char *p; |
884 | 97 struct sockaddr_in *sin; |
98 ngx_http_upstream_ip_hash_peer_data_t *iphp; | |
99 | |
100 iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t)); | |
101 if (iphp == NULL) { | |
102 return NGX_ERROR; | |
103 } | |
104 | |
105 r->upstream->peer.data = &iphp->rrp; | |
106 | |
107 if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) { | |
108 return NGX_ERROR; | |
109 } | |
110 | |
111 r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer; | |
112 | |
113 /* AF_INET only */ | |
2512
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
114 |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
115 if (r->connection->sockaddr->sa_family == AF_INET) { |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
116 |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
117 sin = (struct sockaddr_in *) r->connection->sockaddr; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
118 p = (u_char *) &sin->sin_addr.s_addr; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
119 iphp->addr[0] = p[0]; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
120 iphp->addr[1] = p[1]; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
121 iphp->addr[2] = p[2]; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
122 |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
123 } else { |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
124 iphp->addr[0] = 0; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
125 iphp->addr[1] = 0; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
126 iphp->addr[2] = 0; |
2e91aecb9e57
a prelimiary IPv6 support, HTTP listen
Igor Sysoev <igor@sysoev.ru>
parents:
1418
diff
changeset
|
127 } |
884 | 128 |
129 iphp->hash = 89; | |
130 iphp->tries = 0; | |
131 iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer; | |
132 | |
133 return NGX_OK; | |
134 } | |
135 | |
136 | |
137 static ngx_int_t | |
138 ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data) | |
139 { | |
140 ngx_http_upstream_ip_hash_peer_data_t *iphp = data; | |
141 | |
142 time_t now; | |
143 uintptr_t m; | |
144 ngx_uint_t i, n, p, hash; | |
145 ngx_http_upstream_rr_peer_t *peer; | |
146 | |
147 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
148 "get ip hash peer, try: %ui", pc->tries); | |
149 | |
150 /* TODO: cached */ | |
151 | |
1418
acb1f441e7b2
update ip_hash to "backup" option
Igor Sysoev <igor@sysoev.ru>
parents:
1417
diff
changeset
|
152 if (iphp->tries > 20 || iphp->rrp.peers->single) { |
884 | 153 return iphp->get_rr_peer(pc, &iphp->rrp); |
154 } | |
155 | |
156 now = ngx_time(); | |
157 | |
158 pc->cached = 0; | |
159 pc->connection = NULL; | |
160 | |
161 hash = iphp->hash; | |
162 | |
163 for ( ;; ) { | |
164 | |
165 for (i = 0; i < 3; i++) { | |
166 hash = (hash * 113 + iphp->addr[i]) % 6271; | |
167 } | |
168 | |
169 p = hash % iphp->rrp.peers->number; | |
170 | |
171 n = p / (8 * sizeof(uintptr_t)); | |
1416
ad2311c943a3
fix ip_hash on 64-bit platform
Igor Sysoev <igor@sysoev.ru>
parents:
1284
diff
changeset
|
172 m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); |
884 | 173 |
174 if (!(iphp->rrp.tried[n] & m)) { | |
175 | |
176 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, | |
177 "get ip hash peer, hash: %ui %04XA", p, m); | |
178 | |
179 peer = &iphp->rrp.peers->peer[p]; | |
180 | |
181 /* ngx_lock_mutex(iphp->rrp.peers->mutex); */ | |
182 | |
183 if (!peer->down) { | |
184 | |
1025 | 185 if (peer->max_fails == 0 || peer->fails < peer->max_fails) { |
186 break; | |
187 } | |
884 | 188 |
4207
4fc91bae6f83
Better recheck of dead upstream servers.
Maxim Dounin <mdounin@mdounin.ru>
parents:
2512
diff
changeset
|
189 if (now - peer->checked > peer->fail_timeout) { |
4fc91bae6f83
Better recheck of dead upstream servers.
Maxim Dounin <mdounin@mdounin.ru>
parents:
2512
diff
changeset
|
190 peer->checked = now; |
1025 | 191 break; |
192 } | |
1129 | 193 } |
884 | 194 |
1129 | 195 iphp->rrp.tried[n] |= m; |
884 | 196 |
197 /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */ | |
198 | |
199 pc->tries--; | |
200 } | |
201 | |
202 if (++iphp->tries >= 20) { | |
203 return iphp->get_rr_peer(pc, &iphp->rrp); | |
204 } | |
205 } | |
206 | |
1417
b23a80f9a7b8
set current peer to use it in ngx_http_upstream_free_round_robin_peer()
Igor Sysoev <igor@sysoev.ru>
parents:
1416
diff
changeset
|
207 iphp->rrp.current = p; |
b23a80f9a7b8
set current peer to use it in ngx_http_upstream_free_round_robin_peer()
Igor Sysoev <igor@sysoev.ru>
parents:
1416
diff
changeset
|
208 |
884 | 209 pc->sockaddr = peer->sockaddr; |
210 pc->socklen = peer->socklen; | |
211 pc->name = &peer->name; | |
212 | |
213 /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */ | |
214 | |
215 iphp->rrp.tried[n] |= m; | |
216 iphp->hash = hash; | |
217 | |
218 return NGX_OK; | |
219 } | |
220 | |
221 | |
222 static char * | |
223 ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
224 { | |
225 ngx_http_upstream_srv_conf_t *uscf; | |
226 | |
227 uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); | |
228 | |
229 uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash; | |
230 | |
231 uscf->flags = NGX_HTTP_UPSTREAM_CREATE | |
232 |NGX_HTTP_UPSTREAM_MAX_FAILS | |
233 |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT | |
234 |NGX_HTTP_UPSTREAM_DOWN; | |
235 | |
236 return NGX_CONF_OK; | |
237 } |