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