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