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_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 = 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(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 = 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 = 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 = 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(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 = 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 = 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
nxt_valid_ipv6_blocks(u_char * c,size_t len)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