From 5471d80c5350f764ec53733bae9d32c0a1a339e0 Mon Sep 17 00:00:00 2001 From: Jesper Jensen Date: Sun, 29 Aug 2021 00:47:32 +0200 Subject: Add formal benc cursor --- src/benc.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/benc.h | 18 ++++++++++ src/log.h | 9 ++--- src/main.c | 30 ++++++++++------- test/benc.c | 107 ++++++++++++++++++++++++------------------------------------ 5 files changed, 182 insertions(+), 80 deletions(-) diff --git a/src/benc.c b/src/benc.c index 4f61ff7..eef8469 100644 --- a/src/benc.c +++ b/src/benc.c @@ -175,6 +175,102 @@ int64_t benc_decode(const char** cursor, const char* end, int* depth, struct ben return cursor_out; } +int bcur_fill(struct bcursor* cursor) { + if(cursor->source == cursor->source_end) { + return EOF; + } + + int read = benc_decode(&cursor->source, cursor->source_end, &cursor->source_depth, cursor->base, cursor->base_len); + cursor->end = cursor->base + read; + + return 0; +} + +int bcur_open(struct bcursor* cursor, const char* source, const char* source_end, struct benc_node* buffer, size_t buffer_len) { + cursor->source = source; + (*((const char**)&cursor->source_end)) = source_end; + cursor->source_depth = 0; + + *((struct benc_node**)&cursor->base) = buffer; + *((size_t*)&cursor->base_len) = buffer_len; + cursor->readhead = buffer; + + return bcur_fill(cursor); +} + +int bcur_next(struct bcursor* cursor, uint32_t steps) { + cursor->readhead+=steps; + + // Check if we need to read more + if(cursor->readhead >= cursor->end) { + return bcur_fill(cursor); + } + return 0; +} + +int bcur_next_sibling(struct bcursor* cursor) { + assert(cursor->readhead->type == BNT_LIST || cursor->readhead->type == BNT_DICT); + + if(cursor->readhead->type == BNT_LIST || cursor->readhead->type == BNT_DICT) { + uint32_t tdepth = cursor->readhead->depth; + cursor->readhead++; + while(cursor->readhead->type != BNT_END || cursor->readhead->depth != tdepth) { + if(bcur_next(cursor, 1) != 0) { + fatal("Invalid dict/list"); + } + } + } + + return bcur_next(cursor, 1); +} + +// All the arrays should be equal length +ssize_t bcur_find_key(struct bcursor* cursor, const enum benc_nodetype* keyTypes, const char** keyValues, const size_t* keyLengths, const size_t keys) { + // @ROBUSTNESS: Check if keytypes are anything but strings and ints because that is not supported + int rc; + + while(true) { + // Skip lists + if(cursor->readhead->type == BNT_LIST || cursor->readhead->type == BNT_DICT) { + rc = bcur_next_sibling(cursor); + if(rc != 0) { + fatal("Invalid dict"); + } + } else { + // Check if the key one of the ones we are looking for + for(size_t i = 0; i < keys; i++) { + if(cursor->readhead->type == keyTypes[i] && memcmp(cursor->readhead->loc, keyValues[i], MIN(cursor->readhead->size, keyLengths[i])) == 0) { + return i; + } + } + } + + // Skip the key part + rc = bcur_next(cursor, 1); + if(rc != 0) { + fatal("Invalid dict"); + } + + if(cursor->readhead->type == BNT_LIST || cursor->readhead->type == BNT_DICT) { + // Skip a multitoken element + rc = bcur_next_sibling(cursor); + if(rc != 0) { + fatal("Invalid dict"); + } + } else { + // Skip a single token element + rc = bcur_next(cursor, 1); + if(rc != 0) { + fatal("Invalid dict"); + } + } + + if(cursor->readhead->type == BNT_END) { + return -1; + } + } +} + void skip_sibling(const struct benc_node** cursor, const struct benc_node* end) { assert((*cursor)->type == BNT_LIST || (*cursor)->type == BNT_DICT); @@ -204,7 +300,7 @@ ssize_t skip_to_key(const struct benc_node** cursor, const struct benc_node* end while(true) { if(*cursor > end-1) { - fatal("Invalid dict"); + return -1; } // Skip lists diff --git a/src/benc.h b/src/benc.h index a5734ea..be06d5e 100644 --- a/src/benc.h +++ b/src/benc.h @@ -27,5 +27,23 @@ bool readint(const char** loc, int64_t* val); void benc_print(const struct benc_node* stream, size_t stream_len, int* depth); int64_t benc_decode(const char** cursor, const char* end, int* depth, struct benc_node* stream, size_t stream_len); +struct bcursor { + const char* source; + const char* const source_end; + int source_depth; + + struct benc_node* const base; + const size_t base_len; + + struct benc_node* readhead; + struct benc_node* end; +}; + +int bcur_open(struct bcursor* cursor, const char* source, const char* source_end, struct benc_node* buffer, size_t buffer_len); +int bcur_next(struct bcursor* cursor, uint32_t steps); +int bcur_next_sibling(struct bcursor* cursor); +ssize_t bcur_find_key(struct bcursor* cursor, const enum benc_nodetype* keyTypes, const char** keyValues, const size_t* keyLengths, const size_t keys); + void skip_sibling(const struct benc_node** cursor, const struct benc_node* end); ssize_t skip_to_key(const struct benc_node** cursor, const struct benc_node* end, const enum benc_nodetype* keyTypes, const char** keyValues, const size_t* keyLengths, const size_t keys); + diff --git a/src/log.h b/src/log.h index 1717011..12135af 100644 --- a/src/log.h +++ b/src/log.h @@ -13,8 +13,9 @@ printf(format "\n", ## __VA_ARGS__); \ fflush(stderr) -#define fatal(format, ...) \ - printf(format "\n", ## __VA_ARGS__); \ - fflush(stderr); \ - abort() +#define fatal(format, ...) do{\ + printf(format "\n", ## __VA_ARGS__); \ + fflush(stderr); \ + abort(); \ + } while(0) diff --git a/src/main.c b/src/main.c index 0c49797..5de849a 100644 --- a/src/main.c +++ b/src/main.c @@ -337,28 +337,36 @@ int main(int argc, char** argv) { struct benc_node* stream_cursor = stream; if(stream_cursor->type != BNT_DICT) { - err("Response is not a dict"); - exit(EXIT_FAILURE); + fatal("First value is not a dict"); } stream_cursor++; if(skip_to_key((const struct benc_node**)&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"t"}, (const size_t[]){1}, 1) == -1) { - err("No t key in packet"); - exit(EXIT_FAILURE); + fatal("No t key in packet"); } stream_cursor++; - char char_buffer = stream_cursor->loc[stream_cursor->size]; - ((char*)stream_cursor->loc)[stream_cursor->size] = '\0'; - uint32_t transaction = strtol(stream_cursor->loc, NULL, 10); - ((char*)stream_cursor->loc)[stream_cursor->size] = char_buffer; + uint32_t transaction; + { + // Temporary null terminate the string to parse the number without a copy + char char_buffer = stream_cursor->loc[stream_cursor->size]; + ((char*)stream_cursor->loc)[stream_cursor->size] = '\0'; + char* end; + transaction = strtol(stream_cursor->loc, &end, 10); + ((char*)stream_cursor->loc)[stream_cursor->size] = char_buffer; + + if(end != stream_cursor->loc+stream_cursor->size) { + dbg("DISCARD: Transaction id is not a number %.*s", stream_cursor->size, stream->loc); + continue; + } + } uint16_t reqId; if(!find_req(transaction, &reqId)) { - err("No request with transaction ID %d", transaction); - exit(EXIT_FAILURE); + dbg("DISCARD: unknown transaction id %d", transaction); + continue; } - dbg("Transaction id matches requst %d", reqId); + dbg("Transaction id matches request %d", reqId); if(sockaddr_cmp(&requestdata[reqId].addr, (struct sockaddr*)&remote) != 0) { err("Unexpected IP for valid transaction"); diff --git a/test/benc.c b/test/benc.c index a60e8f0..d734907 100644 --- a/test/benc.c +++ b/test/benc.c @@ -231,136 +231,115 @@ void test_find_key_under_cursor() { struct benc_node stream[4]; char* packet = "d1:ri1ee"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 4); + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 4); + bcur_next(&bcursor, 1); // Skip the dict token - - const struct benc_node* stream_cursor = stream; - stream_cursor++; // Skip the dict - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); TEST_ASSERT_EQUAL(0, found); - TEST_ASSERT_EQUAL_PTR(stream+1, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+1, bcursor.readhead); } void test_key_not_found() { struct benc_node stream[4]; char* packet = "d1:ri1ee"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 4); - + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 4); + bcur_next(&bcursor, 1); // Skip the dict token - const struct benc_node* stream_cursor = stream; - stream_cursor++; // Skip the dict - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"a"}, (const size_t[]){1}, 1); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"a"}, (const size_t[]){1}, 1); TEST_ASSERT_EQUAL(-1, found); - TEST_ASSERT_EQUAL_PTR(stream+3, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+3, bcursor.readhead); } void test_skip_nested_list_value() { struct benc_node stream[8]; char* packet = "d1:al1:re1:ri1ee"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 8); - + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 8); + bcur_next(&bcursor, 1); // Skip the dict token - const struct benc_node* stream_cursor = stream; - stream_cursor++; // We know the first is a dict - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); TEST_ASSERT_EQUAL(0, found); - TEST_ASSERT_EQUAL_PTR(stream+5, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+5, bcursor.readhead); } void test_skip_nested_dict_value() { struct benc_node stream[9]; char* packet = "d1:adi2e1:re1:ri1ee"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 9); + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 9); + bcur_next(&bcursor, 1); // Skip the dict token - const struct benc_node* stream_cursor = stream; - stream_cursor++; // We know the first is a dict - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); TEST_ASSERT_EQUAL(0, found); - TEST_ASSERT_EQUAL_PTR(stream+6, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+6, bcursor.readhead); } void test_skip_nested_dict_key() { struct benc_node stream[9]; char* packet = "d1:ad1:ri2ee1:ri1ee"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 9); + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 9); + bcur_next(&bcursor, 1); // Skip the dict token - const struct benc_node* stream_cursor = stream; - stream_cursor++; // We know the first is a dict - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); TEST_ASSERT_EQUAL(0, found); - TEST_ASSERT_EQUAL_PTR(stream+6, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+6, bcursor.readhead); } void test_skip_multilevel_list() { struct benc_node stream[9]; char* packet = "d1:all1:ree1:ri1ee"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 9); + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 9); + bcur_next(&bcursor, 1); // Skip the dict token - const struct benc_node* stream_cursor = stream; - stream_cursor++; // We know the first is a dict - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); TEST_ASSERT_EQUAL(0, found); - TEST_ASSERT_EQUAL_PTR(stream+7, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+7, bcursor.readhead); } void test_find_any_semantics() { struct benc_node stream[9]; char* packet = "d1:a1:a1:b1:be"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 9); + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 9); + bcur_next(&bcursor, 1); // Skip the dict token - const struct benc_node* stream_cursor = stream; - stream_cursor++; // We know the first is a dict - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING, BNT_STRING}, (const char*[]){"b", "a"}, (const size_t[]){1, 1}, 2); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING, BNT_STRING}, (const char*[]){"b", "a"}, (const size_t[]){1, 1}, 2); TEST_ASSERT_EQUAL(1, found); - TEST_ASSERT_EQUAL_PTR(stream+1, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+1, bcursor.readhead); - stream_cursor+=2; // Skip the key and value - found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING, BNT_STRING}, (const char*[]){"b", "a"}, (const size_t[]){1, 1}, 2); + bcur_next(&bcursor, 2); // Skip the dict token + found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING, BNT_STRING}, (const char*[]){"b", "a"}, (const size_t[]){1, 1}, 2); TEST_ASSERT_EQUAL(0, found); - TEST_ASSERT_EQUAL_PTR(stream+3, stream_cursor); + TEST_ASSERT_EQUAL_PTR(stream+3, bcursor.readhead); } void test_stop_at_end() { struct benc_node stream[9]; char* packet = "ld1:a1:aee"; - const char* cursor = packet; - int depth = 0; - int64_t len = benc_decode(&cursor, cursor + strlen(packet), &depth, stream, 9); + struct bcursor bcursor; + bcur_open(&bcursor, packet, packet+strlen(packet), stream, 9); + bcur_next(&bcursor, 2); // Skip the dict token - const struct benc_node* stream_cursor = stream; - stream_cursor++; // We know the first is a list - stream_cursor++; // We know the second is a dict - // X does not exist in the packet - ssize_t found = skip_to_key(&stream_cursor, stream+len, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"x"}, (const size_t[]){1}, 1); + ssize_t found = bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"x"}, (const size_t[]){1}, 1); TEST_ASSERT_EQUAL_MESSAGE(-1, found, "Found something"); - TEST_ASSERT_EQUAL_PTR_MESSAGE(stream+4, stream_cursor, "Didn't stop at dict end"); + TEST_ASSERT_EQUAL_PTR_MESSAGE(stream+4, bcursor.readhead, "Didn't stop at dict end"); } -- cgit v1.2.3