diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-06 18:27:44 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-06 18:27:44 +0100 |
| commit | 92c27bbda9e809683537e5cc48adad0d5c390587 (patch) | |
| tree | 51c1de9d3c50e571d6a3d23857cd4198c0958d2f | |
| parent | 355c8ae57d582974f4f6a4ba42f635f2253b84a7 (diff) | |
Gives urlencoded_parse a destination bound
| -rw-r--r-- | src/ot_http.c | 34 | ||||
| -rw-r--r-- | src/ot_urlencoded.c | 12 | ||||
| -rw-r--r-- | src/ot_urlencoded.h | 4 | ||||
| -rw-r--r-- | tests/t_urlencoded.c | 66 |
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 | ||
| 71 | int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { | 71 | int 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 | ||
| 89 | ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { | 90 | ssize_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 | */ |
| 31 | ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); | 31 | ssize_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 | */ |
| 41 | int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags); | 41 | int 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 | ||
| 320 | static 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 | |||
| 330 | static 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 | |||
| 320 | void test_urlencoded(void) { | 340 | void 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(); |
