From 6837c5ac80e3624c6dc3a82d03e50d68b2f5a989 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Mon, 26 Oct 2026 17:13:36 +0100 Subject: Second run of making variable and function names more readable, urlencoder now matches overall naming scheme --- Makefile | 8 +- opentracker.c | 2 +- ot_accesslist.c | 4 +- ot_fullscrape.c | 12 +- ot_hex.c | 4 +- ot_hex.h | 4 +- ot_http.c | 40 ++--- ot_iovec.c | 10 +- ot_iovec.h | 6 +- ot_stats.c | 22 +-- ot_sync.c | 6 +- ot_urlencoded.c | 183 ++++++++++++++++++++ ot_urlencoded.h | 61 +++++++ scan_urlencoded_query.c | 183 -------------------- scan_urlencoded_query.h | 61 ------- tests/runner.c | 2 +- tests/t_hex.c | 28 +-- tests/t_iovec.c | 20 +-- tests/t_scan_urlencoded_query.c | 372 ---------------------------------------- tests/t_urlencoded.c | 372 ++++++++++++++++++++++++++++++++++++++++ tests/test.h | 2 +- 21 files changed, 701 insertions(+), 701 deletions(-) create mode 100644 ot_urlencoded.c create mode 100644 ot_urlencoded.h delete mode 100644 scan_urlencoded_query.c delete mode 100644 scan_urlencoded_query.h delete mode 100644 tests/t_scan_urlencoded_query.c create mode 100644 tests/t_urlencoded.c diff --git a/Makefile b/Makefile index 87c20e2..d9b8690 100644 --- a/Makefile +++ b/Makefile @@ -68,8 +68,8 @@ CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread BINARY =opentracker -HEADERS=trackerlogic.h scan_urlencoded_query.h ot_batch.h ot_buf.h ot_fdstate.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h -SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_batch.c ot_buf.c ot_fdstate.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c +HEADERS=trackerlogic.h ot_urlencoded.h ot_batch.h ot_buf.h ot_fdstate.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h +SOURCES=opentracker.c trackerlogic.c ot_urlencoded.c ot_batch.c ot_buf.c ot_fdstate.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c SOURCES_proxy=proxy.c ot_batch.c ot_fdstate.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c OBJECTS = $(SOURCES:%.c=%.o) @@ -119,8 +119,8 @@ $(OBJECTS_proxy_debug): $(HEADERS) # has to. tests/stubs.c supplies the few symbols those modules reference but # the tests never reach. TESTBIN=tests/runner -TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_scan_urlencoded_query.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_batch.c tests/t_fdstate.c -TEST_UNITS=scan_urlencoded_query.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c +TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_batch.c tests/t_fdstate.c +TEST_UNITS=ot_urlencoded.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c TEST_HEADERS=tests/test.h $(HEADERS) # Tests reach code that sits behind feature flags, so the units under test are # built with those features enabled. A test binary is not a production binary. diff --git a/opentracker.c b/opentracker.c index 0f0b314..f0f6404 100644 --- a/opentracker.c +++ b/opentracker.c @@ -375,7 +375,7 @@ void load_state(const char *const state_filename) { while (fgets(inbuf, sizeof(inbuf), state_filehandle)) { size_t i; - if (!hex_tohash(infohash, inbuf)) + if (!hex_to_hash(infohash, inbuf)) continue; i = 2 * sizeof(ot_hash); diff --git a/ot_accesslist.c b/ot_accesslist.c index ef845d6..79a11a5 100644 --- a/ot_accesslist.c +++ b/ot_accesslist.c @@ -110,7 +110,7 @@ size_t accesslist_parse_hashes(ot_hash *dest, size_t capacity, const char *buf, size_t count = 0; while (count < capacity && end - offs >= 40) { - if (hex_tohash(dest[count], offs)) { + if (hex_to_hash(dest[count], offs)) { offs += 40; if (offs == end || !hex_isdigit((unsigned char)*offs)) @@ -308,7 +308,7 @@ static void *accesslist_adddel_worker(const char *fifoname, ot_accesslist *_ if (linelen < 41) continue; - if (!hex_tohash(info_hash, line)) + if (!hex_to_hash(info_hash, line)) continue; /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the diff --git a/ot_fullscrape.c b/ot_fullscrape.c index 788e574..0dee5c2 100644 --- a/ot_fullscrape.c +++ b/ot_fullscrape.c @@ -25,7 +25,7 @@ #include "ot_fullscrape.h" #include "ot_iovec.h" #include "ot_mutex.h" -#include "scan_urlencoded_query.h" +#include "ot_urlencoded.h" #include "trackerlogic.h" /* Fetch full scrape info for all torrents @@ -105,12 +105,12 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre break; case TASK_FULLSCRAPE_TPB_ASCII: - hex_fromhash(r, *hash); + hex_from_hash(r, *hash); r += 2 * sizeof(ot_hash); r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); break; case TASK_FULLSCRAPE_TPB_ASCII_PLUS: - hex_fromhash(r, *hash); + hex_from_hash(r, *hash); r += 2 * sizeof(ot_hash); r += sprintf(r, ":%zd:%zd:%zd\n", seed_count, peer_count - seed_count, down_count); break; @@ -126,7 +126,7 @@ static char * fullscrape_write_one( ot_tasktype mode, char *r, ot_torrent *torre r += sprintf(r, ":%zd:%zd\n", seed_count, peer_count - seed_count); break; case TASK_FULLSCRAPE_TRACKERSTATE: - hex_fromhash(r, *hash); + hex_from_hash(r, *hash); r += 2 * sizeof(ot_hash); r += sprintf(r, ":%zd:%zd\n", torrent->peer_list6->base, down_count); break; @@ -296,7 +296,7 @@ static void fullscrape_make_gzip(int taskid, ot_tasktype mode) { iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); if (!iovector.iov_base) { - fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); + fprintf(stderr, "Problem with iovec_next_chunk\n"); deflateEnd(&strm); return; } @@ -422,7 +422,7 @@ static void fullscrape_make_zstd(int taskid, ot_tasktype mode) { iovector.iov_base = malloc(OT_SCRAPE_CHUNK_SIZE); if (!iovector.iov_base) { - fprintf(stderr, "Problem with iovec_fix_increase_or_free\n"); + fprintf(stderr, "Problem with iovec_next_chunk\n"); ZSTD_freeCCtx(zstream); return; } diff --git a/ot_hex.c b/ot_hex.c index 9581951..2bf69c4 100644 --- a/ot_hex.c +++ b/ot_hex.c @@ -23,7 +23,7 @@ int hex_value(int c) { int hex_isdigit(int c) { return hex_value(c) >= 0; } -char *hex_fromhash(char *dest, const uint8_t *hash) { +char *hex_from_hash(char *dest, const uint8_t *hash) { static const char digits[] = "0123456789ABCDEF"; char *d = dest; size_t i; @@ -36,7 +36,7 @@ char *hex_fromhash(char *dest, const uint8_t *hash) { return dest; } -int hex_tohash(ot_hash hash, const char *src) { +int hex_to_hash(ot_hash hash, const char *src) { uint8_t decoded[sizeof(ot_hash)]; size_t i; diff --git a/ot_hex.h b/ot_hex.h index 3b45b61..28eddf5 100644 --- a/ot_hex.h +++ b/ot_hex.h @@ -12,12 +12,12 @@ /* Write hash as forty upper case hex digits followed by a terminating zero. dest must have room for forty one bytes. Returns dest. */ -char *hex_fromhash(char *dest, const uint8_t *hash); +char *hex_from_hash(char *dest, const uint8_t *hash); /* Read forty hex digits from src into hash. Returns non-zero on success. On failure hash is left untouched and no byte of src beyond the offending character is read, so src need not be forty characters long. */ -int hex_tohash(ot_hash hash, const char *src); +int hex_to_hash(ot_hash hash, const char *src); /* Non-zero if c is a hexadecimal digit, in either case. */ int hex_isdigit(int c); diff --git a/ot_http.c b/ot_http.c index 6bf343c..6c3332c 100644 --- a/ot_http.c +++ b/ot_http.c @@ -27,7 +27,7 @@ #include "ot_scan.h" #include "ot_fdstate.h" #include "ot_stats.h" -#include "scan_urlencoded_query.h" +#include "ot_urlencoded.h" #include "trackerlogic.h" #define OT_MAXMULTISCRAPE_COUNT 64 @@ -257,21 +257,21 @@ static ssize_t http_handle_stats(const int64_t sock, struct ot_workstruct *ws, c #endif while (scanon) { - switch (scan_find_keywords(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { + switch (urlencoded_match_keyword(keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM)) { case -2: scanon = 0; break; /* TERMINATOR */ case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -3: - scan_urlencoded_skipvalue(&read_ptr); + urlencoded_skip_value(&read_ptr); break; case 1: /* matched "mode" */ - if ((mode = scan_find_keywords(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) + if ((mode = urlencoded_match_keyword(keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) HTTPERROR_400_PARAM; break; case 2: /* matched "format" */ - if ((format = scan_find_keywords(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) + if ((format = urlencoded_match_keyword(keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) HTTPERROR_400_PARAM; break; case 3: @@ -418,18 +418,18 @@ static ssize_t http_handle_scrape(const int64_t sock, struct ot_workstruct *ws, } while (scanon) { - switch (scan_find_keywords(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { + switch (urlencoded_match_keyword(keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM)) { case -2: scanon = 0; break; /* TERMINATOR */ default: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -3: - scan_urlencoded_skipvalue(&read_ptr); + urlencoded_skip_value(&read_ptr); break; case 1: /* matched "info_hash" */ /* ignore this, when we have less than 20 bytes */ - if (scan_urlencoded_query(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash)) + if (urlencoded_parse(&read_ptr, (char *)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE) != (ssize_t)sizeof(ot_hash)) HTTPERROR_400_PARAM; break; } @@ -518,24 +518,24 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct scanon = 1; while (scanon) { - switch (scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { + switch (urlencoded_match_keyword(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM)) { case -2: scanon = 0; break; /* TERMINATOR */ case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ case -3: - scan_urlencoded_skipvalue(&read_ptr); + urlencoded_skip_value(&read_ptr); break; case 1: /* matched "port" */ - len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); + len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff)) HTTPERROR_400_PARAM; port = htons(tmp); OT_SETPORT(&ws->peer, &port); break; case 2: /* matched "left" */ - if ((len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) + if ((len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) HTTPERROR_400_PARAM; if (scan_fixed_int(write_ptr, len, &tmp)) tmp = 0; @@ -543,7 +543,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; break; case 3: /* matched "event" */ - switch (scan_find_keywords(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { + switch (urlencoded_match_keyword(keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE)) { case -1: HTTPERROR_400_PARAM; case 1: /* matched "completed" */ @@ -557,7 +557,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct } break; case 4: /* matched "numwant" */ - len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); + len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) HTTPERROR_400_PARAM; if (tmp < 0) @@ -567,7 +567,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct numwant = tmp; break; case 5: /* matched "compact" */ - len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); + len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) HTTPERROR_400_PARAM; if (!tmp) @@ -577,7 +577,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct if (ws->hash) HTTPERROR_400_DOUBLEHASH; /* ignore this, when we have less than 20 bytes */ - if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) + if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) HTTPERROR_400_PARAM; ws->hash = (ot_hash *)write_ptr; break; @@ -585,7 +585,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct case 7: /* matched "ip" */ { char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; - len = scan_urlencoded_query(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); + len = urlencoded_parse(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); tmp_buf2[len] = 0; if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1)) HTTPERROR_400_PARAM; @@ -601,7 +601,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct uint64_t value; signed short parsed, bits; - len = scan_urlencoded_query(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE); + len = urlencoded_parse(&read_ptr, tmp_buf, SCAN_SEARCHPATH_VALUE); tmp_buf[len] = 0; if (len <= 0) HTTPERROR_400_PARAM; @@ -630,7 +630,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct #endif case 9: /* matched "peer_id" */ /* ignore this, when we have less than 20 bytes */ - if (scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) + if (urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE) != 20) HTTPERROR_400_PARAM; ws->peer_id = write_ptr; break; @@ -709,7 +709,7 @@ ssize_t http_handle_request(const int64_t sock, struct ot_workstruct *ws) { In reality we abandoned requiring the url to be correct. This now only decodes url encoded characters, we check for announces and scrapes by looking for "a*" or "sc" */ - len = scan_urlencoded_query(&read_ptr, write_ptr = read_ptr, SCAN_PATH); + len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_PATH); /* If parsing returned an error, leave with not found */ if (g_redirecturl && (len == -2)) diff --git a/ot_iovec.c b/ot_iovec.c index cd5b9f9..2ee455c 100644 --- a/ot_iovec.c +++ b/ot_iovec.c @@ -12,7 +12,7 @@ /* Opentracker */ #include "ot_iovec.h" -void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) { +void *iovec_new_chunk(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc) { void *new_data; size_t new_entries = 1 + *iovec_entries; struct iovec *new_vec = realloc(*iovector, new_entries * sizeof(struct iovec)); @@ -62,7 +62,7 @@ void iovec_free(size_t *iovec_entries, struct iovec **iovector) { *iovec_entries = 0; } -void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) { +void iovec_trim_last(size_t *iovec_entries, struct iovec **iovector, void *last_ptr) { if (*iovec_entries) { char *base = (char *)((*iovector)[*iovec_entries - 1]).iov_base; size_t new_alloc = ((char *)last_ptr) - base; @@ -72,12 +72,12 @@ void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_pt } } -void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) { +void *iovec_next_chunk(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc) { void *new_data; - iovec_fixlast(iovec_entries, iovector, last_ptr); + iovec_trim_last(iovec_entries, iovector, last_ptr); - if (!(new_data = iovec_increase(iovec_entries, iovector, new_alloc))) + if (!(new_data = iovec_new_chunk(iovec_entries, iovector, new_alloc))) iovec_free(iovec_entries, iovector); return new_data; diff --git a/ot_iovec.h b/ot_iovec.h index eb4365b..6f5a1d6 100644 --- a/ot_iovec.h +++ b/ot_iovec.h @@ -8,13 +8,13 @@ #include -void *iovec_increase(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc); +void *iovec_new_chunk(size_t *iovec_entries, struct iovec **iovector, size_t new_alloc); void *iovec_append(size_t *iovec_entries, struct iovec **iovector, struct iovec *append_iovector); -void iovec_fixlast(size_t *iovec_entries, struct iovec **iovector, void *last_ptr); +void iovec_trim_last(size_t *iovec_entries, struct iovec **iovector, void *last_ptr); void iovec_free(size_t *iovec_entries, struct iovec **iovector); size_t iovec_length(const size_t *iovec_entries, const struct iovec **iovector); -void *iovec_fix_increase_or_free(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc); +void *iovec_next_chunk(size_t *iovec_entries, struct iovec **iovector, void *last_ptr, size_t new_alloc); #endif diff --git a/ot_stats.c b/ot_stats.c index 6589aef..ceeb89d 100644 --- a/ot_stats.c +++ b/ot_stats.c @@ -345,15 +345,15 @@ size_t stats_top_txt(char *reply, size_t amount) { r += sprintf(r, "Top %zd torrents by peers:\n", amount); for (idx = 0; idx < amount; ++idx) if (top100c[idx].val) - r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, hex_fromhash(hex_out, top100c[idx].hash)); + r += sprintf(r, "\t%zd\t%s\n", top100c[idx].val, hex_from_hash(hex_out, top100c[idx].hash)); r += sprintf(r, "Top %zd torrents by seeds:\n", amount); for (idx = 0; idx < amount; ++idx) if (top100s[idx].val) - r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, hex_fromhash(hex_out, top100s[idx].hash)); + r += sprintf(r, "\t%zd\t%s\n", top100s[idx].val, hex_from_hash(hex_out, top100s[idx].hash)); r += sprintf(r, "Top %zd torrents by leechers:\n", amount); for (idx = 0; idx < amount; ++idx) if (top100l[idx].val) - r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, hex_fromhash(hex_out, top100l[idx].hash)); + r += sprintf(r, "\t%zd\t%s\n", top100l[idx].val, hex_from_hash(hex_out, top100l[idx].hash)); return r - reply; } @@ -459,7 +459,7 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, while (loglist) { if (r + (loglist->size + 64) >= re) { - r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE); + r = iovec_next_chunk(iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE); if (!r) return; re = r + 32 * OT_STATS_TMPSIZE; @@ -479,7 +479,7 @@ static void stats_return_fulllog(size_t *iovec_entries, struct iovec **iovector, free(loglist); loglist = llnext; } - iovec_fixlast(iovec_entries, iovector, r); + iovec_trim_last(iovec_entries, iovector, r); } #endif @@ -570,7 +570,7 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty *iovec_entries = 0; *iovector = NULL; - if (!(r = iovec_increase(iovec_entries, iovector, OT_STATS_TMPSIZE))) + if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_STATS_TMPSIZE))) return; switch (mode & TASK_TASK_MASK) { @@ -587,13 +587,13 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty r += stats_top_txt(r, 10); break; case TASK_STATS_TOP100: - r = iovec_fix_increase_or_free(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE); + r = iovec_next_chunk(iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE); if (!r) return; r += stats_top_txt(r, 100); break; case TASK_STATS_EVERYTHING: - r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT); + r = iovec_next_chunk(iovec_entries, iovector, r, OT_STATS_TMPSIZE + 64 * OT_PEER_TIMEOUT); if (!r) return; r += stats_return_everything(r); @@ -612,7 +612,7 @@ static void stats_make(size_t *iovec_entries, struct iovec **iovector, ot_taskty iovec_free(iovec_entries, iovector); return; } - iovec_fixlast(iovec_entries, iovector, r); + iovec_trim_last(iovec_entries, iovector, r); } void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_data) { @@ -651,9 +651,9 @@ void stats_issue_event(ot_status_event event, PROTO_FLAG proto, uintptr_t event_ localtime_r(&ttt, &time_now); strftime(timestring, sizeof(timestring), "%FT%T%z", &time_now); - hex_fromhash(hash_hex, *ws->hash); + hex_from_hash(hash_hex, *ws->hash); if (ws->peer_id) - hex_fromhash(peerid_hex, (uint8_t *)ws->peer_id); + hex_from_hash(peerid_hex, (uint8_t *)ws->peer_id); else { *peerid_hex = 0; } diff --git a/ot_sync.c b/ot_sync.c index dc71ddd..5527c94 100644 --- a/ot_sync.c +++ b/ot_sync.c @@ -77,7 +77,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) { /* Setup return vector... */ *iovec_entries = 0; *iovector = NULL; - if (!(r = iovec_increase(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE))) + if (!(r = iovec_new_chunk(iovec_entries, iovector, OT_SYNC_CHUNK_SIZE))) return; /* ... and pointer to end of current output buffer. @@ -105,7 +105,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) { /* Allocate a fresh output buffer at the end of our buffers list release bucket and return, if that fails */ - if (!(r = iovec_fix_increase_or_free(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE))) + if (!(r = iovec_next_chunk(iovec_entries, iovector, r, OT_SYNC_CHUNK_SIZE))) return mutex_bucket_unlock(bucket); /* Adjust new end of output buffer */ @@ -131,7 +131,7 @@ static void sync_make(size_t *iovec_entries, struct iovec **iovector) { *r++ = 'e'; /* Release unused memory in current output buffer */ - iovec_fixlast(iovec_entries, iovector, r); + iovec_trim_last(iovec_entries, iovector, r); } /* This is the entry point into this worker thread diff --git a/ot_urlencoded.c b/ot_urlencoded.c new file mode 100644 index 0000000..1ca6e81 --- /dev/null +++ b/ot_urlencoded.c @@ -0,0 +1,183 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +/* Opentracker */ +#include "ot_urlencoded.h" + +/* System */ +#include + +/* Idea is to do a in place replacement or guarantee at least + strlen( string ) bytes in deststring + watch http://www.ietf.org/rfc/rfc2396.txt + unreserved = alphanum | mark + mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" + we add '%' to the matrix to not stop at encoded chars. + After losing too many requests to being too strict, add the following characters to reserved matrix + relax = "+" | "," | "/" | ";" | "<" | ">" | ":" +*/ + +/* This matrix holds for each ascii character the information, + whether it is a non-terminating character for one of the three + scan states we are in, that is 'path', 'param' and 'value' from + /path?param=value¶m=value, it is encoded in bit 0, 1 and 2 + respectively + + The top bit of lower nibble indicates, whether this character is + a hard terminator, ie. \0, \n or \s, where the whole scanning + process should terminate + */ +static const unsigned char is_unreserved[256] = { + 8,0,0,0,0,0,0,0,0,0,8,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 8,7,8,8,8,7,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,7,6, + 4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,7, + 8,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,7,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +/* Do a fast nibble to hex representation conversion */ +static unsigned char fromhex(unsigned char x) { + x -= '0'; + if (x <= 9) + return x; + x &= ~0x20; + x -= 'A' - '0'; + if (x < 6) + return x + 10; + return 0xff; +} + +/* Skip the value of a param=value pair */ +void urlencoded_skip_value(char **string) { + const unsigned char *s = *(const unsigned char **)string; + unsigned char f; + + /* Since we are asked to skip the 'value', we assume to stop at + terminators for a 'value' string position */ + while ((f = is_unreserved[*s++]) & SCAN_SEARCHPATH_VALUE) + ; + + /* If we stopped at a hard terminator like \0 or \n, make the + next urlencoded_parse encounter it again */ + if (f & SCAN_SEARCHPATH_TERMINATOR) + --s; + + *string = (char *)s; +} + +int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { + char *deststring = *string; + ssize_t match_length = urlencoded_parse(string, deststring, flags); + + if (match_length < 0) + return match_length; + if (match_length == 0) + return -3; + + while (keywords->key) { + if (!strncmp(keywords->key, deststring, match_length) && !keywords->key[match_length]) + return keywords->value; + keywords++; + } + + return -3; +} + +ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { + const unsigned char *s = *(const unsigned char **)string; + unsigned char *d = (unsigned char *)deststring; + unsigned char b, c; + + /* This is the main decoding loop. + 'flag' determines, which characters are non-terminating in current context + (ie. stop at '=' and '&' if scanning for a 'param'; stop at '?' if scanning for the path ) + */ + while (is_unreserved[c = *s++] & flags) { + + /* When encountering an url escaped character, try to decode */ + if (c == '%') { + if ((b = fromhex(*s++)) == 0xff) + return -1; + if ((c = fromhex(*s++)) == 0xff) + return -1; + c |= (b << 4); + } + + /* Write (possibly decoded) character to output */ + *d++ = c; + } + + switch (c) { + case 0: + case '\r': + case '\n': + case ' ': + /* If we started scanning on a hard terminator, indicate we've finished */ + if (d == (unsigned char *)deststring) + return -2; + + /* Else make the next call to urlencoded_parse encounter it again */ + --s; + break; + case '?': + if (flags != SCAN_PATH) + return -1; + break; + case '=': + if (flags != SCAN_SEARCHPATH_PARAM) + return -1; + break; + case '&': + if (flags == SCAN_PATH) + return -1; + if (flags == SCAN_SEARCHPATH_PARAM) + --s; + break; + default: + return -1; + } + + *string = (char *)s; + return d - (unsigned char *)deststring; +} + +ssize_t scan_fixed_int(char *data, size_t len, int *tmp) { + int minus = 0; + *tmp = 0; + if (*data == '-') + --len, ++data, ++minus; + while ((len > 0) && (*data >= '0') && (*data <= '9')) { + --len; + *tmp = 10 * *tmp + *data++ - '0'; + } + if (minus) + *tmp = -*tmp; + return len; +} + +size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) { + static const char safe[] = "$/.=~-_"; + static const char hexdigit[] = "0123456789abcdef"; + size_t written = 0, i; + + for (i = 0; i < len; ++i) { + uint8_t c = src[i]; + + /* strchr would find the terminator, so a zero byte never counts as safe */ + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c && strchr(safe, c))) { + dest[written++] = (char)c; + } else { + dest[written] = '%'; + dest[written + 1] = hexdigit[c >> 4]; + dest[written + 2] = hexdigit[c & 15]; + written += 3; + } + } + + return written; +} diff --git a/ot_urlencoded.h b/ot_urlencoded.h new file mode 100644 index 0000000..8147370 --- /dev/null +++ b/ot_urlencoded.h @@ -0,0 +1,61 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +#ifndef SCAN_URLENCODED_QUERY_H__ +#define SCAN_URLENCODED_QUERY_H__ + +#include +#include +#include + +typedef struct { + const char *key; + int value; +} ot_keywords; + +typedef enum { + SCAN_PATH = 1, + SCAN_SEARCHPATH_PARAM = 2, + SCAN_SEARCHPATH_VALUE = 4, + SCAN_SEARCHPATH_TERMINATOR = 8 +} SCAN_SEARCHPATH_FLAG; + +/* string in: pointer to source + out: pointer to next scan position + deststring pointer to destination + flags determines, what to parse + returns number of valid converted characters in deststring + or -1 for parse error + or -2 for terminator found +*/ +ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); + +/* string in: pointer to source + out: pointer to next scan position + flags determines, what to parse + returns value for matched keyword + or -1 for parse error + or -2 for terminator found + or -3 for no keyword matched + */ +int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags); + +/* string in: pointer to value of a param=value pair to skip + out: pointer to next scan position on return +*/ +void urlencoded_skip_value(char **string); + +/* data pointer to len chars of string + len length of chars in data to parse + number number to receive result + returns number of bytes not parsed, mostly !=0 means fail + */ +ssize_t scan_fixed_int(char *data, size_t len, int *number); + +/* The other direction: percent encode len bytes from src into dest and return + how many characters were written. dest needs room for three times len. */ +size_t urlencoded_format(char *dest, const uint8_t *src, size_t len); + +#endif diff --git a/scan_urlencoded_query.c b/scan_urlencoded_query.c deleted file mode 100644 index cfc8708..0000000 --- a/scan_urlencoded_query.c +++ /dev/null @@ -1,183 +0,0 @@ -/* This software was written by Dirk Engling - It is considered beerware. Prost. Skol. Cheers or whatever. - - $id$ */ - -/* Opentracker */ -#include "scan_urlencoded_query.h" - -/* System */ -#include - -/* Idea is to do a in place replacement or guarantee at least - strlen( string ) bytes in deststring - watch http://www.ietf.org/rfc/rfc2396.txt - unreserved = alphanum | mark - mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" - we add '%' to the matrix to not stop at encoded chars. - After losing too many requests to being too strict, add the following characters to reserved matrix - relax = "+" | "," | "/" | ";" | "<" | ">" | ":" -*/ - -/* This matrix holds for each ascii character the information, - whether it is a non-terminating character for one of the three - scan states we are in, that is 'path', 'param' and 'value' from - /path?param=value¶m=value, it is encoded in bit 0, 1 and 2 - respectively - - The top bit of lower nibble indicates, whether this character is - a hard terminator, ie. \0, \n or \s, where the whole scanning - process should terminate - */ -static const unsigned char is_unreserved[256] = { - 8,0,0,0,0,0,0,0,0,0,8,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 8,7,8,8,8,7,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,7,6, - 4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,7, - 8,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,7,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - -/* Do a fast nibble to hex representation conversion */ -static unsigned char fromhex(unsigned char x) { - x -= '0'; - if (x <= 9) - return x; - x &= ~0x20; - x -= 'A' - '0'; - if (x < 6) - return x + 10; - return 0xff; -} - -/* Skip the value of a param=value pair */ -void scan_urlencoded_skipvalue(char **string) { - const unsigned char *s = *(const unsigned char **)string; - unsigned char f; - - /* Since we are asked to skip the 'value', we assume to stop at - terminators for a 'value' string position */ - while ((f = is_unreserved[*s++]) & SCAN_SEARCHPATH_VALUE) - ; - - /* If we stopped at a hard terminator like \0 or \n, make the - next scan_urlencoded_query encounter it again */ - if (f & SCAN_SEARCHPATH_TERMINATOR) - --s; - - *string = (char *)s; -} - -int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags) { - char *deststring = *string; - ssize_t match_length = scan_urlencoded_query(string, deststring, flags); - - if (match_length < 0) - return match_length; - if (match_length == 0) - return -3; - - while (keywords->key) { - if (!strncmp(keywords->key, deststring, match_length) && !keywords->key[match_length]) - return keywords->value; - keywords++; - } - - return -3; -} - -ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags) { - const unsigned char *s = *(const unsigned char **)string; - unsigned char *d = (unsigned char *)deststring; - unsigned char b, c; - - /* This is the main decoding loop. - 'flag' determines, which characters are non-terminating in current context - (ie. stop at '=' and '&' if scanning for a 'param'; stop at '?' if scanning for the path ) - */ - while (is_unreserved[c = *s++] & flags) { - - /* When encountering an url escaped character, try to decode */ - if (c == '%') { - if ((b = fromhex(*s++)) == 0xff) - return -1; - if ((c = fromhex(*s++)) == 0xff) - return -1; - c |= (b << 4); - } - - /* Write (possibly decoded) character to output */ - *d++ = c; - } - - switch (c) { - case 0: - case '\r': - case '\n': - case ' ': - /* If we started scanning on a hard terminator, indicate we've finished */ - if (d == (unsigned char *)deststring) - return -2; - - /* Else make the next call to scan_urlencoded_param encounter it again */ - --s; - break; - case '?': - if (flags != SCAN_PATH) - return -1; - break; - case '=': - if (flags != SCAN_SEARCHPATH_PARAM) - return -1; - break; - case '&': - if (flags == SCAN_PATH) - return -1; - if (flags == SCAN_SEARCHPATH_PARAM) - --s; - break; - default: - return -1; - } - - *string = (char *)s; - return d - (unsigned char *)deststring; -} - -ssize_t scan_fixed_int(char *data, size_t len, int *tmp) { - int minus = 0; - *tmp = 0; - if (*data == '-') - --len, ++data, ++minus; - while ((len > 0) && (*data >= '0') && (*data <= '9')) { - --len; - *tmp = 10 * *tmp + *data++ - '0'; - } - if (minus) - *tmp = -*tmp; - return len; -} - -size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) { - static const char safe[] = "$/.=~-_"; - static const char hexdigit[] = "0123456789abcdef"; - size_t written = 0, i; - - for (i = 0; i < len; ++i) { - uint8_t c = src[i]; - - /* strchr would find the terminator, so a zero byte never counts as safe */ - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c && strchr(safe, c))) { - dest[written++] = (char)c; - } else { - dest[written] = '%'; - dest[written + 1] = hexdigit[c >> 4]; - dest[written + 2] = hexdigit[c & 15]; - written += 3; - } - } - - return written; -} diff --git a/scan_urlencoded_query.h b/scan_urlencoded_query.h deleted file mode 100644 index 24e09fd..0000000 --- a/scan_urlencoded_query.h +++ /dev/null @@ -1,61 +0,0 @@ -/* This software was written by Dirk Engling - It is considered beerware. Prost. Skol. Cheers or whatever. - - $id$ */ - -#ifndef SCAN_URLENCODED_QUERY_H__ -#define SCAN_URLENCODED_QUERY_H__ - -#include -#include -#include - -typedef struct { - const char *key; - int value; -} ot_keywords; - -typedef enum { - SCAN_PATH = 1, - SCAN_SEARCHPATH_PARAM = 2, - SCAN_SEARCHPATH_VALUE = 4, - SCAN_SEARCHPATH_TERMINATOR = 8 -} SCAN_SEARCHPATH_FLAG; - -/* string in: pointer to source - out: pointer to next scan position - deststring pointer to destination - flags determines, what to parse - returns number of valid converted characters in deststring - or -1 for parse error - or -2 for terminator found -*/ -ssize_t scan_urlencoded_query(char **string, char *deststring, SCAN_SEARCHPATH_FLAG flags); - -/* string in: pointer to source - out: pointer to next scan position - flags determines, what to parse - returns value for matched keyword - or -1 for parse error - or -2 for terminator found - or -3 for no keyword matched - */ -int scan_find_keywords(const ot_keywords *keywords, char **string, SCAN_SEARCHPATH_FLAG flags); - -/* string in: pointer to value of a param=value pair to skip - out: pointer to next scan position on return -*/ -void scan_urlencoded_skipvalue(char **string); - -/* data pointer to len chars of string - len length of chars in data to parse - number number to receive result - returns number of bytes not parsed, mostly !=0 means fail - */ -ssize_t scan_fixed_int(char *data, size_t len, int *number); - -/* The other direction: percent encode len bytes from src into dest and return - how many characters were written. dest needs room for three times len. */ -size_t urlencoded_format(char *dest, const uint8_t *src, size_t len); - -#endif diff --git a/tests/runner.c b/tests/runner.c index 335d83d..9e5c99f 100644 --- a/tests/runner.c +++ b/tests/runner.c @@ -14,7 +14,7 @@ struct module { void (*run)(void); }; -static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urlencoded_query}, +static const struct module modules[] = {{"ot_urlencoded", test_urlencoded}, {"ot_iovec", test_iovec}, {"ot_vector", test_vector}, {"ot_random", test_random}, diff --git a/tests/t_hex.c b/tests/t_hex.c index 7cce8ac..a761737 100644 --- a/tests/t_hex.c +++ b/tests/t_hex.c @@ -18,7 +18,7 @@ static void case_encode(void) { test_case("a hash encodes to forty upper case digits and a terminator"); memset(dest, 0x7f, sizeof(dest)); - CHECK_PTR(hex_fromhash(dest, sample), dest); + CHECK_PTR(hex_from_hash(dest, sample), dest); CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0"); CHECK_INT(strlen(dest), 40); CHECK_INT((unsigned char)dest[41], 0x7f); @@ -28,7 +28,7 @@ static void case_decode(void) { ot_hash hash; test_case("forty digits decode back to the hash"); - CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0")); + CHECK(hex_to_hash(hash, "000123456789ABCDEFFF10203040506070809" "0A0")); CHECK_MEM(hash, sample, sizeof(sample)); } @@ -41,8 +41,8 @@ static void case_round_trip(void) { for (i = 0; i < 256; ++i) { uint8_t probe[20]; memset(probe, (uint8_t)i, sizeof(probe)); - hex_fromhash(text, probe); - if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) { + hex_from_hash(text, probe); + if (!hex_to_hash(hash, text) || memcmp(hash, probe, sizeof(probe))) { test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i); return; } @@ -54,8 +54,8 @@ static void case_decode_is_case_insensitive(void) { ot_hash upper, lower; test_case("lower case digits decode the same as upper case"); - CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01")); - CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01")); + CHECK(hex_to_hash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01")); + CHECK(hex_to_hash(lower, "abcdef0123456789abcdef0123456789abcdef01")); CHECK_MEM(lower, upper, sizeof(ot_hash)); } @@ -63,18 +63,18 @@ static void case_rejects_non_digits(void) { ot_hash hash; test_case("a non hex character anywhere rejects the whole string"); - CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0); - CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0); - CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0); + CHECK_INT(hex_to_hash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0); + CHECK_INT(hex_to_hash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0); + CHECK_INT(hex_to_hash(hash, "000123456789ABCDEF:F102030405060708090A"), 0); } static void case_rejects_short_input(void) { ot_hash hash; test_case("a string shorter than forty digits is rejected"); - CHECK_INT(hex_tohash(hash, ""), 0); - CHECK_INT(hex_tohash(hash, "00"), 0); - CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0); + CHECK_INT(hex_to_hash(hash, ""), 0); + CHECK_INT(hex_to_hash(hash, "00"), 0); + CHECK_INT(hex_to_hash(hash, "000123456789ABCDEFFF102030405060708090A"), 0); } static void case_leaves_hash_untouched_on_failure(void) { @@ -82,7 +82,7 @@ static void case_leaves_hash_untouched_on_failure(void) { test_case("a rejected string leaves the destination untouched"); memset(hash, 0x5a, sizeof(hash)); - CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0); + CHECK_INT(hex_to_hash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0); CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash)); } @@ -96,7 +96,7 @@ static void case_stops_at_the_terminator(void) { memcpy(guarded, "0012", 4); guarded[4] = 0; memset(guarded + 5, 'f', 3); - CHECK_INT(hex_tohash(hash, guarded), 0); + CHECK_INT(hex_to_hash(hash, guarded), 0); } static void case_isdigit(void) { diff --git a/tests/t_iovec.c b/tests/t_iovec.c index 89f3fda..2553141 100644 --- a/tests/t_iovec.c +++ b/tests/t_iovec.c @@ -18,13 +18,13 @@ static void case_increase_and_length(void) { void *first, *second; test_case("increase appends an entry and length is their sum"); - first = iovec_increase(&entries, &vector, 100); + first = iovec_new_chunk(&entries, &vector, 100); CHECK(first != NULL); CHECK_INT(entries, 1); CHECK_INT(vector[0].iov_len, 100); CHECK_PTR(vector[0].iov_base, first); - second = iovec_increase(&entries, &vector, 50); + second = iovec_new_chunk(&entries, &vector, 50); CHECK(second != NULL); CHECK_INT(entries, 2); CHECK_INT(vector[1].iov_len, 50); @@ -38,8 +38,8 @@ static void case_free_resets(void) { struct iovec *vector = NULL; test_case("free releases the entries and resets the vector"); - CHECK(iovec_increase(&entries, &vector, 10) != NULL); - CHECK(iovec_increase(&entries, &vector, 10) != NULL); + CHECK(iovec_new_chunk(&entries, &vector, 10) != NULL); + CHECK(iovec_new_chunk(&entries, &vector, 10) != NULL); iovec_free(&entries, &vector); CHECK_INT(entries, 0); CHECK_PTR(vector, NULL); @@ -52,12 +52,12 @@ static void case_fixlast_shrinks(void) { char *block; test_case("fixlast trims the last entry to the bytes actually used"); - CHECK(iovec_increase(&entries, &vector, 64) != NULL); - block = iovec_increase(&entries, &vector, 1000); + CHECK(iovec_new_chunk(&entries, &vector, 64) != NULL); + block = iovec_new_chunk(&entries, &vector, 1000); CHECK(block != NULL); memset(block, 'x', 20); - iovec_fixlast(&entries, &vector, block + 20); + iovec_trim_last(&entries, &vector, block + 20); CHECK_INT(entries, 2); CHECK_INT(vector[1].iov_len, 20); CHECK_INT(iovec_length(&entries, (const struct iovec **)&vector), 84); @@ -71,7 +71,7 @@ static void case_fixlast_on_empty(void) { struct iovec *vector = NULL; test_case("fixlast on an empty vector does nothing"); - iovec_fixlast(&entries, &vector, NULL); + iovec_trim_last(&entries, &vector, NULL); CHECK_INT(entries, 0); CHECK_PTR(vector, NULL); } @@ -103,9 +103,9 @@ static void case_fix_increase(void) { char *block; test_case("fix_increase trims the previous entry and adds a new one"); - block = iovec_increase(&entries, &vector, 500); + block = iovec_new_chunk(&entries, &vector, 500); CHECK(block != NULL); - block = iovec_fix_increase_or_free(&entries, &vector, block + 30, 200); + block = iovec_next_chunk(&entries, &vector, block + 30, 200); CHECK(block != NULL); CHECK_INT(entries, 2); CHECK_INT(vector[0].iov_len, 30); diff --git a/tests/t_scan_urlencoded_query.c b/tests/t_scan_urlencoded_query.c deleted file mode 100644 index cb6c982..0000000 --- a/tests/t_scan_urlencoded_query.c +++ /dev/null @@ -1,372 +0,0 @@ -/* This software was written by Dirk Engling - It is considered beerware. Prost. Skol. Cheers or whatever. - - $id$ */ - -/* System */ -#include - -/* Opentracker */ -#include "scan_urlencoded_query.h" -#include "test.h" - -/* The scanner returns the number of bytes written to the destination, or - -1 for a parse error and -2 for a hard terminator encountered before any - output was produced. It advances the source pointer past the separator it - stopped on, except for hard terminators and the '&' ending a parameter, - which it leaves for the next call to see. */ - -static void case_path(void) { - char query[] = "/announce?info_hash=A"; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("path stops at the question mark"); - n = scan_urlencoded_query(&p, dest, SCAN_PATH); - CHECK_INT(n, 9); - dest[n] = 0; - CHECK_STR(dest, "/announce"); - CHECK_INT(*p, 'i'); -} - -static void case_param_and_value(void) { - char query[] = "info_hash=abc&port=80"; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("parameter stops at '=', value stops at '&'"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); - CHECK_INT(n, 9); - dest[n] = 0; - CHECK_STR(dest, "info_hash"); - - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 3); - dest[n] = 0; - CHECK_STR(dest, "abc"); - - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); - CHECK_INT(n, 4); - dest[n] = 0; - CHECK_STR(dest, "port"); - - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 2); - dest[n] = 0; - CHECK_STR(dest, "80"); -} - -static void case_percent_decoding(void) { - char query[] = "%41%42%43&x=1"; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("percent escapes decode"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 3); - CHECK_MEM(dest, "ABC", 3); -} - -static void case_binary_info_hash(void) { - /* The twenty byte info_hash is what the announce path really decodes */ - char query[] = "%00%01%02%ff%fe%7f%80ABCDEFGHIJKLM&x=1"; - unsigned char want[] = {0x00, 0x01, 0x02, 0xff, 0xfe, 0x7f, 0x80, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M'}; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("twenty byte binary value survives decoding"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 20); - CHECK_MEM(dest, want, 20); -} - -static void case_in_place(void) { - char query[] = "%41%42&x=1"; - char *p = query; - ssize_t n; - - test_case("decoding in place over the source is safe"); - n = scan_urlencoded_query(&p, query, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 2); - CHECK_MEM(query, "AB", 2); -} - -static void case_bad_escape(void) { - char query[] = "%zz"; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("a non hexadecimal escape is a parse error"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, -1); -} - -static void case_truncated_escape(void) { - char query[] = "%4"; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("an escape cut short by the terminator is a parse error"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, -1); -} - -static void case_terminator(void) { - char empty[] = ""; - char space[] = " rest"; - char dest[64]; - char *p = empty; - ssize_t n; - - test_case("a hard terminator before any output reports end of input"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); - CHECK_INT(n, -2); - - p = space; - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_PARAM); - CHECK_INT(n, -2); -} - -static void case_terminator_after_output(void) { - char query[] = "abc"; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("a hard terminator after output is left for the next call"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 3); - CHECK_INT(*p, 0); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, -2); -} - -/* The character matrix makes '?' terminate a path only, and '=' terminate a - path or a parameter only. Both are ordinary characters inside a value, so a - value is free to contain a nested query string. */ - -static void case_separators_are_contextual(void) { - char question[] = "a?b&x=1"; - char equals[] = "a=b&x=1"; - char dest[64]; - char *p = question; - ssize_t n; - - test_case("a question mark is literal inside a value"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 3); - dest[n] = 0; - CHECK_STR(dest, "a?b"); - - test_case("an equals sign is literal inside a value"); - p = equals; - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, 3); - dest[n] = 0; - CHECK_STR(dest, "a=b"); -} - -static void case_ampersand_in_path(void) { - char query[] = "/path&x"; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("an ampersand in a path is a parse error"); - n = scan_urlencoded_query(&p, dest, SCAN_PATH); - CHECK_INT(n, -1); -} - -static void case_raw_high_byte(void) { - char query[] = {'a', 'b', (char)0x80, 0}; - char dest[64]; - char *p = query; - ssize_t n; - - test_case("an undecoded byte above ASCII is a parse error"); - n = scan_urlencoded_query(&p, dest, SCAN_SEARCHPATH_VALUE); - CHECK_INT(n, -1); -} - -static void case_skipvalue(void) { - char query[] = "abc&next=1"; - char bare[] = "abc"; - char *p = query; - - test_case("skipvalue steps over the value and its separator"); - scan_urlencoded_skipvalue(&p); - CHECK_STR(p, "next=1"); - - test_case("skipvalue leaves a hard terminator in place"); - p = bare; - scan_urlencoded_skipvalue(&p); - CHECK_INT(*p, 0); -} - -static void case_find_keywords(void) { - static const ot_keywords keywords[] = {{"port", 1}, {"left", 2}, {"compact", 3}, {NULL, -3}}; - char match[] = "left=0"; - char nomatch[] = "bogus=1"; - char prefix[] = "por=1"; - char *p = match; - - test_case("a known keyword returns its value"); - CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), 2); - - test_case("an unknown keyword returns -3"); - p = nomatch; - CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); - - test_case("a prefix of a keyword does not match it"); - p = prefix; - CHECK_INT(scan_find_keywords(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); -} - -static void case_fixed_int(void) { - char positive[] = "123"; - char negative[] = "-45"; - char trailing[] = "12a"; - int value; - - test_case("scan_fixed_int returns the count of unparsed bytes"); - CHECK_INT(scan_fixed_int(positive, 3, &value), 0); - CHECK_INT(value, 123); - - CHECK_INT(scan_fixed_int(negative, 3, &value), 0); - CHECK_INT(value, -45); - - CHECK_INT(scan_fixed_int(trailing, 3, &value), 1); - CHECK_INT(value, 12); -} - -static void case_fixed_int_bounded(void) { - char digits[] = "12345"; - int value; - - test_case("scan_fixed_int honours the length it is given"); - CHECK_INT(scan_fixed_int(digits, 3, &value), 0); - CHECK_INT(value, 123); -} - - -/* The encoding direction. Which bytes survive unescaped is fixed by what - existing parsers of the urlencoded full scrape expect, so these cases pin - the set rather than describe a preference. */ - -static void case_fmt_passes_safe_bytes(void) { - const uint8_t src[] = "aZ09$-./=_~"; - char dest[64]; - size_t n; - - test_case("alphanumerics and the safe punctuation pass through"); - n = urlencoded_format(dest, src, 11); - dest[n] = 0; - CHECK_INT(n, 11); - CHECK_STR(dest, "aZ09$-./=_~"); -} - -static void case_fmt_escapes_the_rest(void) { - const uint8_t src[] = {' ', '%', '&', '+', ':', '?'}; - char dest[64]; - size_t n; - - test_case("everything else becomes a percent escape"); - n = urlencoded_format(dest, src, sizeof(src)); - dest[n] = 0; - CHECK_INT(n, 18); - CHECK_STR(dest, "%20%25%26%2b%3a%3f"); -} - -static void case_fmt_hex_is_lower_case(void) { - const uint8_t src[] = {0x00, 0xab, 0xff}; - char dest[64]; - size_t n; - - test_case("escapes use lower case hex digits"); - n = urlencoded_format(dest, src, sizeof(src)); - dest[n] = 0; - CHECK_INT(n, 9); - CHECK_STR(dest, "%00%ab%ff"); -} - -static void case_fmt_zero_length(void) { - char dest[4]; - - test_case("a zero length input writes nothing"); - memset(dest, 0x5a, sizeof(dest)); - CHECK_INT(urlencoded_format(dest, (const uint8_t *)"abc", 0), 0); - CHECK_MEM(dest, "\x5a\x5a\x5a\x5a", 4); -} - -static void case_fmt_worst_case_fits(void) { - uint8_t src[20]; - char dest[61]; - size_t i, n; - - test_case("a hash of entirely unsafe bytes needs three times its length"); - for (i = 0; i < sizeof(src); ++i) - src[i] = 0xff; - n = urlencoded_format(dest, src, sizeof(src)); - dest[n] = 0; - CHECK_INT(n, 60); - for (i = 0; i < 20; ++i) - if (memcmp(dest + 3 * i, "%ff", 3)) { - test_failf(__FILE__, __LINE__, "escape %zu is wrong", i); - return; - } - ++test_checks; -} - -static void case_fmt_decoder_disagrees_on_dollar(void) { - /* Documenting a known asymmetry rather than a wish. Of the bytes the - encoder passes through unescaped, '$' is the only one the decoder then - refuses, and it refuses the whole string rather than stopping short. - Output of the urlencoded full scrape therefore does not round trip - through scan_urlencoded_query when a hash contains 0x24. Harmless today - because only third party tooling reads that output. */ - const uint8_t src[] = {'a', '$', 'b'}; - char dest[16]; - char scanned[16]; - char *p = dest; - size_t n; - ssize_t got; - - test_case("the encoder passes '$' but the decoder rejects it"); - n = urlencoded_format(dest, src, sizeof(src)); - dest[n] = 0; - CHECK_STR(dest, "a$b"); - - got = scan_urlencoded_query(&p, scanned, SCAN_SEARCHPATH_VALUE); - CHECK_INT(got, -1); -} - -void test_scan_urlencoded_query(void) { - case_path(); - case_param_and_value(); - case_percent_decoding(); - case_binary_info_hash(); - case_in_place(); - case_bad_escape(); - case_truncated_escape(); - case_terminator(); - case_terminator_after_output(); - case_separators_are_contextual(); - case_ampersand_in_path(); - case_raw_high_byte(); - case_skipvalue(); - case_find_keywords(); - case_fixed_int(); - case_fixed_int_bounded(); - case_fmt_passes_safe_bytes(); - case_fmt_escapes_the_rest(); - case_fmt_hex_is_lower_case(); - case_fmt_zero_length(); - case_fmt_worst_case_fits(); - case_fmt_decoder_disagrees_on_dollar(); -} diff --git a/tests/t_urlencoded.c b/tests/t_urlencoded.c new file mode 100644 index 0000000..1239e6b --- /dev/null +++ b/tests/t_urlencoded.c @@ -0,0 +1,372 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +/* System */ +#include + +/* Opentracker */ +#include "ot_urlencoded.h" +#include "test.h" + +/* The scanner returns the number of bytes written to the destination, or + -1 for a parse error and -2 for a hard terminator encountered before any + output was produced. It advances the source pointer past the separator it + stopped on, except for hard terminators and the '&' ending a parameter, + which it leaves for the next call to see. */ + +static void case_path(void) { + char query[] = "/announce?info_hash=A"; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("path stops at the question mark"); + n = urlencoded_parse(&p, dest, SCAN_PATH); + CHECK_INT(n, 9); + dest[n] = 0; + CHECK_STR(dest, "/announce"); + CHECK_INT(*p, 'i'); +} + +static void case_param_and_value(void) { + char query[] = "info_hash=abc&port=80"; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("parameter stops at '=', value stops at '&'"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); + CHECK_INT(n, 9); + dest[n] = 0; + CHECK_STR(dest, "info_hash"); + + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 3); + dest[n] = 0; + CHECK_STR(dest, "abc"); + + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); + CHECK_INT(n, 4); + dest[n] = 0; + CHECK_STR(dest, "port"); + + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 2); + dest[n] = 0; + CHECK_STR(dest, "80"); +} + +static void case_percent_decoding(void) { + char query[] = "%41%42%43&x=1"; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("percent escapes decode"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 3); + CHECK_MEM(dest, "ABC", 3); +} + +static void case_binary_info_hash(void) { + /* The twenty byte info_hash is what the announce path really decodes */ + char query[] = "%00%01%02%ff%fe%7f%80ABCDEFGHIJKLM&x=1"; + unsigned char want[] = {0x00, 0x01, 0x02, 0xff, 0xfe, 0x7f, 0x80, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M'}; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("twenty byte binary value survives decoding"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 20); + CHECK_MEM(dest, want, 20); +} + +static void case_in_place(void) { + char query[] = "%41%42&x=1"; + char *p = query; + ssize_t n; + + test_case("decoding in place over the source is safe"); + n = urlencoded_parse(&p, query, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 2); + CHECK_MEM(query, "AB", 2); +} + +static void case_bad_escape(void) { + char query[] = "%zz"; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("a non hexadecimal escape is a parse error"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, -1); +} + +static void case_truncated_escape(void) { + char query[] = "%4"; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("an escape cut short by the terminator is a parse error"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, -1); +} + +static void case_terminator(void) { + char empty[] = ""; + char space[] = " rest"; + char dest[64]; + char *p = empty; + ssize_t n; + + test_case("a hard terminator before any output reports end of input"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); + CHECK_INT(n, -2); + + p = space; + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_PARAM); + CHECK_INT(n, -2); +} + +static void case_terminator_after_output(void) { + char query[] = "abc"; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("a hard terminator after output is left for the next call"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 3); + CHECK_INT(*p, 0); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, -2); +} + +/* The character matrix makes '?' terminate a path only, and '=' terminate a + path or a parameter only. Both are ordinary characters inside a value, so a + value is free to contain a nested query string. */ + +static void case_separators_are_contextual(void) { + char question[] = "a?b&x=1"; + char equals[] = "a=b&x=1"; + char dest[64]; + char *p = question; + ssize_t n; + + test_case("a question mark is literal inside a value"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 3); + dest[n] = 0; + CHECK_STR(dest, "a?b"); + + test_case("an equals sign is literal inside a value"); + p = equals; + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, 3); + dest[n] = 0; + CHECK_STR(dest, "a=b"); +} + +static void case_ampersand_in_path(void) { + char query[] = "/path&x"; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("an ampersand in a path is a parse error"); + n = urlencoded_parse(&p, dest, SCAN_PATH); + CHECK_INT(n, -1); +} + +static void case_raw_high_byte(void) { + char query[] = {'a', 'b', (char)0x80, 0}; + char dest[64]; + char *p = query; + ssize_t n; + + test_case("an undecoded byte above ASCII is a parse error"); + n = urlencoded_parse(&p, dest, SCAN_SEARCHPATH_VALUE); + CHECK_INT(n, -1); +} + +static void case_skipvalue(void) { + char query[] = "abc&next=1"; + char bare[] = "abc"; + char *p = query; + + test_case("skipvalue steps over the value and its separator"); + urlencoded_skip_value(&p); + CHECK_STR(p, "next=1"); + + test_case("skipvalue leaves a hard terminator in place"); + p = bare; + urlencoded_skip_value(&p); + CHECK_INT(*p, 0); +} + +static void case_find_keywords(void) { + static const ot_keywords keywords[] = {{"port", 1}, {"left", 2}, {"compact", 3}, {NULL, -3}}; + char match[] = "left=0"; + char nomatch[] = "bogus=1"; + char prefix[] = "por=1"; + char *p = match; + + test_case("a known keyword returns its value"); + CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), 2); + + test_case("an unknown keyword returns -3"); + p = nomatch; + CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); + + test_case("a prefix of a keyword does not match it"); + p = prefix; + CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); +} + +static void case_fixed_int(void) { + char positive[] = "123"; + char negative[] = "-45"; + char trailing[] = "12a"; + int value; + + test_case("scan_fixed_int returns the count of unparsed bytes"); + CHECK_INT(scan_fixed_int(positive, 3, &value), 0); + CHECK_INT(value, 123); + + CHECK_INT(scan_fixed_int(negative, 3, &value), 0); + CHECK_INT(value, -45); + + CHECK_INT(scan_fixed_int(trailing, 3, &value), 1); + CHECK_INT(value, 12); +} + +static void case_fixed_int_bounded(void) { + char digits[] = "12345"; + int value; + + test_case("scan_fixed_int honours the length it is given"); + CHECK_INT(scan_fixed_int(digits, 3, &value), 0); + CHECK_INT(value, 123); +} + + +/* The encoding direction. Which bytes survive unescaped is fixed by what + existing parsers of the urlencoded full scrape expect, so these cases pin + the set rather than describe a preference. */ + +static void case_fmt_passes_safe_bytes(void) { + const uint8_t src[] = "aZ09$-./=_~"; + char dest[64]; + size_t n; + + test_case("alphanumerics and the safe punctuation pass through"); + n = urlencoded_format(dest, src, 11); + dest[n] = 0; + CHECK_INT(n, 11); + CHECK_STR(dest, "aZ09$-./=_~"); +} + +static void case_fmt_escapes_the_rest(void) { + const uint8_t src[] = {' ', '%', '&', '+', ':', '?'}; + char dest[64]; + size_t n; + + test_case("everything else becomes a percent escape"); + n = urlencoded_format(dest, src, sizeof(src)); + dest[n] = 0; + CHECK_INT(n, 18); + CHECK_STR(dest, "%20%25%26%2b%3a%3f"); +} + +static void case_fmt_hex_is_lower_case(void) { + const uint8_t src[] = {0x00, 0xab, 0xff}; + char dest[64]; + size_t n; + + test_case("escapes use lower case hex digits"); + n = urlencoded_format(dest, src, sizeof(src)); + dest[n] = 0; + CHECK_INT(n, 9); + CHECK_STR(dest, "%00%ab%ff"); +} + +static void case_fmt_zero_length(void) { + char dest[4]; + + test_case("a zero length input writes nothing"); + memset(dest, 0x5a, sizeof(dest)); + CHECK_INT(urlencoded_format(dest, (const uint8_t *)"abc", 0), 0); + CHECK_MEM(dest, "\x5a\x5a\x5a\x5a", 4); +} + +static void case_fmt_worst_case_fits(void) { + uint8_t src[20]; + char dest[61]; + size_t i, n; + + test_case("a hash of entirely unsafe bytes needs three times its length"); + for (i = 0; i < sizeof(src); ++i) + src[i] = 0xff; + n = urlencoded_format(dest, src, sizeof(src)); + dest[n] = 0; + CHECK_INT(n, 60); + for (i = 0; i < 20; ++i) + if (memcmp(dest + 3 * i, "%ff", 3)) { + test_failf(__FILE__, __LINE__, "escape %zu is wrong", i); + return; + } + ++test_checks; +} + +static void case_fmt_decoder_disagrees_on_dollar(void) { + /* Documenting a known asymmetry rather than a wish. Of the bytes the + encoder passes through unescaped, '$' is the only one the decoder then + refuses, and it refuses the whole string rather than stopping short. + Output of the urlencoded full scrape therefore does not round trip + through urlencoded_parse when a hash contains 0x24. Harmless today + because only third party tooling reads that output. */ + const uint8_t src[] = {'a', '$', 'b'}; + char dest[16]; + char scanned[16]; + char *p = dest; + size_t n; + ssize_t got; + + test_case("the encoder passes '$' but the decoder rejects it"); + n = urlencoded_format(dest, src, sizeof(src)); + dest[n] = 0; + CHECK_STR(dest, "a$b"); + + got = urlencoded_parse(&p, scanned, SCAN_SEARCHPATH_VALUE); + CHECK_INT(got, -1); +} + +void test_urlencoded(void) { + case_path(); + case_param_and_value(); + case_percent_decoding(); + case_binary_info_hash(); + case_in_place(); + case_bad_escape(); + case_truncated_escape(); + case_terminator(); + case_terminator_after_output(); + case_separators_are_contextual(); + case_ampersand_in_path(); + case_raw_high_byte(); + case_skipvalue(); + case_find_keywords(); + case_fixed_int(); + case_fixed_int_bounded(); + case_fmt_passes_safe_bytes(); + case_fmt_escapes_the_rest(); + case_fmt_hex_is_lower_case(); + case_fmt_zero_length(); + case_fmt_worst_case_fits(); + case_fmt_decoder_disagrees_on_dollar(); +} diff --git a/tests/test.h b/tests/test.h index bc8c3bd..cebd08a 100644 --- a/tests/test.h +++ b/tests/test.h @@ -76,7 +76,7 @@ extern unsigned long test_failures; } while (0) /* One entry point per module under test */ -void test_scan_urlencoded_query(void); +void test_urlencoded(void); void test_iovec(void); void test_vector(void); void test_random(void); -- cgit v1.3