1
2 /*
3 * Copyright (C) Axel Duch
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_main.h>
8 #include <nxt_http_route_addr.h>
9
10
11 #if (NXT_INET6)
12 static nxt_bool_t nxt_valid_ipv6_blocks(u_char *c, size_t len);
13 #endif
14
15
16 nxt_int_t
nxt_http_route_addr_pattern_parse(nxt_mp_t * mp,nxt_http_route_addr_pattern_t * pattern,nxt_conf_value_t * cv)17 nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
18 nxt_http_route_addr_pattern_t *pattern, nxt_conf_value_t *cv)
19 {
20 u_char *delim;
21 nxt_int_t ret, cidr_prefix;
22 nxt_str_t addr, port;
23 nxt_http_route_addr_base_t *base;
24 nxt_http_route_addr_range_t *inet;
25
26 if (nxt_conf_type(cv) != NXT_CONF_STRING) {
27 return NXT_ADDR_PATTERN_CV_TYPE_ERROR;
28 }
29
30 nxt_conf_get_string(cv, &addr);
31
32 base = &pattern->base;
33
34 if (addr.length > 0 && addr.start[0] == '!') {
35 addr.start++;
36 addr.length--;
37
38 base->negative = 1;
39
40 } else {
41 base->negative = 0;
42 }
43
44 if (nxt_slow_path(addr.length < 2)) {
45 return NXT_ADDR_PATTERN_LENGTH_ERROR;
46 }
47
48 nxt_str_null(&port);
49
50 if (addr.start[0] == '*' && addr.start[1] == ':') {
51 port.start = addr.start + 2;
52 port.length = addr.length - 2;
53 base->addr_family = AF_UNSPEC;
54 base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
55
56 goto parse_port;
57 }
58
59 if (nxt_inet6_probe(&addr)) {
60 #if (NXT_INET6)
61 u_char *end;
62 uint8_t i;
63 nxt_int_t len;
64 nxt_http_route_in6_addr_range_t *inet6;
65
66 base->addr_family = AF_INET6;
67
68 if (addr.start[0] == '[') {
69 addr.start++;
70 addr.length--;
71
72 end = addr.start + addr.length;
73
74 port.start = nxt_rmemstrn(addr.start, end, "]:", 2);
75 if (nxt_slow_path(port.start == NULL)) {
76 return NXT_ADDR_PATTERN_FORMAT_ERROR;
77 }
78
79 addr.length = port.start - addr.start;
80 port.start += nxt_length("]:");
81 port.length = end - port.start;
82 }
83
84 inet6 = &pattern->addr.v6;
85
86 delim = nxt_memchr(addr.start, '-', addr.length);
87 if (delim != NULL) {
88 len = delim - addr.start;
89 if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, len))) {
90 return NXT_ADDR_PATTERN_FORMAT_ERROR;
91 }
92
93 ret = nxt_inet6_addr(&inet6->start, addr.start, len);
94 if (nxt_slow_path(ret != NXT_OK)) {
95 return NXT_ADDR_PATTERN_FORMAT_ERROR;
96 }
97
98 len = addr.start + addr.length - delim - 1;
99 if (nxt_slow_path(!nxt_valid_ipv6_blocks(delim + 1, len))) {
100 return NXT_ADDR_PATTERN_FORMAT_ERROR;
101 }
102
103 ret = nxt_inet6_addr(&inet6->end, delim + 1, len);
104 if (nxt_slow_path(ret != NXT_OK)) {
105 return NXT_ADDR_PATTERN_FORMAT_ERROR;
106 }
107
108 if (nxt_slow_path(nxt_memcmp(&inet6->start, &inet6->end,
109 sizeof(struct in6_addr)) > 0))
110 {
111 return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
112 }
113
114 base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE;
115
116 goto parse_port;
117 }
118
119 delim = nxt_memchr(addr.start, '/', addr.length);
120 if (delim != NULL) {
121 cidr_prefix = nxt_int_parse(delim + 1,
122 addr.start + addr.length - (delim + 1));
123 if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 128)) {
124 return NXT_ADDR_PATTERN_CIDR_ERROR;
125 }
126
127 addr.length = delim - addr.start;
128 if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start,
129 addr.length)))
130 {
131 return NXT_ADDR_PATTERN_FORMAT_ERROR;
132 }
133
134 ret = nxt_inet6_addr(&inet6->start, addr.start, addr.length);
135 if (nxt_slow_path(ret != NXT_OK)) {
136 return NXT_ADDR_PATTERN_FORMAT_ERROR;
137 }
138
139 if (nxt_slow_path(cidr_prefix == 0)) {
140 base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
141
142 goto parse_port;
143 }
144
145 if (nxt_slow_path(cidr_prefix == 128)) {
146 base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
147
148 goto parse_port;
149 }
150
151 base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR;
152
153 for (i = 0; i < sizeof(struct in6_addr); i++) {
154 if (cidr_prefix >= 8) {
155 inet6->end.s6_addr[i] = 0xFF;
156 cidr_prefix -= 8;
157
158 continue;
159 }
160
161 if (cidr_prefix > 0) {
162 inet6->end.s6_addr[i] = 0xFF & (0xFF << (8 - cidr_prefix));
163 inet6->start.s6_addr[i] &= inet6->end.s6_addr[i];
164 cidr_prefix = 0;
165
166 continue;
167 }
168
169 inet6->start.s6_addr[i] = 0;
170 inet6->end.s6_addr[i] = 0;
171 }
172
173 goto parse_port;
174 }
175
176 base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
177
178 if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, addr.length))) {
179 return NXT_ADDR_PATTERN_FORMAT_ERROR;
180 }
181
182 ret = nxt_inet6_addr(&inet6->start, addr.start, addr.length);
183 if (nxt_slow_path(ret != NXT_OK)) {
184 return NXT_ADDR_PATTERN_FORMAT_ERROR;
185 }
186
187 goto parse_port;
188 #endif
189 return NXT_ADDR_PATTERN_NO_IPv6_ERROR;
190 }
191
192 base->addr_family = AF_INET;
193
194 delim = nxt_memchr(addr.start, ':', addr.length);
195 if (delim != NULL) {
196 port.start = delim + 1;
197 port.length = addr.start + addr.length - port.start;
198 addr.length = delim - addr.start;
199 }
200
201 inet = &pattern->addr.v4;
202
203 delim = nxt_memchr(addr.start, '-', addr.length);
204 if (delim != NULL) {
205 inet->start = nxt_inet_addr(addr.start, delim - addr.start);
206 if (nxt_slow_path(inet->start == INADDR_NONE)) {
207 return NXT_ADDR_PATTERN_FORMAT_ERROR;
208 }
209
210 inet->end = nxt_inet_addr(delim + 1,
211 addr.start + addr.length - (delim + 1));
212 if (nxt_slow_path(inet->end == INADDR_NONE)) {
213 return NXT_ADDR_PATTERN_FORMAT_ERROR;
214 }
215
216 if (nxt_slow_path(nxt_memcmp(&inet->start, &inet->end,
217 sizeof(struct in_addr)) > 0))
218 {
219 return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
220 }
221
222 base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE;
223
224 goto parse_port;
225 }
226
227 delim = nxt_memchr(addr.start, '/', addr.length);
228 if (delim != NULL) {
229 cidr_prefix = nxt_int_parse(delim + 1,
230 addr.start + addr.length - (delim + 1));
231 if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 32)) {
232 return NXT_ADDR_PATTERN_CIDR_ERROR;
233 }
234
235 addr.length = delim - addr.start;
236 inet->end = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - cidr_prefix)));
237
238 inet->start = nxt_inet_addr(addr.start, addr.length) & inet->end;
239 if (nxt_slow_path(inet->start == INADDR_NONE)) {
240 return NXT_ADDR_PATTERN_FORMAT_ERROR;
241 }
242
243 if (cidr_prefix == 0) {
244 base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
245
246 goto parse_port;
247 }
248
249 if (cidr_prefix < 32) {
250 base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR;
251
252 goto parse_port;
253 }
254 }
255
256 inet->start = nxt_inet_addr(addr.start, addr.length);
257 if (nxt_slow_path(inet->start == INADDR_NONE)) {
258 return NXT_ADDR_PATTERN_FORMAT_ERROR;
259 }
260
261 base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
262
263 parse_port:
264
265 if (port.length == 0) {
266 if (nxt_slow_path(port.start != NULL)) {
267 return NXT_ADDR_PATTERN_FORMAT_ERROR;
268 }
269
270 base->port.start = 0;
271 base->port.end = 65535;
272
273 return NXT_OK;
274 }
275
276 delim = nxt_memchr(port.start, '-', port.length - 1);
277 if (delim != NULL) {
278 ret = nxt_int_parse(port.start, delim - port.start);
279 if (nxt_slow_path(ret < 0 || ret > 65535)) {
280 return NXT_ADDR_PATTERN_PORT_ERROR;
281 }
282
283 base->port.start = ret;
284
285 ret = nxt_int_parse(delim + 1, port.start + port.length - (delim + 1));
286 if (nxt_slow_path(ret < base->port.start || ret > 65535)) {
287 return NXT_ADDR_PATTERN_PORT_ERROR;
288 }
289
290 base->port.end = ret;
291
292 } else {
293 ret = nxt_int_parse(port.start, port.length);
294 if (nxt_slow_path(ret < 0 || ret > 65535)) {
295 return NXT_ADDR_PATTERN_PORT_ERROR;
296 }
297
298 base->port.start = ret;
299 base->port.end = ret;
300 }
301
302 return NXT_OK;
303 }
304
305
306 #if (NXT_INET6)
307
308 static nxt_bool_t
nxt_valid_ipv6_blocks(u_char * c,size_t len)309 nxt_valid_ipv6_blocks(u_char *c, size_t len)
310 {
311 u_char *end;
312 nxt_uint_t colon_gap;
313
314 end = c + len;
315 colon_gap = 0;
316
317 while (c != end) {
318 if (*c == ':') {
319 colon_gap = 0;
320 c++;
321
322 continue;
323 }
324
325 colon_gap++;
326 c++;
327
328 if (nxt_slow_path(colon_gap > 4)) {
329 return 0;
330 }
331 }
332
333 return 1;
334 }
335
336 #endif
337