xref: /unit/src/nxt_http_route_addr.c (revision 1324:73562b05bf48)
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, *end;
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         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         nxt_inet6_addr(&inet6->start, addr.start, addr.length);
183 
184         goto parse_port;
185 #endif
186         return NXT_ADDR_PATTERN_NO_IPv6_ERROR;
187     }
188 
189     base->addr_family = AF_INET;
190 
191     delim = nxt_memchr(addr.start, ':', addr.length);
192     if (delim != NULL) {
193         port.start = delim + 1;
194         port.length = addr.start + addr.length - port.start;
195         addr.length = delim - addr.start;
196     }
197 
198     inet = &pattern->addr.v4;
199 
200     delim = nxt_memchr(addr.start, '-', addr.length);
201     if (delim != NULL) {
202         inet->start = nxt_inet_addr(addr.start, delim - addr.start);
203         if (nxt_slow_path(inet->start == INADDR_NONE)) {
204             return NXT_ADDR_PATTERN_FORMAT_ERROR;
205         }
206 
207         inet->end = nxt_inet_addr(delim + 1,
208                                   addr.start + addr.length - (delim + 1));
209         if (nxt_slow_path(inet->end == INADDR_NONE)) {
210             return NXT_ADDR_PATTERN_FORMAT_ERROR;
211         }
212 
213         if (nxt_slow_path(nxt_memcmp(&inet->start, &inet->end,
214                                      sizeof(struct in_addr)) > 0))
215         {
216             return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
217         }
218 
219         base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE;
220 
221         goto parse_port;
222     }
223 
224     delim = nxt_memchr(addr.start, '/', addr.length);
225     if (delim != NULL) {
226         cidr_prefix = nxt_int_parse(delim + 1,
227                                     addr.start + addr.length - (delim + 1));
228         if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 32)) {
229             return NXT_ADDR_PATTERN_CIDR_ERROR;
230         }
231 
232         addr.length = delim - addr.start;
233         inet->end = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - cidr_prefix)));
234 
235         inet->start = nxt_inet_addr(addr.start, addr.length) & inet->end;
236         if (nxt_slow_path(inet->start == INADDR_NONE)) {
237             return NXT_ADDR_PATTERN_FORMAT_ERROR;
238         }
239 
240         if (cidr_prefix == 0) {
241             base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
242 
243             goto parse_port;
244         }
245 
246         if (cidr_prefix < 32) {
247             base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR;
248 
249             goto parse_port;
250         }
251     }
252 
253     inet->start = nxt_inet_addr(addr.start, addr.length);
254     if (nxt_slow_path(inet->start == INADDR_NONE)) {
255         return NXT_ADDR_PATTERN_FORMAT_ERROR;
256     }
257 
258     base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
259 
260 parse_port:
261 
262     if (port.length == 0) {
263         if (nxt_slow_path(port.start != NULL)) {
264             return NXT_ADDR_PATTERN_FORMAT_ERROR;
265         }
266 
267         base->port.start = 0;
268         base->port.end = 65535;
269 
270         return NXT_OK;
271     }
272 
273     delim = nxt_memchr(port.start, '-', port.length - 1);
274     if (delim != NULL) {
275         ret = nxt_int_parse(port.start, delim - port.start);
276         if (nxt_slow_path(ret < 0 || ret > 65535)) {
277             return NXT_ADDR_PATTERN_PORT_ERROR;
278         }
279 
280         base->port.start = ret;
281 
282         ret = nxt_int_parse(delim + 1, port.start + port.length - (delim + 1));
283         if (nxt_slow_path(ret < base->port.start || ret > 65535)) {
284             return NXT_ADDR_PATTERN_PORT_ERROR;
285         }
286 
287         base->port.end = ret;
288 
289     } else {
290         ret = nxt_int_parse(port.start, port.length);
291         if (nxt_slow_path(ret < 0 || ret > 65535)) {
292             return NXT_ADDR_PATTERN_PORT_ERROR;
293         }
294 
295         base->port.start = ret;
296         base->port.end = ret;
297     }
298 
299     return NXT_OK;
300 }
301 
302 
303 static nxt_bool_t
304 nxt_str_looks_like_ipv6(const nxt_str_t *str)
305 {
306     u_char  *colon, *end;
307 
308     colon = nxt_memchr(str->start, ':', str->length);
309 
310     if (colon != NULL) {
311         end = str->start + str->length;
312         colon = nxt_memchr(colon + 1, ':', end - (colon + 1));
313     }
314 
315     return (colon != NULL);
316 }
317 
318 
319 #if (NXT_INET6)
320 
321 static nxt_bool_t
322 nxt_valid_ipv6_blocks(u_char *c, size_t len)
323 {
324     u_char      *end;
325     nxt_uint_t  colon_gap;
326 
327     end = c + len;
328     colon_gap = 0;
329 
330     while (c != end) {
331         if (*c == ':') {
332             colon_gap = 0;
333             c++;
334 
335             continue;
336         }
337 
338         colon_gap++;
339         c++;
340 
341         if (nxt_slow_path(colon_gap > 4)) {
342             return 0;
343         }
344     }
345 
346     return 1;
347 }
348 
349 #endif
350