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