1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /*
13 * Copyright Joyent, Inc. and other Node contributors. All rights reserved.
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this software and associated documentation files (the "Software"), to
17 * deal in the Software without restriction, including without limitation the
18 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
19 * sell copies of the Software, and to permit persons to whom the Software is
20 * furnished to do so, subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
31 * IN THE SOFTWARE.
32 */
33
34 #include <ctype.h>
35 #include <limits.h>
36 #include <stddef.h>
37 #include <string.h>
38
39 #include <isc/url.h>
40 #include <isc/util.h>
41
42 #ifndef BIT_AT
43 #define BIT_AT(a, i) \
44 (!!((unsigned int)(a)[(unsigned int)(i) >> 3] & \
45 (1 << ((unsigned int)(i)&7))))
46 #endif
47
48 #if HTTP_PARSER_STRICT
49 #define T(v) 0
50 #else
51 #define T(v) v
52 #endif
53
54 static const uint8_t normal_url_char[32] = {
55 /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
56 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
57 /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
58 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
59 /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
60 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
61 /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
62 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
63 /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
64 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
65 /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
66 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
67 /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
68 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
69 /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
70 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
71 /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
72 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
73 /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
74 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
75 /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
76 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
77 /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
78 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
79 /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
80 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
81 /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
82 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
83 /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
84 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
85 /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
86 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
87 };
88
89 #undef T
90
91 typedef enum {
92 s_dead = 1, /* important that this is > 0 */
93
94 s_start_req_or_res,
95 s_res_or_resp_H,
96 s_start_res,
97 s_res_H,
98 s_res_HT,
99 s_res_HTT,
100 s_res_HTTP,
101 s_res_http_major,
102 s_res_http_dot,
103 s_res_http_minor,
104 s_res_http_end,
105 s_res_first_status_code,
106 s_res_status_code,
107 s_res_status_start,
108 s_res_status,
109 s_res_line_almost_done,
110
111 s_start_req,
112
113 s_req_method,
114 s_req_spaces_before_url,
115 s_req_schema,
116 s_req_schema_slash,
117 s_req_schema_slash_slash,
118 s_req_server_start,
119 s_req_server,
120 s_req_server_with_at,
121 s_req_path,
122 s_req_query_string_start,
123 s_req_query_string,
124 s_req_fragment_start,
125 s_req_fragment,
126 s_req_http_start,
127 s_req_http_H,
128 s_req_http_HT,
129 s_req_http_HTT,
130 s_req_http_HTTP,
131 s_req_http_I,
132 s_req_http_IC,
133 s_req_http_major,
134 s_req_http_dot,
135 s_req_http_minor,
136 s_req_http_end,
137 s_req_line_almost_done,
138
139 s_header_field_start,
140 s_header_field,
141 s_header_value_discard_ws,
142 s_header_value_discard_ws_almost_done,
143 s_header_value_discard_lws,
144 s_header_value_start,
145 s_header_value,
146 s_header_value_lws,
147
148 s_header_almost_done,
149
150 s_chunk_size_start,
151 s_chunk_size,
152 s_chunk_parameters,
153 s_chunk_size_almost_done,
154
155 s_headers_almost_done,
156 s_headers_done,
157
158 /*
159 * Important: 's_headers_done' must be the last 'header' state. All
160 * states beyond this must be 'body' states. It is used for overflow
161 * checking. See the PARSING_HEADER() macro.
162 */
163
164 s_chunk_data,
165 s_chunk_data_almost_done,
166 s_chunk_data_done,
167
168 s_body_identity,
169 s_body_identity_eof,
170
171 s_message_done
172 } state_t;
173
174 typedef enum {
175 s_http_host_dead = 1,
176 s_http_userinfo_start,
177 s_http_userinfo,
178 s_http_host_start,
179 s_http_host_v6_start,
180 s_http_host,
181 s_http_host_v6,
182 s_http_host_v6_end,
183 s_http_host_v6_zone_start,
184 s_http_host_v6_zone,
185 s_http_host_port_start,
186 s_http_host_port
187 } host_state_t;
188
189 /* Macros for character classes; depends on strict-mode */
190 #define IS_MARK(c) \
191 ((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || \
192 (c) == '*' || (c) == '\'' || (c) == '(' || (c) == ')')
193 #define IS_USERINFO_CHAR(c) \
194 (isalnum((unsigned char)c) || IS_MARK(c) || (c) == '%' || \
195 (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
196 (c) == '$' || (c) == ',')
197
198 #if HTTP_PARSER_STRICT
199 #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
200 #define IS_HOST_CHAR(c) (isalnum((unsigned char)c) || (c) == '.' || (c) == '-')
201 #else
202 #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c) || ((c)&0x80))
203 #define IS_HOST_CHAR(c) \
204 (isalnum((unsigned char)c) || (c) == '.' || (c) == '-' || (c) == '_')
205 #endif
206
207 /*
208 * Our URL parser.
209 *
210 * This is designed to be shared by http_parser_execute() for URL validation,
211 * hence it has a state transition + byte-for-byte interface. In addition, it
212 * is meant to be embedded in http_parser_parse_url(), which does the dirty
213 * work of turning state transitions URL components for its API.
214 *
215 * This function should only be invoked with non-space characters. It is
216 * assumed that the caller cares about (and can detect) the transition between
217 * URL and non-URL states by looking for these.
218 */
219 static state_t
parse_url_char(state_t s,const char ch)220 parse_url_char(state_t s, const char ch) {
221 if (ch == ' ' || ch == '\r' || ch == '\n') {
222 return (s_dead);
223 }
224
225 #if HTTP_PARSER_STRICT
226 if (ch == '\t' || ch == '\f') {
227 return (s_dead);
228 }
229 #endif
230
231 switch (s) {
232 case s_req_spaces_before_url:
233 /* Proxied requests are followed by scheme of an absolute URI
234 * (alpha). All methods except CONNECT are followed by '/' or
235 * '*'.
236 */
237
238 if (ch == '/' || ch == '*') {
239 return (s_req_path);
240 }
241
242 if (isalpha((unsigned char)ch)) {
243 return (s_req_schema);
244 }
245
246 break;
247
248 case s_req_schema:
249 if (isalpha((unsigned char)ch)) {
250 return (s);
251 }
252
253 if (ch == ':') {
254 return (s_req_schema_slash);
255 }
256
257 break;
258
259 case s_req_schema_slash:
260 if (ch == '/') {
261 return (s_req_schema_slash_slash);
262 }
263
264 break;
265
266 case s_req_schema_slash_slash:
267 if (ch == '/') {
268 return (s_req_server_start);
269 }
270
271 break;
272
273 case s_req_server_with_at:
274 if (ch == '@') {
275 return (s_dead);
276 }
277
278 /* FALLTHROUGH */
279 case s_req_server_start:
280 case s_req_server:
281 if (ch == '/') {
282 return (s_req_path);
283 }
284
285 if (ch == '?') {
286 return (s_req_query_string_start);
287 }
288
289 if (ch == '@') {
290 return (s_req_server_with_at);
291 }
292
293 if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
294 return (s_req_server);
295 }
296
297 break;
298
299 case s_req_path:
300 if (IS_URL_CHAR(ch)) {
301 return (s);
302 }
303
304 switch (ch) {
305 case '?':
306 return (s_req_query_string_start);
307
308 case '#':
309 return (s_req_fragment_start);
310 }
311
312 break;
313
314 case s_req_query_string_start:
315 case s_req_query_string:
316 if (IS_URL_CHAR(ch)) {
317 return (s_req_query_string);
318 }
319
320 switch (ch) {
321 case '?':
322 /* allow extra '?' in query string */
323 return (s_req_query_string);
324
325 case '#':
326 return (s_req_fragment_start);
327 }
328
329 break;
330
331 case s_req_fragment_start:
332 if (IS_URL_CHAR(ch)) {
333 return (s_req_fragment);
334 }
335
336 switch (ch) {
337 case '?':
338 return (s_req_fragment);
339
340 case '#':
341 return (s);
342 }
343
344 break;
345
346 case s_req_fragment:
347 if (IS_URL_CHAR(ch)) {
348 return (s);
349 }
350
351 switch (ch) {
352 case '?':
353 case '#':
354 return (s);
355 }
356
357 break;
358
359 default:
360 break;
361 }
362
363 /*
364 * We should never fall out of the switch above unless there's an
365 * error.
366 */
367 return (s_dead);
368 }
369
370 static host_state_t
http_parse_host_char(host_state_t s,const char ch)371 http_parse_host_char(host_state_t s, const char ch) {
372 switch (s) {
373 case s_http_userinfo:
374 case s_http_userinfo_start:
375 if (ch == '@') {
376 return (s_http_host_start);
377 }
378
379 if (IS_USERINFO_CHAR(ch)) {
380 return (s_http_userinfo);
381 }
382 break;
383
384 case s_http_host_start:
385 if (ch == '[') {
386 return (s_http_host_v6_start);
387 }
388
389 if (IS_HOST_CHAR(ch)) {
390 return (s_http_host);
391 }
392
393 break;
394
395 case s_http_host:
396 if (IS_HOST_CHAR(ch)) {
397 return (s_http_host);
398 }
399
400 /* FALLTHROUGH */
401 case s_http_host_v6_end:
402 if (ch == ':') {
403 return (s_http_host_port_start);
404 }
405
406 break;
407
408 case s_http_host_v6:
409 if (ch == ']') {
410 return (s_http_host_v6_end);
411 }
412
413 /* FALLTHROUGH */
414 case s_http_host_v6_start:
415 if (isxdigit((unsigned char)ch) || ch == ':' || ch == '.') {
416 return (s_http_host_v6);
417 }
418
419 if (s == s_http_host_v6 && ch == '%') {
420 return (s_http_host_v6_zone_start);
421 }
422 break;
423
424 case s_http_host_v6_zone:
425 if (ch == ']') {
426 return (s_http_host_v6_end);
427 }
428
429 /* FALLTHROUGH */
430 case s_http_host_v6_zone_start:
431 /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
432 if (isalnum((unsigned char)ch) || ch == '%' || ch == '.' ||
433 ch == '-' || ch == '_' || ch == '~')
434 {
435 return (s_http_host_v6_zone);
436 }
437 break;
438
439 case s_http_host_port:
440 case s_http_host_port_start:
441 if (isdigit((unsigned char)ch)) {
442 return (s_http_host_port);
443 }
444
445 break;
446
447 default:
448 break;
449 }
450
451 return (s_http_host_dead);
452 }
453
454 static isc_result_t
http_parse_host(const char * buf,isc_url_parser_t * up,int found_at)455 http_parse_host(const char *buf, isc_url_parser_t *up, int found_at) {
456 host_state_t s;
457 const char *p = NULL;
458 size_t buflen = up->field_data[ISC_UF_HOST].off +
459 up->field_data[ISC_UF_HOST].len;
460
461 REQUIRE((up->field_set & (1 << ISC_UF_HOST)) != 0);
462
463 up->field_data[ISC_UF_HOST].len = 0;
464
465 s = found_at ? s_http_userinfo_start : s_http_host_start;
466
467 for (p = buf + up->field_data[ISC_UF_HOST].off; p < buf + buflen; p++) {
468 host_state_t new_s = http_parse_host_char(s, *p);
469
470 if (new_s == s_http_host_dead) {
471 return (ISC_R_FAILURE);
472 }
473
474 switch (new_s) {
475 case s_http_host:
476 if (s != s_http_host) {
477 up->field_data[ISC_UF_HOST].off =
478 (uint16_t)(p - buf);
479 }
480 up->field_data[ISC_UF_HOST].len++;
481 break;
482
483 case s_http_host_v6:
484 if (s != s_http_host_v6) {
485 up->field_data[ISC_UF_HOST].off =
486 (uint16_t)(p - buf);
487 }
488 up->field_data[ISC_UF_HOST].len++;
489 break;
490
491 case s_http_host_v6_zone_start:
492 case s_http_host_v6_zone:
493 up->field_data[ISC_UF_HOST].len++;
494 break;
495
496 case s_http_host_port:
497 if (s != s_http_host_port) {
498 up->field_data[ISC_UF_PORT].off =
499 (uint16_t)(p - buf);
500 up->field_data[ISC_UF_PORT].len = 0;
501 up->field_set |= (1 << ISC_UF_PORT);
502 }
503 up->field_data[ISC_UF_PORT].len++;
504 break;
505
506 case s_http_userinfo:
507 if (s != s_http_userinfo) {
508 up->field_data[ISC_UF_USERINFO].off =
509 (uint16_t)(p - buf);
510 up->field_data[ISC_UF_USERINFO].len = 0;
511 up->field_set |= (1 << ISC_UF_USERINFO);
512 }
513 up->field_data[ISC_UF_USERINFO].len++;
514 break;
515
516 default:
517 break;
518 }
519
520 s = new_s;
521 }
522
523 /* Make sure we don't end somewhere unexpected */
524 switch (s) {
525 case s_http_host_start:
526 case s_http_host_v6_start:
527 case s_http_host_v6:
528 case s_http_host_v6_zone_start:
529 case s_http_host_v6_zone:
530 case s_http_host_port_start:
531 case s_http_userinfo:
532 case s_http_userinfo_start:
533 return (ISC_R_FAILURE);
534 default:
535 break;
536 }
537
538 return (ISC_R_SUCCESS);
539 }
540
541 isc_result_t
isc_url_parse(const char * buf,size_t buflen,bool is_connect,isc_url_parser_t * up)542 isc_url_parse(const char *buf, size_t buflen, bool is_connect,
543 isc_url_parser_t *up) {
544 state_t s;
545 isc_url_field_t uf, old_uf;
546 int found_at = 0;
547 const char *p = NULL;
548
549 if (buflen == 0) {
550 return (ISC_R_FAILURE);
551 }
552
553 up->port = up->field_set = 0;
554 s = is_connect ? s_req_server_start : s_req_spaces_before_url;
555 old_uf = ISC_UF_MAX;
556
557 for (p = buf; p < buf + buflen; p++) {
558 s = parse_url_char(s, *p);
559
560 /* Figure out the next field that we're operating on */
561 switch (s) {
562 case s_dead:
563 return (ISC_R_FAILURE);
564
565 /* Skip delimiters */
566 case s_req_schema_slash:
567 case s_req_schema_slash_slash:
568 case s_req_server_start:
569 case s_req_query_string_start:
570 case s_req_fragment_start:
571 continue;
572
573 case s_req_schema:
574 uf = ISC_UF_SCHEMA;
575 break;
576
577 case s_req_server_with_at:
578 found_at = 1;
579 /* FALLTHROUGH */
580 case s_req_server:
581 uf = ISC_UF_HOST;
582 break;
583
584 case s_req_path:
585 uf = ISC_UF_PATH;
586 break;
587
588 case s_req_query_string:
589 uf = ISC_UF_QUERY;
590 break;
591
592 case s_req_fragment:
593 uf = ISC_UF_FRAGMENT;
594 break;
595
596 default:
597 INSIST(0);
598 ISC_UNREACHABLE();
599 }
600
601 /* Nothing's changed; soldier on */
602 if (uf == old_uf) {
603 up->field_data[uf].len++;
604 continue;
605 }
606
607 up->field_data[uf].off = (uint16_t)(p - buf);
608 up->field_data[uf].len = 1;
609
610 up->field_set |= (1 << uf);
611 old_uf = uf;
612 }
613
614 /* host must be present if there is a schema */
615 /* parsing http:///toto will fail */
616 if ((up->field_set & (1 << ISC_UF_SCHEMA)) &&
617 (up->field_set & (1 << ISC_UF_HOST)) == 0)
618 {
619 return (ISC_R_FAILURE);
620 }
621
622 if (up->field_set & (1 << ISC_UF_HOST)) {
623 isc_result_t result;
624
625 result = http_parse_host(buf, up, found_at);
626 if (result != ISC_R_SUCCESS) {
627 return (result);
628 }
629 }
630
631 /* CONNECT requests can only contain "hostname:port" */
632 if (is_connect &&
633 up->field_set != ((1 << ISC_UF_HOST) | (1 << ISC_UF_PORT))) {
634 return (ISC_R_FAILURE);
635 }
636
637 if (up->field_set & (1 << ISC_UF_PORT)) {
638 uint16_t off;
639 uint16_t len;
640 const char *pp = NULL;
641 const char *end = NULL;
642 unsigned long v;
643
644 off = up->field_data[ISC_UF_PORT].off;
645 len = up->field_data[ISC_UF_PORT].len;
646 end = buf + off + len;
647
648 /*
649 * NOTE: The characters are already validated and are in the
650 * [0-9] range
651 */
652 INSIST(off + len <= buflen);
653
654 v = 0;
655 for (pp = buf + off; pp < end; pp++) {
656 v *= 10;
657 v += *pp - '0';
658
659 /* Ports have a max value of 2^16 */
660 if (v > 0xffff) {
661 return (ISC_R_RANGE);
662 }
663 }
664
665 up->port = (uint16_t)v;
666 }
667
668 return (ISC_R_SUCCESS);
669 }
670