summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ot_http.c34
-rw-r--r--src/ot_urlencoded.c12
-rw-r--r--src/ot_urlencoded.h4
-rw-r--r--tests/t_urlencoded.c66
4 files changed, 72 insertions, 44 deletions
diff --git a/src/ot_http.c b/src/ot_http.c
index 29f41a3..fc49333 100644
--- a/src/ot_http.c
+++ b/src/ot_http.c
@@ -313,7 +313,7 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
313#endif 313#endif
314 314
315 while (scanon) { 315 while (scanon) {
316 switch (urlencoded_match_keyword(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { 316 switch (urlencoded_match_keyword(keywords_main, &read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_PARAM)) {
317 case -2: 317 case -2:
318 scanon = 0; 318 scanon = 0;
319 break; /* TERMINATOR */ 319 break; /* TERMINATOR */
@@ -323,11 +323,11 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c
323 urlencoded_skip_value(&read_ptr); 323 urlencoded_skip_value(&read_ptr);
324 break; 324 break;
325 case 1: /* matched "mode" */ 325 case 1: /* matched "mode" */
326 if ((mode = urlencoded_match_keyword(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) 326 if ((mode = urlencoded_match_keyword(keywords_mode, &read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE)) <= 0)
327 HTTPERROR_400_PARAM; 327 HTTPERROR_400_PARAM;
328 break; 328 break;
329 case 2: /* matched "format" */ 329 case 2: /* matched "format" */
330 if ((format = urlencoded_match_keyword(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) 330 if ((format = urlencoded_match_keyword(keywords_format, &read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE)) <= 0)
331 HTTPERROR_400_PARAM; 331 HTTPERROR_400_PARAM;
332 break; 332 break;
333 case 3: 333 case 3:
@@ -505,7 +505,7 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws,
505 } 505 }
506 506
507 while (scanon) { 507 while (scanon) {
508 switch (urlencoded_match_keyword(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { 508 switch (urlencoded_match_keyword(keywords_scrape, &read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_PARAM)) {
509 case -2: 509 case -2:
510 scanon = 0; 510 scanon = 0;
511 break; /* TERMINATOR */ 511 break; /* TERMINATOR */
@@ -515,10 +515,12 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws,
515 urlencoded_skip_value(&read_ptr); 515 urlencoded_skip_value(&read_ptr);
516 break; 516 break;
517 case 1: /* matched "info_hash" */ 517 case 1: /* matched "info_hash" */
518 {
518 /* ignore this, when we have less than 20 bytes */ 519 /* ignore this, when we have less than 20 bytes */
519 if (urlencoded_parse(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash)) 520 ot_hash *slot = multiscrape_buf + numwant++;
521 if (urlencoded_parse(&read_ptr, (char *)slot, (char *)(slot + 1), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash))
520 HTTPERROR_400_PARAM; 522 HTTPERROR_400_PARAM;
521 break; 523 } break;
522 } 524 }
523 } 525 }
524 526
@@ -622,7 +624,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
622 scanon = 1; 624 scanon = 1;
623 625
624 while (scanon) { 626 while (scanon) {
625 switch (urlencoded_match_keyword(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { 627 switch (urlencoded_match_keyword(keywords_announce, &read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_PARAM)) {
626 case -2: 628 case -2:
627 scanon = 0; 629 scanon = 0;
628 break; /* TERMINATOR */ 630 break; /* TERMINATOR */
@@ -632,14 +634,14 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
632 urlencoded_skip_value(&read_ptr); 634 urlencoded_skip_value(&read_ptr);
633 break; 635 break;
634 case 1: /* matched "port" */ 636 case 1: /* matched "port" */
635 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 637 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE);
636 if ((len <= 0) || scan_int(write_ptr, len, &tmp) || (tmp > 0xffff)) 638 if ((len <= 0) || scan_int(write_ptr, len, &tmp) || (tmp > 0xffff))
637 HTTPERROR_400_PARAM; 639 HTTPERROR_400_PARAM;
638 port = htons((uint16_t)tmp); 640 port = htons((uint16_t)tmp);
639 OT_SETPORT(&ws->peer, &port); 641 OT_SETPORT(&ws->peer, &port);
640 break; 642 break;
641 case 2: /* matched "left" */ 643 case 2: /* matched "left" */
642 if ((len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) 644 if ((len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE)) <= 0)
643 HTTPERROR_400_PARAM; 645 HTTPERROR_400_PARAM;
644 if (scan_int(write_ptr, len, &tmp)) 646 if (scan_int(write_ptr, len, &tmp))
645 tmp = 0; 647 tmp = 0;
@@ -647,7 +649,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
647 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; 649 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING;
648 break; 650 break;
649 case 3: /* matched "event" */ 651 case 3: /* matched "event" */
650 switch (urlencoded_match_keyword(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { 652 switch (urlencoded_match_keyword(keywords_announce_event, &read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE)) {
651 case -1: 653 case -1:
652 HTTPERROR_400_PARAM; 654 HTTPERROR_400_PARAM;
653 case 1: /* matched "completed" */ 655 case 1: /* matched "completed" */
@@ -661,7 +663,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
661 } 663 }
662 break; 664 break;
663 case 4: /* matched "numwant" */ 665 case 4: /* matched "numwant" */
664 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 666 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE);
665 if ((len <= 0) || scan_int(write_ptr, len, &tmp)) 667 if ((len <= 0) || scan_int(write_ptr, len, &tmp))
666 HTTPERROR_400_PARAM; 668 HTTPERROR_400_PARAM;
667 if (tmp < 0) 669 if (tmp < 0)
@@ -671,7 +673,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
671 numwant = (size_t)tmp; 673 numwant = (size_t)tmp;
672 break; 674 break;
673 case 5: /* matched "compact" */ 675 case 5: /* matched "compact" */
674 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 676 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE);
675 if ((len <= 0) || scan_int(write_ptr, len, &tmp)) 677 if ((len <= 0) || scan_int(write_ptr, len, &tmp))
676 HTTPERROR_400_PARAM; 678 HTTPERROR_400_PARAM;
677 if (!tmp) 679 if (!tmp)
@@ -681,7 +683,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
681 if (ws->hash) 683 if (ws->hash)
682 HTTPERROR_400_DOUBLEHASH; 684 HTTPERROR_400_DOUBLEHASH;
683 /* ignore this, when we have less than 20 bytes */ 685 /* ignore this, when we have less than 20 bytes */
684 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) 686 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE) != 20)
685 HTTPERROR_400_PARAM; 687 HTTPERROR_400_PARAM;
686 ws->hash = (ot_hash *)write_ptr; 688 ws->hash = (ot_hash *)write_ptr;
687 break; 689 break;
@@ -689,7 +691,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
689 case 7: /* matched "ip" */ 691 case 7: /* matched "ip" */
690 { 692 {
691 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; 693 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
692 len = urlencoded_parse(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); 694 len = urlencoded_parse(&read_ptr, tmp_buf2, ws->outbuf + G_OUTBUF_SIZE, SCAN_SEARCHPATH_VALUE);
693 tmp_buf2[len] = 0; 695 tmp_buf2[len] = 0;
694 if ((len <= 0) || ip_parse(tmp_buf2, tmp_buf1) != (size_t)len) 696 if ((len <= 0) || ip_parse(tmp_buf2, tmp_buf1) != (size_t)len)
695 HTTPERROR_400_PARAM; 697 HTTPERROR_400_PARAM;
@@ -698,7 +700,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
698#endif 700#endif
699 case 8: /* matched "peer_id" */ 701 case 8: /* matched "peer_id" */
700 /* ignore this, when we have less than 20 bytes */ 702 /* ignore this, when we have less than 20 bytes */
701 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) 703 if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, ws->request + ws->request_size, SCAN_SEARCHPATH_VALUE) != 20)
702 HTTPERROR_400_PARAM; 704 HTTPERROR_400_PARAM;
703 ws->peer_id = write_ptr; 705 ws->peer_id = write_ptr;
704 break; 706 break;
@@ -772,7 +774,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) {
772 In reality we abandoned requiring the url to be correct. This now 774 In reality we abandoned requiring the url to be correct. This now
773 only decodes url encoded characters, we check for announces and 775 only decodes url encoded characters, we check for announces and
774 scrapes by looking for "a*" or "sc" */ 776 scrapes by looking for "a*" or "sc" */
775 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_PATH); 777 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, ws->request + ws->request_size, SCAN_PATH);
776 778
777 /* If parsing returned an error, leave with not found */ 779 /* If parsing returned an error, leave with not found */
778 if (g_redirecturl && (len == -2)) 780 if (g_redirecturl && (len == -2))
diff --git a/src/ot_urlencoded.c b/src/ot_urlencoded.c
index 4109bb5..c977adb 100644
--- a/src/ot_urlencoded.c
+++ b/src/ot_urlencoded.c
@@ -68,17 +68,18 @@ void urlencoded_skip_value(char **string) {
68 *string = (char *)s; 68 *string = (char *)s;
69} 69}
70 70
71int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { 71int urlencoded_match_keyword(const ot_keywords *keywords, char **string, const char *deststring_end, SCAN_SEARCHPATH_FLAG flags) {
72 char *deststring = *string; 72 char *deststring = *string;
73 ssize_t match_length = urlencoded_parse(string, deststring, flags); 73 ssize_t match_length = urlencoded_parse(string, deststring, deststring_end, flags);
74 74
75 if (match_length < 0) 75 if (match_length < 0)
76 return match_length; 76 return match_length;
77 if (match_length == 0) 77 if (match_length == 0)
78 return -3; 78 return -3;
79 79
80 /* Compare on the exact decoded length. */
80 while (keywords->key) { 81 while (keywords->key) {
81 if (!strncmp(keywords->key, deststring, match_length) && !keywords->key[match_length]) 82 if ((size_t)match_length == strlen(keywords->key) && !memcmp(keywords->key, deststring, (size_t)match_length))
82 return keywords->value; 83 return keywords->value;
83 keywords++; 84 keywords++;
84 } 85 }
@@ -86,7 +87,7 @@ int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SE
86 return -3; 87 return -3;
87} 88}
88 89
89ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { 90ssize_t urlencoded_parse(char **string, char *deststring, const char *deststring_end, SCAN_SEARCHPATH_FLAG flags) {
90 const unsigned char *s = *(const unsigned char **)string; 91 const unsigned char *s = *(const unsigned char **)string;
91 unsigned char *d = (unsigned char *)deststring; 92 unsigned char *d = (unsigned char *)deststring;
92 unsigned char b, c; 93 unsigned char b, c;
@@ -106,6 +107,9 @@ ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG f
106 c |= (b << 4); 107 c |= (b << 4);
107 } 108 }
108 109
110 if ((const char *)d >= deststring_end)
111 return -1;
112
109 /* Write (possibly decoded) character to output */ 113 /* Write (possibly decoded) character to output */
110 *d++ = c; 114 *d++ = c;
111 } 115 }
diff --git a/src/ot_urlencoded.h b/src/ot_urlencoded.h
index 9882468..053e079 100644
--- a/src/ot_urlencoded.h
+++ b/src/ot_urlencoded.h
@@ -28,7 +28,7 @@ typedef enum {
28 or -1 for parse error 28 or -1 for parse error
29 or -2 for terminator found 29 or -2 for terminator found
30*/ 30*/
31ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); 31ssize_t urlencoded_parse(char **string, char *deststring, const char *deststring_end, SCAN_SEARCHPATH_FLAG flags);
32 32
33/* string in: pointer to source 33/* string in: pointer to source
34 out: pointer to next scan position 34 out: pointer to next scan position
@@ -38,7 +38,7 @@ ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG f
38 or -2 for terminator found 38 or -2 for terminator found
39 or -3 for no keyword matched 39 or -3 for no keyword matched
40 */ 40 */
41int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags); 41int urlencoded_match_keyword(const ot_keywords *keywords, char **string, const char *deststring_end, SCAN_SEARCHPATH_FLAG flags);
42 42
43/* string in: pointer to value of a param=value pair to skip 43/* string in: pointer to value of a param=value pair to skip
44 out: pointer to next scan position on return 44 out: pointer to next scan position on return
diff --git a/tests/t_urlencoded.c b/tests/t_urlencoded.c
index 3b3e6c2..9935c1c 100644
--- a/tests/t_urlencoded.c
+++ b/tests/t_urlencoded.c
@@ -21,7 +21,7 @@ static void case_path(void) {
21 ssize_t n; 21 ssize_t n;
22 22
23 test_case("path stops at the question mark"); 23 test_case("path stops at the question mark");
24 n = urlencoded_parse(&p, dest, SCAN_PATH); 24 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_PATH);
25 CHECK_INT(n, 9); 25 CHECK_INT(n, 9);
26 dest[n] = 0; 26 dest[n] = 0;
27 CHECK_STR(dest, "/announce"); 27 CHECK_STR(dest, "/announce");
@@ -35,22 +35,22 @@ static void case_param_and_value(void) {
35 ssize_t n; 35 ssize_t n;
36 36
37 test_case("parameter stops at '=', value stops at '&'"); 37 test_case("parameter stops at '=', value stops at '&'");
38 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); 38 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_PARAM);
39 CHECK_INT(n, 9); 39 CHECK_INT(n, 9);
40 dest[n] = 0; 40 dest[n] = 0;
41 CHECK_STR(dest, "info_hash"); 41 CHECK_STR(dest, "info_hash");
42 42
43 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 43 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
44 CHECK_INT(n, 3); 44 CHECK_INT(n, 3);
45 dest[n] = 0; 45 dest[n] = 0;
46 CHECK_STR(dest, "abc"); 46 CHECK_STR(dest, "abc");
47 47
48 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); 48 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_PARAM);
49 CHECK_INT(n, 4); 49 CHECK_INT(n, 4);
50 dest[n] = 0; 50 dest[n] = 0;
51 CHECK_STR(dest, "port"); 51 CHECK_STR(dest, "port");
52 52
53 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 53 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
54 CHECK_INT(n, 2); 54 CHECK_INT(n, 2);
55 dest[n] = 0; 55 dest[n] = 0;
56 CHECK_STR(dest, "80"); 56 CHECK_STR(dest, "80");
@@ -63,7 +63,7 @@ static void case_percent_decoding(void) {
63 ssize_t n; 63 ssize_t n;
64 64
65 test_case("percent escapes decode"); 65 test_case("percent escapes decode");
66 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 66 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
67 CHECK_INT(n, 3); 67 CHECK_INT(n, 3);
68 CHECK_MEM(dest, "ABC", 3); 68 CHECK_MEM(dest, "ABC", 3);
69} 69}
@@ -77,7 +77,7 @@ static void case_binary_info_hash(void) {
77 ssize_t n; 77 ssize_t n;
78 78
79 test_case("twenty byte binary value survives decoding"); 79 test_case("twenty byte binary value survives decoding");
80 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 80 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
81 CHECK_INT(n, 20); 81 CHECK_INT(n, 20);
82 CHECK_MEM(dest, want, 20); 82 CHECK_MEM(dest, want, 20);
83} 83}
@@ -88,7 +88,7 @@ static void case_in_place(void) {
88 ssize_t n; 88 ssize_t n;
89 89
90 test_case("decoding in place over the source is safe"); 90 test_case("decoding in place over the source is safe");
91 n = urlencoded_parse(&p, query, SCAN_SEARCHPATH_VALUE); 91 n = urlencoded_parse(&p, query, query + sizeof(query), SCAN_SEARCHPATH_VALUE);
92 CHECK_INT(n, 2); 92 CHECK_INT(n, 2);
93 CHECK_MEM(query, "AB", 2); 93 CHECK_MEM(query, "AB", 2);
94} 94}
@@ -100,7 +100,7 @@ static void case_bad_escape(void) {
100 ssize_t n; 100 ssize_t n;
101 101
102 test_case("a non hexadecimal escape is a parse error"); 102 test_case("a non hexadecimal escape is a parse error");
103 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 103 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
104 CHECK_INT(n, -1); 104 CHECK_INT(n, -1);
105} 105}
106 106
@@ -111,7 +111,7 @@ static void case_truncated_escape(void) {
111 ssize_t n; 111 ssize_t n;
112 112
113 test_case("an escape cut short by the terminator is a parse error"); 113 test_case("an escape cut short by the terminator is a parse error");
114 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 114 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
115 CHECK_INT(n, -1); 115 CHECK_INT(n, -1);
116} 116}
117 117
@@ -123,11 +123,11 @@ static void case_terminator(void) {
123 ssize_t n; 123 ssize_t n;
124 124
125 test_case("a hard terminator before any output reports end of input"); 125 test_case("a hard terminator before any output reports end of input");
126 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); 126 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_PARAM);
127 CHECK_INT(n, -2); 127 CHECK_INT(n, -2);
128 128
129 p = space; 129 p = space;
130 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); 130 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_PARAM);
131 CHECK_INT(n, -2); 131 CHECK_INT(n, -2);
132} 132}
133 133
@@ -138,10 +138,10 @@ static void case_terminator_after_output(void) {
138 ssize_t n; 138 ssize_t n;
139 139
140 test_case("a hard terminator after output is left for the next call"); 140 test_case("a hard terminator after output is left for the next call");
141 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 141 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
142 CHECK_INT(n, 3); 142 CHECK_INT(n, 3);
143 CHECK_INT(*p, 0); 143 CHECK_INT(*p, 0);
144 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 144 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
145 CHECK_INT(n, -2); 145 CHECK_INT(n, -2);
146} 146}
147 147
@@ -157,14 +157,14 @@ static void case_separators_are_contextual(void) {
157 ssize_t n; 157 ssize_t n;
158 158
159 test_case("a question mark is literal inside a value"); 159 test_case("a question mark is literal inside a value");
160 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 160 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
161 CHECK_INT(n, 3); 161 CHECK_INT(n, 3);
162 dest[n] = 0; 162 dest[n] = 0;
163 CHECK_STR(dest, "a?b"); 163 CHECK_STR(dest, "a?b");
164 164
165 test_case("an equals sign is literal inside a value"); 165 test_case("an equals sign is literal inside a value");
166 p = equals; 166 p = equals;
167 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 167 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
168 CHECK_INT(n, 3); 168 CHECK_INT(n, 3);
169 dest[n] = 0; 169 dest[n] = 0;
170 CHECK_STR(dest, "a=b"); 170 CHECK_STR(dest, "a=b");
@@ -177,7 +177,7 @@ static void case_ampersand_in_path(void) {
177 ssize_t n; 177 ssize_t n;
178 178
179 test_case("an ampersand in a path is a parse error"); 179 test_case("an ampersand in a path is a parse error");
180 n = urlencoded_parse(&p, dest, SCAN_PATH); 180 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_PATH);
181 CHECK_INT(n, -1); 181 CHECK_INT(n, -1);
182} 182}
183 183
@@ -188,7 +188,7 @@ static void case_raw_high_byte(void) {
188 ssize_t n; 188 ssize_t n;
189 189
190 test_case("an undecoded byte above ASCII is a parse error"); 190 test_case("an undecoded byte above ASCII is a parse error");
191 n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); 191 n = urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE);
192 CHECK_INT(n, -1); 192 CHECK_INT(n, -1);
193} 193}
194 194
@@ -215,15 +215,15 @@ static void case_find_keywords(void) {
215 char *p = match; 215 char *p = match;
216 216
217 test_case("a known keyword returns its value"); 217 test_case("a known keyword returns its value");
218 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), 2); 218 CHECK_INT(urlencoded_match_keyword(keywords, &p, match + sizeof(match), SCAN_SEARCHPATH_PARAM), 2);
219 219
220 test_case("an unknown keyword returns -3"); 220 test_case("an unknown keyword returns -3");
221 p = nomatch; 221 p = nomatch;
222 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); 222 CHECK_INT(urlencoded_match_keyword(keywords, &p, nomatch + sizeof(nomatch), SCAN_SEARCHPATH_PARAM), -3);
223 223
224 test_case("a prefix of a keyword does not match it"); 224 test_case("a prefix of a keyword does not match it");
225 p = prefix; 225 p = prefix;
226 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); 226 CHECK_INT(urlencoded_match_keyword(keywords, &p, prefix + sizeof(prefix), SCAN_SEARCHPATH_PARAM), -3);
227} 227}
228 228
229/* The encoding direction. Which bytes survive unescaped is fixed by what 229/* The encoding direction. Which bytes survive unescaped is fixed by what
@@ -313,10 +313,30 @@ static void case_fmt_decoder_disagrees_on_dollar(void) {
313 dest[n] = 0; 313 dest[n] = 0;
314 CHECK_STR(dest, "a$b"); 314 CHECK_STR(dest, "a$b");
315 315
316 got = urlencoded_parse(&p, scanned, SCAN_SEARCHPATH_VALUE); 316 got = urlencoded_parse(&p, scanned, scanned + sizeof(scanned), SCAN_SEARCHPATH_VALUE);
317 CHECK_INT(got, -1); 317 CHECK_INT(got, -1);
318} 318}
319 319
320static void case_value_stops_at_dest_end(void) {
321 /* A value that decodes to more than the destination holds is a parse error,
322 not a write past the buffer. (ip= used to overflow ws->outbuf this way.) */
323 char query[] = "AAAAAAAAAA&x=1"; /* ten A's then a terminator */
324 char dest[4];
325 char *p = query;
326 test_case("a value longer than the destination returns -1");
327 CHECK_INT((int)urlencoded_parse(&p, dest, dest + sizeof(dest), SCAN_SEARCHPATH_VALUE), -1);
328}
329
330static void case_keyword_with_decoded_nul_does_not_match(void) {
331 /* A percent-encoded NUL used to make a value match a shorter keyword and
332 then index the keyword past its end. */
333 static const ot_keywords keywords[] = {{"port", 1}, {NULL, -3}};
334 char query[] = "port%00xxxx=1";
335 char *p = query;
336 test_case("a keyword followed by a decoded NUL does not match the keyword");
337 CHECK_INT(urlencoded_match_keyword(keywords, &p, query + sizeof(query), SCAN_SEARCHPATH_PARAM), -3);
338}
339
320void test_urlencoded(void) { 340void test_urlencoded(void) {
321 case_path(); 341 case_path();
322 case_param_and_value(); 342 case_param_and_value();
@@ -332,6 +352,8 @@ void test_urlencoded(void) {
332 case_raw_high_byte(); 352 case_raw_high_byte();
333 case_skipvalue(); 353 case_skipvalue();
334 case_find_keywords(); 354 case_find_keywords();
355 case_value_stops_at_dest_end();
356 case_keyword_with_decoded_nul_does_not_match();
335 case_fmt_passes_safe_bytes(); 357 case_fmt_passes_safe_bytes();
336 case_fmt_escapes_the_rest(); 358 case_fmt_escapes_the_rest();
337 case_fmt_hex_is_lower_case(); 359 case_fmt_hex_is_lower_case();