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