diff options
| author | Jesper Jensen <jesper@jnsn.dev> | 2024-01-28 09:01:22 +0100 |
|---|---|---|
| committer | Jesper Jensen <jesper@jnsn.dev> | 2025-04-12 10:21:20 +0200 |
| commit | cd622b745254baafa21adf4b1b724e079652321a (patch) | |
| tree | b1faf03808a5f8729cf8a69f971c00cb9d662683 | |
| parent | 47d4780eb448f839fc6b0644135395f879080ba4 (diff) | |
I don't know
| -rw-r--r-- | Makefile | 9 | ||||
| -rw-r--r-- | src/main.c | 55 | ||||
| -rw-r--r-- | src/main.c.orig | 202 | ||||
| -rw-r--r-- | src/peers.c | 5 | ||||
| -rw-r--r-- | src/peers.h | 5 | ||||
| -rw-r--r-- | src/proto.c | 221 | ||||
| -rw-r--r-- | src/proto.c.orig | 718 | ||||
| -rw-r--r-- | src/proto.h | 45 | ||||
| -rw-r--r-- | src/proto.h.orig | 78 | ||||
| -rw-r--r-- | src/query.c | 56 | ||||
| -rw-r--r-- | src/query.h | 8 | ||||
| -rw-r--r-- | src/routing.c | 57 | ||||
| -rw-r--r-- | src/routing.c.orig | 267 | ||||
| -rw-r--r-- | src/routing.h | 3 | ||||
| -rw-r--r-- | test/proto.c | 113 | ||||
| -rw-r--r-- | test/query.c | 46 | ||||
| m--------- | thirdparty/crypto-algorithms | 0 |
17 files changed, 457 insertions, 1431 deletions
@@ -6,14 +6,15 @@ GENDIR ?= gen OBJDIR ?= obj LIBS = -lm -INCS = -Isrc/ -Igen/ -I. +INCS = -Isrc/ -Igen/ -I. -Ithirdparty/crypto-algorithms/ -CFLAGS ?= -O3 -D_FORTIFY_SOURCE=2 -Wall +CFLAGS ?= -O3 -D_FORTIFY_SOURCE=2 -Wall -g # add all the required Cflags CFLAGS += -std=gnu11 -fms-extensions -flto APP_MAIN_SOURCES = src/main.c APP_SOURCES = $(filter-out $(APP_MAIN_SOURCES),$(shell find $(SRCDIR) -name "*.c")) +APP_SOURCES += thirdparty/crypto-algorithms/sha256.c APP_OBJS = $(APP_SOURCES:%.c=$(OBJDIR)/%.o) APP_MAIN_OBJS = $(APP_MAIN_SOURCES:%.c=$(OBJDIR)/%.o) APP_DEPS = $(APP_OBJS:%.o=%.d) @@ -22,7 +23,7 @@ APP_MAIN_DEPS = $(APP_MAIN_OBJS:%.o=%.d) TEST_LIB_SOURCES = thirdparty/Unity/src/unity.c TEST_LIB_OBJS = $(TEST_LIB_SOURCES:%.c=$(OBJDIR)/%.o) TEST_LIB_DEPS = $(TEST_LIB_SOURCES:%.c=%.d) -TEST_LIB_INCS = -Ithirdparty/Unity/src +TEST_LIB_INCS = -Ithirdparty/Unity/src/ TEST_LIB_CFLAGS = -DUNITY_INCLUDE_DOUBLE TEST_SOURCES = $(shell find $(TSTDIR) -name "*.c") @@ -72,7 +73,7 @@ $(OBJDIR)/test/%.o: test/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) $(TEST_LIB_CFLAGS) $(TEST_LIB_INCS) $(INCS) -MMD -o $@ -c $< -# Generated test sources are located under obj +# Generated test sources are located under obj $(OBJDIR)/test/%.o: $(OBJDIR)/test/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) $(TEST_LIB_CFLAGS) $(TEST_LIB_INCS) $(INCS) -MMD -o $@ -c $< @@ -81,8 +81,21 @@ void flush_messages(int sfd, struct message* cursor, const struct message* const } } +struct lookup { + struct nodeid target; + + struct nodeid closest[8]; + struct addr closest_addr[8]; + bool closest_valid[8]; // @SLOP: This could be a single word + + uint64_t outstanding; + + time_t wake; +}; + +#define OUTBOX_SIZE 32 int main(int argc, char** argv) { - struct message outbuff[32] = {0}; + struct message outbuff[OUTBOX_SIZE] = {0}; struct sigaction sa; sa.sa_handler = sigint_handler; @@ -92,11 +105,11 @@ int main(int argc, char** argv) { if(sigaction(SIGINT, &sa, NULL) == -1) fatal("Couldn't set signal handler"); - struct dht dht; + struct dht dht = {0}; { int rc = read_config(); if(rc == CONF_ENO) { - myID = (struct nodeid){.inner={0xebe9bbf1, 0x3cdba6b3, 0x993e0c87, 0x900d5e25}}; + myID = (struct nodeid){.inner={0xebe9bbf1, 0x3cdba6b3, 0x993e0c87, 0x900d5e25, 0x00000000}}; routing_flush(); allocate_hashtable(); } @@ -108,6 +121,21 @@ int main(int argc, char** argv) { proto_begin(&dht, time(NULL), &message_cursor, outbuff+32); flush_messages(dht.sfd, outbuff, message_cursor); + struct lookup lookup; + // Init the lookup + { + lookup.wake = 0; + lookup.target = (struct nodeid){.inner={0x19b8a941, 0x38fa0191, 0x1403fac2, 0x581000ab, 0x19583cda}}; + + struct entry* entry[8]; + int found = routing_closest(&lookup.target, 8, entry); + for(size_t i = 0; i < found; i++) { + lookup.closest[i] = entry[i]->id; + lookup.closest_addr[i] = entry[i]->addr; + lookup.closest_valid[i] = true; + } + } + #define RECV_BUFF_SIZE 4096 char buff_storage[RECV_BUFF_SIZE+1]; int rc = 0; @@ -115,24 +143,7 @@ int main(int argc, char** argv) { char* buff = buff_storage; bool timedout = false; - time_t next = 0; - if(!dht.pause){ - struct entry* oldest; - routing_oldest(&oldest); - if(oldest != NULL) - next = oldest->expire; - } else { - dbg("DHT timeout is paused"); - } - - for(int i = 0; i < MAX_INFLIGHT; i++) { - if(!dht.reqalloc[i]) - continue; - - time_t timeout = dht.requestdata[i].timeout; - if(next == 0 || (timeout != 0 && difftime(timeout, next) < 0)) - next = timeout; - } + time_t next = dht.wake; if(next != 0) { time_t sleepfor = next - time(NULL); @@ -180,7 +191,7 @@ int main(int argc, char** argv) { struct message* message_cursor = outbuff; time_t now = time(NULL); - rc = proto_run(&dht, buff, recv_len, (struct sockaddr_in*)&remote, remote_len, now, &message_cursor, outbuff+10); + rc = proto_run(&dht, buff, recv_len, (struct sockaddr_in*)&remote, remote_len, now, &message_cursor, outbuff+OUTBOX_SIZE); flush_messages(dht.sfd, outbuff, message_cursor); } diff --git a/src/main.c.orig b/src/main.c.orig deleted file mode 100644 index 95dc2b9..0000000 --- a/src/main.c.orig +++ /dev/null @@ -1,202 +0,0 @@ -#include "proto.h" -#include "peers.h" -#include "log.h" - -#include <time.h> -#include <assert.h> -#include <errno.h> -#include <signal.h> - -static volatile bool killed = false; -void sigint_handler(int sig) { - killed = true; -} - -#define CONF_ENO 1 - -void save_config() { - FILE* config = fopen("conf.dmp", "w"); - if(config == NULL) - fatal("Couldn't open config for writing"); - - if(fwrite(&myID, sizeof(struct nodeid), 1, config) != 1) - fatal("Couldn't write state"); - if(fwrite(table, sizeof(struct entry), table_size, config) != table_size) - fatal("Couldn't write state"); - - long pos = ftell(config); - dbg("Routing stops at 0x%04lX", pos); - - if(fwrite(&peer_table_size, sizeof(peer_table_size), 1, config) != 1) - fatal("Couldn't write peer table size"); - if(fwrite(&peer_table_load, sizeof(peer_table_load), 1, config) != 1) - fatal("Couldn't write peer table load"); - if(fwrite(peer_table, sizeof(struct peer_entry), peer_table_size, config) != peer_table_size) - fatal("Couldn't write peer table"); - - if(fclose(config) != 0) - fatal("Couldn't close config file"); -} - -int read_config() { - FILE* config = fopen("conf.dmp", "r"); - if(config == NULL) - return CONF_ENO; - - if(fread(&myID, sizeof(struct nodeid), 1, config) != 1) - fatal("Couldn't read routing table"); - if(fread(table, sizeof(struct entry), table_size, config) != table_size) - fatal("Couldn't read routing table"); - - if(fread(&peer_table_size, sizeof(peer_table_size), 1, config) != 1) - fatal("Couldn't read peer table"); - if(fread(&peer_table_load, sizeof(peer_table_load), 1, config) != 1) - fatal("Couldn't read peer table"); - - peer_table = malloc(sizeof(struct peer_entry) * peer_table_size); - assert(peer_table != NULL); - - if(fread(peer_table, sizeof(struct peer_entry), peer_table_size, config) != peer_table_size) - fatal("Couldn't read peer table"); - - long pos = ftell(config); - fseek(config, 0, SEEK_END); - if(pos != ftell(config)) - fatal("The config file was too long?"); - - if(fclose(config) != 0) - fatal("Couldn't close config file"); - - return 0; -} - -void flush_messages(int sfd, struct message* cursor, const struct message* const end) { - dbg("Flushing %ld pending messages", end - cursor); - for(; cursor < end; cursor++) { - //now reply the client with the same data - int rc = sendto(sfd, cursor->payload, cursor->payload_len, 0, (const struct sockaddr*)&cursor->dest, cursor->dest_len); - if (rc < 0) { - fatal("Failed to send message %m"); - } - } -} - -int main(int argc, char** argv) { - struct message outbuff[32] = {0}; - - struct sigaction sa; - sa.sa_handler = sigint_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - if(sigaction(SIGINT, &sa, NULL) == -1) - fatal("Couldn't set signal handler"); - - struct dht dht; -<<<<<<< HEAD - dht.self = (struct nodeid){.inner={0xebe9bbf1, 0x3cdba6b3, 0x993e0c87, 0x900d5e25}}; - routing_init(&dht.self); -======= - { - int rc = read_config(); - if(rc == CONF_ENO) { - myID = (struct nodeid){.inner={0xebe9bbf1, 0x3cdba6b3, 0x993e0c87, 0x900d5e25}}; - routing_flush(); - allocate_hashtable(); - } - - dht.self = myID; - } ->>>>>>> 685b13e (I don't remember) - - struct message* message_cursor = outbuff; - proto_begin(&dht, time(NULL), &message_cursor, outbuff+32); - flush_messages(dht.sfd, outbuff, message_cursor); - -<<<<<<< HEAD - char buff_storage[2049]; -======= - -#define RECV_BUFF_SIZE 4096 - char buff_storage[RECV_BUFF_SIZE+1]; ->>>>>>> 685b13e (I don't remember) - int rc = 0; - while(rc == 0 && !killed) { - char* buff = buff_storage; - - bool timedout = false; - time_t next = 0; - if(!dht.pause){ - struct entry* oldest; - routing_oldest(&oldest); - if(oldest != NULL) - next = oldest->expire; - } else { - dbg("DHT timeout is paused"); - } - - for(int i = 0; i < MAX_INFLIGHT; i++) { - if(!dht.reqalloc[i]) - continue; - - time_t timeout = dht.requestdata[i].timeout; - if(next == 0 || (timeout != 0 && difftime(timeout, next) < 0)) - next = timeout; - } - - if(next != 0) { - time_t sleepfor = next - time(NULL); - dbg("Set timeout to %ld", sleepfor); - struct timeval tv = { - .tv_sec = sleepfor, - .tv_usec = 0, - }; - if(tv.tv_sec <= 0) { - timedout = true; - } else { - setsockopt(dht.sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - } - } - - // Try to receive some data, this is a blocking call - struct sockaddr_storage remote; - socklen_t remote_len = sizeof(remote); - ssize_t recv_len; - if(!timedout) { - recv_len = recvfrom(dht.sfd, buff, RECV_BUFF_SIZE, 0, (struct sockaddr *)&remote, &remote_len); - if(recv_len == -1) { - // This is really strange. The man pages say we should be getting - // an ETIMEDOUT here, but instead linux gives us this. - if(errno == EAGAIN) { - buff = NULL; - recv_len = 0; - } else if(errno == EINTR) { - continue; - } else { - fatal("RECV failed %d %m", errno); - } - } else if(recv_len >= RECV_BUFF_SIZE) { - dbg("Receive buffer too small"); - continue; - } - // Null terminate the packet - if(buff != NULL) { - buff[recv_len] = '\0'; - } - } else { - buff = NULL; - recv_len = 0; - } - - struct message* message_cursor = outbuff; - time_t now = time(NULL); - rc = proto_run(&dht, buff, recv_len, (struct sockaddr_in*)&remote, remote_len, now, &message_cursor, outbuff+10); - flush_messages(dht.sfd, outbuff, message_cursor); - } - - proto_end(&dht); - dbg("Writing out config"); - save_config(); - - return rc; -} diff --git a/src/peers.c b/src/peers.c index e1825c1..55f1f79 100644 --- a/src/peers.c +++ b/src/peers.c @@ -6,11 +6,14 @@ #include <string.h> #include <assert.h> +struct peer_status peer_status; struct peer_entry* peer_table; size_t peer_table_size; size_t peer_table_load; int allocate_hashtable() { + memset(&peer_status, 0, sizeof(struct peer_status)); + peer_table_load = 0; peer_table_size = 16; peer_table = calloc(peer_table_size, sizeof(struct peer_entry)); @@ -83,6 +86,7 @@ int add_peer(struct infohash* infohash, struct addr* peer) { entry->set = true; entry->key = *infohash; peer_table_load++; + peer_status.hashes++; } size_t peern = entry->value_len; @@ -91,6 +95,7 @@ int add_peer(struct infohash* infohash, struct addr* peer) { assert(peern < PEERS_PER_HASH); entry->value[peern] = *peer; entry->value_len++; + peer_status.peers++; return 0; } diff --git a/src/peers.h b/src/peers.h index 624cb21..106df7b 100644 --- a/src/peers.h +++ b/src/peers.h @@ -20,6 +20,11 @@ struct peer_entry { bool set; }; +extern struct peer_status { + size_t peers; + size_t hashes; +} peer_status; + extern struct peer_entry* peer_table; extern size_t peer_table_size; extern size_t peer_table_load; diff --git a/src/proto.c b/src/proto.c index 2191d33..9e1e7aa 100644 --- a/src/proto.c +++ b/src/proto.c @@ -3,6 +3,7 @@ #include "benc.h" #include "query.h" #include "log.h" +#include "peers.h" #include <errno.h> #include <sys/stat.h> @@ -22,21 +23,83 @@ #include <stdio.h> #include <arpa/inet.h> -#define MAX(a, b) \ - ({ \ - __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a > _b ? _a : _b; \ - }) - -#define MIN(a, b) \ - ({ \ - __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a < _b ? _a : _b; \ - }) - -#define CLAMP(a, b, c) \ +#if UINT8_MAX > RAND_MAX +#error UINT8_MAX is larger than RAND_MAX +#endif +uint8_t rand_byte() { + int limit = (RAND_MAX / UINT8_MAX)*UINT8_MAX; + int val; + while((val = rand()) >= limit); + + return val % UINT8_MAX; +} + +void token_create(struct tokens* tokens, time_t now, struct addr* remote, char* token) { + assert(tokens->head < TOKEN_KNUM); + if(difftime(now, tokens->issued[tokens->head]) >= TOKEN_ITMO) { + // We can no longer issue for this ticket + tokens->head = (tokens->head + 1) % TOKEN_KNUM; + + // The ticket we are going to overwrite should be ineligible for validation + assert(difftime(now, tokens->issued[tokens->head]) >= TOKEN_VTMO); + + tokens->issued[tokens->head] = now; + for(size_t i = 0; i < TOKEN_TLEN; i++) { + tokens->ticket[tokens->head][i] = rand_byte(); + } + } + + sha256_init(&tokens->ctx); + sha256_update(&tokens->ctx, (unsigned char*)&remote->ip, sizeof(uint32_t)); + sha256_update(&tokens->ctx, (unsigned char*)&remote->port, sizeof(uint16_t)); + sha256_update(&tokens->ctx, (unsigned char*)tokens->ticket[tokens->head], TOKEN_TLEN); + sha256_final(&tokens->ctx, (unsigned char*)token); +} + +int token_validate(struct tokens* tokens, time_t now, struct addr* remote, char* token) { + assert(tokens->head < TOKEN_KNUM); + size_t i = tokens->head; + char buf[SHA256_BLOCK_SIZE]; + while(true) { + if(difftime(now, tokens->issued[i]) >= TOKEN_VTMO) { + // If we are outside the validation timeout, we know that all the ones before us were too + break; + } + + sha256_init(&tokens->ctx); + sha256_update(&tokens->ctx, (unsigned char*)&remote->ip, sizeof(uint32_t)); + sha256_update(&tokens->ctx, (unsigned char*)&remote->port, sizeof(uint16_t)); + sha256_update(&tokens->ctx, (unsigned char*)tokens->ticket[i], TOKEN_TLEN); + sha256_final(&tokens->ctx, (unsigned char*)buf); + + if(memcmp(buf, token, SHA256_BLOCK_SIZE) == 0) { + return TOK_VALI; + } + + i = (i + TOKEN_KNUM - 1) % TOKEN_KNUM; + if(i == tokens->head) { + // We've gone through all the tickets + break; + } + } + return TOK_INVA; +} + +#define MAX(a, b) \ + ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +#define MIN(a, b) \ + ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; \ + }) + +#define CLAMP(a, b, c) \ MAX(MIN(a, c), b) @@ -54,6 +117,7 @@ int sockaddr_cmp(struct sockaddr* x, struct sockaddr* y) { typeof(a) cmp = a - b; \ if(cmp != 0) return cmp; \ } while(0) + if (x->sa_family == AF_INET) { struct sockaddr_in *xin = (void*)x; struct sockaddr_in *yin = (void*)y; @@ -103,17 +167,6 @@ struct msgbuff { PROCESS_REPONSE(getclient_response); PROCESS_TIMEOUT(getclient_timeout); -#if UINT8_MAX > RAND_MAX -#error UINT8_MAX is larger than RAND_MAX -#endif -uint8_t rand_byte() { - int limit = (RAND_MAX / UINT8_MAX)*UINT8_MAX; - int val; - while((val = rand()) >= limit); - - return val % UINT8_MAX; -} - // Number of nodeid bits #define IDBITS 160 #if IDBITS > RAND_MAX @@ -151,14 +204,6 @@ int write_find_node(char* buff, size_t* buff_len, struct nodeid* self, struct no return 0; } -struct nodeid random_node() { - struct nodeid target; - for(uint8_t *target_byte = (uint8_t*)⌖ target_byte < ((uint8_t*)&target)+sizeof(target); target_byte++) { - *target_byte = rand_byte(); - } - return target; -} - int send_ping(struct dht* dht, struct nodeid* expected, time_t now, bool node_is_new, const struct sockaddr* dest_addr, socklen_t dest_len, struct msgbuff* msgbuff) { if(*msgbuff->messages >= msgbuff->messages_end) return PROTO_ENOREQ; @@ -175,6 +220,8 @@ int send_ping(struct dht* dht, struct nodeid* expected, time_t now, bool node_is struct ping* data = &dht->requestdata[reqId].cont.ping; if(!node_is_new) { data->remote_id = *expected; + } else { + expected = &dht->self; } data->is_new = node_is_new; data->attempt = 0; @@ -185,7 +232,7 @@ int send_ping(struct dht* dht, struct nodeid* expected, time_t now, bool node_is memcpy(&dht->requestdata[reqId].addr, dest_addr, dest_len); dht->requestdata[reqId].addr_len = dest_len; - struct nodeid target = random_node(); + struct nodeid target = rand_nodeid_in_bucket(&dht->self, expected); message->payload_len = sizeof(message->payload); int rc = write_find_node(message->payload, &message->payload_len, &dht->self, &target, reqId); @@ -219,7 +266,13 @@ PROCESS_TIMEOUT(getclient_timeout) { memcpy(&message->dest, &dht->requestdata[reqId].addr, dht->requestdata[reqId].addr_len); message->dest_len = dht->requestdata[reqId].addr_len; - struct nodeid target = random_node(); + struct nodeid *remote; + if(!cont->ping.is_new) { + remote = &dht->requestdata[reqId].cont.ping.remote_id; + } else { + remote = &dht->self; + } + struct nodeid target = rand_nodeid_in_bucket(&dht->self, remote); message->payload_len = sizeof(message->payload); int rc = write_find_node(message->payload, &message->payload_len, &dht->self, &target, reqId); @@ -396,9 +449,12 @@ void proto_begin(struct dht* dht, time_t now, struct message** output, const str } struct sockaddr_in bindAddr = {0}; bindAddr.sin_family = AF_INET; - bindAddr.sin_port = htons(6881); + bindAddr.sin_port = htons(6886); bindAddr.sin_addr.s_addr = htonl(INADDR_ANY); - bind(dht->sfd, (struct sockaddr*)&bindAddr, sizeof(struct sockaddr_in)); + if(bind(dht->sfd, (struct sockaddr*)&bindAddr, sizeof(struct sockaddr_in)) != 0) { + err("Bind failed"); + // We should bail here, but the tests need this to be unhandled + } struct addrinfo hints = {0}; hints.ai_family = AF_INET; @@ -467,11 +523,16 @@ int handle_packet(struct dht* dht, time_t now, enum commandType type, char* tran dht->requestdata[reqId].timeout = 0; dht->reqalloc[reqId] = false; } else if(type == CT_QUERY) { // Must be a query - if(query == NULL) - fatal("No query function in query request"); + if(query == NULL) { + err("No query method in request body"); + return QUERY_EBADQ; + } if(transaction == NULL) fatal("No transaction in request"); - assert(transaction_len <= 16); + if(transaction_len > 16) { + err("DISCARD: Transaction ID is too long"); + return 0; + } assert(strlen(query) == query_len); @@ -481,26 +542,21 @@ int handle_packet(struct dht* dht, time_t now, enum commandType type, char* tran char* end = message->payload+sizeof(message->payload); char* cursor = message->payload; - int rc = snprintf(cursor, end-cursor , "d1:t%ld:", transaction_len); - if(rc < 0) - fatal("No space for response"); - cursor += rc; - memcpy(cursor, transaction, transaction_len); - cursor += transaction_len; - rc = snprintf(cursor, end-cursor, "1:y1:r1:r"); + int rc; + + rc = snprintf(cursor, end-cursor , "d1:r"); if(rc < 0) fatal("No space for response"); cursor += rc; - dbg("===== HANDLE %s ====", query); - rc = handle_request(&dht->self, query, (const struct sockaddr*)remote, remote_len, packet, packet_len, &cursor, end-cursor-1); + char respType = 'r'; + rc = handle_request(&dht->self, &dht->tokens, now, query, (const struct sockaddr*)remote, remote_len, packet, packet_len, &cursor, end-cursor-1); if(rc == QUERY_EUNK) { dbg("Unknown method"); // @FRAGILE: @HACK: Static offsets to fiddle with already written // out packet data. Acceptable because this is the uncommon error // case. - // The y key should have value e - *(cursor-4) = 'e'; + respType = 'e'; // The r key is called e for errors *(cursor-1) = 'e'; @@ -512,21 +568,44 @@ int handle_packet(struct dht* dht, time_t now, enum commandType type, char* tran assert(cursor < end); // Use the normal finalize flow + } else if(rc == QUERY_EBADQ) { + dbg("Invalid query"); + // @FRAGILE: @HACK: Static offsets to fiddle with already written + // out packet data. Acceptable because this is the uncommon error + // case. + respType = 'e'; + // The r key is called e for errors + *(cursor-1) = 'e'; + + // Now create the payload + rc = snprintf(cursor, end-cursor, "li204e11:Bad Requeste"); + if(rc < 0) + fatal("No space for response"); + cursor += rc; + assert(cursor < end); + + // Use the normal finalize flow } else if(rc != 0) fatal("Error handling request"); - rc = snprintf(cursor, end-cursor, "e"); + rc = snprintf(cursor, end-cursor , "1:t%ld:", transaction_len); + if(rc < 0) + fatal("No space for response"); + cursor += rc; + memcpy(cursor, transaction, transaction_len); + cursor += transaction_len; + rc = snprintf(cursor, end-cursor, "1:y1:%ce", respType); if(rc < 0) fatal("No space for response"); cursor += rc; - assert(cursor < end); + assert(cursor < end); message->payload_len = cursor - message->payload; memcpy(&message->dest, remote, remote_len); message->dest_len = remote_len; (*msgbuff->messages)++; } else if(type == CT_ERROR) { - fatal("Unhandled error"); + dbg("Unhandled error"); } else { fatal("HOW"); } @@ -534,6 +613,25 @@ int handle_packet(struct dht* dht, time_t now, enum commandType type, char* tran return 0; } +static void recalulate_waketime(struct dht *dht) { + dht->wake = 0; + if(!dht->pause){ + struct entry* oldest; + routing_oldest(&oldest); + if(oldest != NULL) + dht->wake = oldest->expire; + } + + for(int i = 0; i < MAX_INFLIGHT; i++) { + if(!dht->reqalloc[i]) + continue; + + time_t timeout = dht->requestdata[i].timeout; + if(dht->wake == 0 || (timeout != 0 && difftime(timeout, dht->wake) < 0)) + dht->wake = timeout; + } +} + int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* remote, socklen_t remote_len, time_t now, struct message** output, const struct message* const output_end) { struct msgbuff msgbuff = { output, @@ -554,6 +652,7 @@ int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* if(rc == PROTO_ENOREQ) { dht->pause = true; + recalulate_waketime(dht); return 0; } if(rc != PROTO_EDISC) { @@ -582,6 +681,7 @@ int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* int rc = send_ping(dht, &oldest->id, now, false, (const struct sockaddr*)&dest, sizeof(dest), &msgbuff); if(rc == PROTO_ENOREQ) { dht->pause = true; + recalulate_waketime(dht); return 0; } else if(rc != 0) { fatal("NOPE %d", rc); @@ -591,9 +691,10 @@ int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* routing_oldest(&oldest); } + recalulate_waketime(dht); return 0; } - + struct bcursor bcursor; struct benc_node stream[256]; bcur_open(&bcursor, buff, buff+recv_len, stream, 256); @@ -654,6 +755,8 @@ int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* } } + recalulate_waketime(dht); + if(discard){ return 0; } @@ -661,6 +764,8 @@ int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* int rc = handle_packet(dht, now, type, transaction_set ? transaction : NULL, transaction_len, query_set ? query : NULL, query_len, buff, recv_len, remote, remote_len, &msgbuff); assert(rc == 0); + // Debug output + { printf("In flight |"); for(int i = 0; i < MAX_INFLIGHT; i++) { @@ -701,5 +806,9 @@ int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* #undef LFACLEN } + { + printf("%ld peers in %ld hashes\n", peer_status.peers, peer_status.hashes); + } + return 0; } diff --git a/src/proto.c.orig b/src/proto.c.orig deleted file mode 100644 index f65a4f4..0000000 --- a/src/proto.c.orig +++ /dev/null @@ -1,718 +0,0 @@ -#include "proto.h" - -#include "benc.h" -#include "query.h" -#include "log.h" - -#include <errno.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> -#include <assert.h> -#include <stdlib.h> -#include <math.h> -#include <stdint.h> -#include <limits.h> -#include <unistd.h> -#include <stdbool.h> -#include <string.h> -#include <netdb.h> -#include <netdb.h> -#include <sys/types.h> -#include <stdio.h> - punt = true; - -#define MAX(a, b) \ - ({ \ - __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a > _b ? _a : _b; \ - }) - -#define MIN(a, b) \ - ({ \ - __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a < _b ? _a : _b; \ - }) - -#define CLAMP(a, b, c) \ - MAX(MIN(a, c), b) - - -void dbgl_id(struct nodeid* id) { - for(uint8_t i = 0; i < 5; i++) { - fprintf(stderr, "0x%08x ", id->inner[i]); - } - fprintf(stderr, "\n"); - fflush(stderr); -} - -int sockaddr_cmp(struct sockaddr* x, struct sockaddr* y) { -#define CMP(a, b) \ - do { \ - typeof(a) cmp = a - b; \ - if(cmp != 0) return cmp; \ - } while(0) - if (x->sa_family == AF_INET) { - struct sockaddr_in *xin = (void*)x; - struct sockaddr_in *yin = (void*)y; - - CMP(ntohl(xin->sin_addr.s_addr), ntohl(yin->sin_addr.s_addr)); - CMP(ntohs(xin->sin_port), ntohs(yin->sin_port)); - } else if (x->sa_family == AF_INET6) { - struct sockaddr_in6 *xin6 = (void*)x, *yin6 = (void*)y; - int r = memcmp(xin6->sin6_addr.s6_addr, yin6->sin6_addr.s6_addr, sizeof(xin6->sin6_addr.s6_addr)); - if (r != 0) - return r; - CMP(ntohs(xin6->sin6_port), ntohs(yin6->sin6_port)); - CMP(xin6->sin6_flowinfo, yin6->sin6_flowinfo); - CMP(xin6->sin6_scope_id, yin6->sin6_scope_id); - } else { - err("Unsupported sa_family"); - abort(); - } - - return 0; -}; - -bool alloc_req(struct dht* dht, uint16_t* reqId) { - for(size_t i = 0; i < MAX_INFLIGHT; i++) { - if(!dht->reqalloc[i]) { - dht->reqalloc[i] = true; - *reqId = i; - return true; - } - } - return false; -} - -bool find_req(struct dht* dht, uint32_t transId, uint16_t* reqId) { - *reqId = transId; - return dht->reqalloc[transId]; -} - -struct msgbuff { - struct message** messages; - const struct message* const messages_end; -}; - -#define PROTO_EDISC 1 -#define PROTO_ENOREQ 2 - -PROCESS_REPONSE(getclient_response); -PROCESS_TIMEOUT(getclient_timeout); - -#if UINT8_MAX > RAND_MAX -#error UINT8_MAX is larger than RAND_MAX -#endif -uint8_t rand_byte() { - int limit = (RAND_MAX / UINT8_MAX)*UINT8_MAX; - int val; - while((val = rand()) >= limit); - - return val % UINT8_MAX; -} - -// Number of nodeid bits -#define IDBITS 160 -#if IDBITS > RAND_MAX -#error IDBITS is larger than RAND_MAX -#endif -uint8_t rand_bucket() { - int limit = (RAND_MAX / IDBITS)*IDBITS; - int val; - while((val = rand()) >= limit); - - return val % IDBITS; -} - -int write_find_node(char* buff, size_t* buff_len, struct nodeid* self, struct nodeid* target, uint16_t tid) { - char* buff_end = buff + *buff_len; - - int rc = snprintf(buff, buff_end - buff, "d1:ad2:id20:"); - if(rc < 0) - fatal("Failed to write packet"); - buff += rc; - memcpy(buff, self, sizeof(struct nodeid)); - buff += sizeof(struct nodeid); - rc = snprintf(buff, buff_end - buff, "6:target20:"); - if(rc < 0) - fatal("Failed to write packet"); - buff += rc; - memcpy(buff, target, sizeof(struct nodeid)); - buff += sizeof(struct nodeid); - rc = snprintf(buff, buff_end - buff, "e1:q9:find_node1:t%d:%d1:y1:qe", tid == 0 ? 1 : (int)(log10(tid)+1), tid); - if(rc < 0) - fatal("Failed to write packet"); - buff += rc; - - *buff_len = buff - (buff_end - *buff_len); - return 0; -} - -struct nodeid random_node() { - struct nodeid target; - for(uint8_t *target_byte = (uint8_t*)⌖ target_byte < ((uint8_t*)&target)+sizeof(target); target_byte++) { - *target_byte = rand_byte(); - } - return target; -} - -int send_ping(struct dht* dht, struct nodeid* expected, time_t now, bool node_is_new, const struct sockaddr* dest_addr, socklen_t dest_len, struct msgbuff* msgbuff) { - if(*msgbuff->messages >= msgbuff->messages_end) - return PROTO_ENOREQ; - struct message* message = *msgbuff->messages; - - uint16_t reqId; - if(!alloc_req(dht, &reqId)) { - return PROTO_ENOREQ; - } - - memcpy(&message->dest, dest_addr, dest_len); - message->dest_len = dest_len; - - struct ping* data = &dht->requestdata[reqId].cont.ping; - if(!node_is_new) { - data->remote_id = *expected; - } - data->is_new = node_is_new; - data->attempt = 0; - - dht->requestdata[reqId].fun = &getclient_response; - dht->requestdata[reqId].timeout = now + PROTO_TMOUT; - dht->requestdata[reqId].timeout_fun = &getclient_timeout; - memcpy(&dht->requestdata[reqId].addr, dest_addr, dest_len); - dht->requestdata[reqId].addr_len = dest_len; - - struct nodeid target = random_node(); - - message->payload_len = sizeof(message->payload); - int rc = write_find_node(message->payload, &message->payload_len, &dht->self, &target, reqId); - if(rc != 0) { - return rc; - } - (*msgbuff->messages)++; - - return 0; -} - -PROCESS_TIMEOUT(getclient_timeout) { - // @HACK: This really sucks. maybe we should just pass in the request id - size_t reqId = (typeof(dht->requestdata[0])*)((void*)cont - offsetof(typeof(dht->requestdata[0]), cont)) - dht->requestdata; - - if(cont->ping.attempt >= 2) { - dbg("Timing out request %ld after %d attempts", reqId, cont->ping.attempt); - if(cont->ping.is_new) - return 0; - - routing_remove(&cont->ping.remote_id); - return 0; - } - -<<<<<<< HEAD - dbg("Retrying request %ld", reqId); - -======= ->>>>>>> 685b13e (I don't remember) - if(*msgbuff->messages >= msgbuff->messages_end) - return PROTO_ENOREQ; - struct message* message = *msgbuff->messages; - - memcpy(&message->dest, &dht->requestdata[reqId].addr, dht->requestdata[reqId].addr_len); - message->dest_len = dht->requestdata[reqId].addr_len; - - struct nodeid target = random_node(); - - message->payload_len = sizeof(message->payload); - int rc = write_find_node(message->payload, &message->payload_len, &dht->self, &target, reqId); - if(rc != 0) { - fatal("Can't create ping"); - } - (*msgbuff->messages)++; - - dht->requestdata[reqId].timeout = now + PROTO_TMOUT; - cont->ping.attempt++; - return PROTO_EDISC; -} - -PROCESS_REPONSE(getclient_response) { - struct benc_node stream[256]; - struct bcursor bcursor; - bcur_open(&bcursor, packet, packet+packet_len, stream, 256); - - if(bcursor.end - bcursor.readhead <= 0) { - fatal("Response too short"); - } - - struct nodeid id; - uint8_t nodes_len; - struct nodeid nodes[8]; - struct in_addr ips[8]; - uint16_t ports[8]; - - // Read the payload - { - // Check that we have a dict - if(bcursor.readhead->type != BNT_DICT) { - fatal("Response is not a dict"); - } - bcur_next(&bcursor, 1); - - bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"r"}, (const size_t[]){1}, 1); - // Skip the key - bcur_next(&bcursor, 1); - - if(bcursor.readhead->type != BNT_DICT) { - fatal("Wrong value type for response"); - } - - // Skip the dict element - bcur_next(&bcursor, 1); - - uint8_t parts = 0; - while(bcursor.readhead->type != BNT_END) { - switch(bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING, BNT_STRING}, (const char*[]){"nodes", "id"}, (const size_t[]){5, 2}, 2)) { - case 0: - // Skip the key - bcur_next(&bcursor, 1); - - if(bcursor.readhead->type != BNT_STRING) { - fatal("Nodes must be a string"); - } - - if((bcursor.readhead->size % 26) != 0) { - fatal("Nodes string value must be a multiple of 26"); - } - - nodes_len = MIN(bcursor.readhead->size/26, 8); - for(int i = 0; i < nodes_len; i++) { - memcpy(nodes+i, bcursor.readhead->loc+(26*i), 20); - memcpy(ips+i, bcursor.readhead->loc+(26*i)+20, 4); - memcpy(ports+i, bcursor.readhead->loc+(26*i)+24, 2); - } - - parts++; - - // Skip the value - bcur_next(&bcursor, 1); - break; - case 1: - // Skip the key - bcur_next(&bcursor, 1); - - if(bcursor.readhead->type != BNT_STRING) { - fatal("Wrong value type for response"); - } - - if(bcursor.readhead->size != 20) { - fatal("remote node id was not 20 bytes long"); - } - - memcpy(&id, bcursor.readhead->loc, 20); - - parts++; - - // Skip the value - bcur_next(&bcursor, 1); - break; - } - } - - if(parts < 2) { - err("Response didn't contain nodes and id"); - return PROTO_EDISC; - } - } - - // The response was good, so save the node - if(cont->ping.is_new) { - struct entry* entry; - if(routing_offer(&id, &entry)) { - struct sockaddr_in* ipv4 = (struct sockaddr_in*)remote; - entry->addr.ip = ipv4->sin_addr.s_addr; - entry->addr.port = ipv4->sin_port; - entry->expire = now + PROTO_UNCTM; - } else { - dbg("We are no longer interested"); - } - } else { - struct entry* entry = routing_get(&id); -<<<<<<< HEAD - assert(entry != NULL); - entry->expire = now + PROTO_UNCTM; -======= - // @CLEANUP: Figure out why this can be null. Is the node getting - // removed while we are waiting for a response? - if(entry != NULL) { - entry->expire = now + PROTO_UNCTM; - } ->>>>>>> 685b13e (I don't remember) - } - - uint8_t accepted = 0; - // Fan out the search if the results were interesting - for(uint8_t i = 0; i < nodes_len; i++) { - // @ROBUST: Some nodes report a bunch of nodes in the same ip. Maybe we - // could check for that here - - struct sockaddr_in dest = { - .sin_family = AF_INET, - .sin_addr = ips[i], - .sin_port = ports[i], - }; - - if(routing_interested(&nodes[i])) { - accepted++; - int rc = send_ping(dht, &nodes[i], now, true, (struct sockaddr*)&dest, sizeof(struct sockaddr_in), msgbuff); - if(rc == PROTO_ENOREQ) { - return rc; - } else if(rc != 0) { - fatal("send_ping failed %d", rc); - } - } - } - - dbg("Node provided %d nodes. %d of them were useful", nodes_len, accepted); - - return 0; -} - -enum commandType { - CT_QUERY, - CT_RESPONSE, - CT_ERROR, -}; - -void proto_begin(struct dht* dht, time_t now, struct message** output, const struct message* const output_end) { - struct msgbuff msgbuff = { - output, - output_end, - }; - dht->pause = false; - - for(int i = 0; i < MAX_INFLIGHT; i++) { - dht->reqalloc[i] = false; - } - - dbgl_id(&dht->self); - - dht->sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if(dht->sfd == -1) { - err("Failed creating socket"); - exit(1); - } - struct sockaddr_in bindAddr = {0}; - bindAddr.sin_family = AF_INET; - bindAddr.sin_port = htons(6881); - bindAddr.sin_addr.s_addr = htonl(INADDR_ANY); - bind(dht->sfd, (struct sockaddr*)&bindAddr, sizeof(struct sockaddr_in)); - - struct addrinfo hints = {0}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - hints.ai_flags = AI_NUMERICSERV; - - struct addrinfo* res; - int rc = getaddrinfo("router.bittorrent.com", "6881", &hints, &res); - if(rc != 0) { - err("Failed getting the bootstrap ip: %s", gai_strerror(rc)); - exit(EXIT_FAILURE); - } - - for(struct addrinfo* cur = res; cur != NULL; cur = cur->ai_next) { - send_ping(dht, NULL, now, true, cur->ai_addr, cur->ai_addrlen, &msgbuff); - } - - freeaddrinfo(res); -} - -void proto_end(struct dht* dht) { - close(dht->sfd); -} - -int handle_packet(struct dht* dht, time_t now, enum commandType type, char* transaction, size_t transaction_len, char* query, size_t query_len, char* packet, size_t packet_len, struct sockaddr_in* remote, socklen_t remote_len, struct msgbuff* msgbuff) { - if(type == CT_RESPONSE) { - uint32_t transaction_number; - - if(transaction == NULL) { - err("DISCARD: No transaction in response"); - return 0; - } - - // Temporary null terminate the string to parse the number without a copy - char* end; - transaction_number = strtol(transaction, &end, 10); - - if(end != transaction+transaction_len) { - err("DISCARD: Transaction id is not a number %.*s", (int)transaction_len, transaction); - return 0; - } - - uint16_t reqId; - if(!find_req(dht, transaction_number, &reqId)) { - err("DISCARD: unknown transaction id %d", transaction_number); - return 0; - } - dbg("Request %d gets a response", reqId); - - if(sockaddr_cmp((struct sockaddr*)&dht->requestdata[reqId].addr, (struct sockaddr*)remote) != 0) { - err("DISCARD: Unexpected IP for valid transaction"); - return 0; - } - - dht->pause = false; - int rc = dht->requestdata[reqId].fun(dht, now, &dht->requestdata[reqId].cont, packet, packet_len, dht->sfd, (struct sockaddr*)remote, remote_len, msgbuff); - if(rc == PROTO_ENOREQ) { - dht->pause = true; - } else if(rc == PROTO_EDISC) { - return 0; - } - - dht->requestdata[reqId].fun = NULL; - dht->requestdata[reqId].timeout_fun = NULL; - dht->requestdata[reqId].timeout = 0; - dht->reqalloc[reqId] = false; - } else if(type == CT_QUERY) { // Must be a query - if(query == NULL) - fatal("No query function in query request"); - if(transaction == NULL) - fatal("No transaction in request"); - assert(transaction_len <= 16); - - assert(strlen(query) == query_len); - - assert(*msgbuff->messages < msgbuff->messages_end); - struct message* message = *msgbuff->messages; - - char* end = message->payload+sizeof(message->payload); - char* cursor = message->payload; - - int rc = snprintf(cursor, end-cursor , "d1:t%ld:", transaction_len); - if(rc < 0) - fatal("No space for response"); - cursor += rc; - memcpy(cursor, transaction, transaction_len); - cursor += transaction_len; - rc = snprintf(cursor, end-cursor, "1:y1:r1:r"); - if(rc < 0) - fatal("No space for response"); - cursor += rc; - - dbg("===== HANDLE %s ====", query); - rc = handle_request(&dht->self, query, (const struct sockaddr*)remote, remote_len, packet, packet_len, &cursor, end-cursor-1); - if(rc == QUERY_EUNK) { - dbg("Unknown method"); - // @FRAGILE: @HACK: Static offsets to fiddle with already written - // out packet data. Acceptable because this is the uncommon error - // case. - // The y key should have value e - *(cursor-4) = 'e'; - // The r key is called e for errors - *(cursor-1) = 'e'; - - // Now create the payload - rc = snprintf(cursor, end-cursor, "li204e14:Unknown Methode"); - if(rc < 0) - fatal("No space for response"); - cursor += rc; - assert(cursor < end); - - // Use the normal finalize flow - } else if(rc != 0) fatal("Error handling request"); - - rc = snprintf(cursor, end-cursor, "e"); - if(rc < 0) - fatal("No space for response"); - cursor += rc; - assert(cursor < end); - - message->payload_len = cursor - message->payload; - - memcpy(&message->dest, remote, remote_len); - message->dest_len = remote_len; - (*msgbuff->messages)++; - } else if(type == CT_ERROR) { - fatal("Unhandled error"); - } else { - fatal("HOW"); - } - - return 0; -} - -int proto_run(struct dht* dht, char* buff, size_t recv_len, struct sockaddr_in* remote, socklen_t remote_len, time_t now, struct message** output, const struct message* const output_end) { - struct msgbuff msgbuff = { - output, - output_end, - }; - - if(recv_len == 0 && buff == NULL) { - uint8_t timedout = 0; - for(int i = 0; i < MAX_INFLIGHT; i++) { - if(!dht->reqalloc[i]) - continue; - if(dht->requestdata[i].timeout == 0) - continue; - if(difftime(now, dht->requestdata[i].timeout) < 0) - continue; - - int rc = dht->requestdata[i].timeout_fun(dht, &dht->self, now, &dht->requestdata[i].cont, &msgbuff); - - if(rc == PROTO_ENOREQ) { - dht->pause = true; - return 0; - } - if(rc != PROTO_EDISC) { - dht->requestdata[i].fun = NULL; - dht->requestdata[i].timeout_fun = NULL; - dht->requestdata[i].timeout = 0; - dht->reqalloc[i] = false; - - dht->pause = false; - } - } - if(timedout != 0) { - dbg("processed %d requests that timed out", timedout); - } - - struct entry* oldest = NULL; - routing_oldest(&oldest); - while(oldest != NULL) { - if(difftime(now, oldest->expire) < 0) - break; - - struct sockaddr_in dest = {0}; - dest.sin_family = AF_INET; - dest.sin_addr.s_addr = oldest->addr.ip; - dest.sin_port = oldest->addr.port; - int rc = send_ping(dht, &oldest->id, now, false, (const struct sockaddr*)&dest, sizeof(dest), &msgbuff); - if(rc == PROTO_ENOREQ) { - dht->pause = true; - return 0; - } else if(rc != 0) { - fatal("NOPE %d", rc); - } - - oldest->expire = 0; - routing_oldest(&oldest); - } - - return 0; - } - - struct bcursor bcursor; - struct benc_node stream[256]; - bcur_open(&bcursor, buff, buff+recv_len, stream, 256); - - if(bcursor.readhead->type != BNT_DICT) { - fatal("First value is not a dict"); - } - bcur_next(&bcursor, 1); - - bool discard = false; - enum commandType type; - bool transaction_set = false; - char transaction[64]; - size_t transaction_len; - bool query_set = false; - char query[64]; - size_t query_len; - while(bcursor.readhead->type != BNT_END) { - switch(bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING, BNT_STRING, BNT_STRING}, (const char*[]){"y", "t", "q"}, (const size_t[]){1, 1, 1}, 3)) { - case 0: - // Skip the key - bcur_next(&bcursor, 1); - if(*bcursor.readhead->loc == 'r') { - type = CT_RESPONSE; - } else if(*bcursor.readhead->loc == 'q') { - type = CT_QUERY; - } else if(*bcursor.readhead->loc == 'e') { - type = CT_ERROR; - } else { - fatal("Unknown command type %c", *bcursor.readhead->loc); - } - // Skip the value - bcur_next(&bcursor, 1); - break; - case 1: { - // Skip the key - bcur_next(&bcursor, 1); - if(bcursor.readhead->size > 64-1) - fatal("Transaction string too long"); - - transaction_set = true; - transaction_len = bcursor.readhead->size; - memcpy(transaction, bcursor.readhead->loc, transaction_len); - transaction[transaction_len] = '\0'; - - // Skip the value - bcur_next(&bcursor, 1); - break; - } - case 2: { - bcur_next(&bcursor, 1); - query_set = true; - query_len = bcursor.readhead->size; - memcpy(query, bcursor.readhead->loc, query_len); - query[query_len] = '\0'; - bcur_next(&bcursor, 1); - } - } - } - - if(discard){ - return 0; - } - - int rc = handle_packet(dht, now, type, transaction_set ? transaction : NULL, transaction_len, query_set ? query : NULL, query_len, buff, recv_len, remote, remote_len, &msgbuff); - assert(rc == 0); - - { - printf("In flight |"); - for(int i = 0; i < MAX_INFLIGHT; i++) { - if(dht->reqalloc[i]) { - printf("#"); - } else { - printf(" "); - } - } -<<<<<<< HEAD - - dbg("%ld/%ld requests pending", allocated, MAX_INFLIGHT); -======= - printf("|\n"); ->>>>>>> 685b13e (I don't remember) - } - { - int filled; - int total; -#define LFACLEN 64 - double load_factor[LFACLEN] = {0}; - routing_status(&filled, &total, load_factor, LFACLEN); - dbg("%d/%d nodes in routing table", filled, total); - -#define GRAPHY 5 - // @HACK @CLEANUP: I'm pretty zooted right now. I have zero confidence - // that this is correct. It looks allright though. - for(int y = 0; y < GRAPHY; y++) { - printf("|"); - for(int x = 0; x < LFACLEN; x++) { - double cell_load = CLAMP((load_factor[x] - ((1.0/GRAPHY) * (GRAPHY-y-1))) * GRAPHY, 0, 1); - if(cell_load == 0.0) { - printf(" "); - } else if (cell_load > 1.0 - 1.0/GRAPHY) { - printf("#"); - } else { - printf("%d", (int)(cell_load*10)); - } - } - printf("|\n"); - } -#undef GRAPHY -#undef LFACLEN - } - - return 0; -} diff --git a/src/proto.h b/src/proto.h index 0ce887e..0336c0c 100644 --- a/src/proto.h +++ b/src/proto.h @@ -1,16 +1,36 @@ #pragma once #include "routing.h" +#include "sha256.h" #include <sys/socket.h> #include <arpa/inet.h> +#define TOKEN_ITMO 20 +#define TOKEN_VTMO 60 +#define TOKEN_TLEN 32 +#define TOKEN_KNUM (TOKEN_VTMO/TOKEN_ITMO) + +#define TOK_VALI 1 +#define TOK_INVA 0 + +struct tokens { + SHA256_CTX ctx; + + char ticket[TOKEN_KNUM][TOKEN_TLEN]; + time_t issued[TOKEN_KNUM]; + size_t head; +}; + +void token_create(struct tokens* tokens, time_t now, struct addr* remote, char* token); +int token_validate(struct tokens* tokens, time_t now, struct addr* remote, char* token); + // 192.0.2.0 #define UNDEF_ADDR (struct in_addr){0xC0000200} #define MAX_DISC 32 -#define MAX_INFLIGHT 32 +#define MAX_INFLIGHT 128 -#define PROTO_UNCTM 60 -#define PROTO_TMOUT 5 +#define PROTO_UNCTM 60*5 +#define PROTO_TMOUT 30 struct ping { struct nodeid remote_id; @@ -46,24 +66,13 @@ struct dht { tmout* timeout_fun; union message_cont cont; } requestdata[MAX_INFLIGHT]; + + time_t wake; + struct tokens tokens; }; -// @HACK: This isn't true anymore -// The longest message we can send is probably a response to find_node which -// consists of: -// d1:t <-- 4 bytes -// <tid length> <-- 2 bytes -// : <-- 1 byte -// <tid> <-- 16 bytes* -// 1:y1:r1:r <-- 9 bytes -// d2:id20: <-- 8 bytes -// <our nodeid> <-- 20 bytes -// 5:nodes208: <-- 11 bytes -// <payload> <-- 208 bytes -// ee <-- 2 bytes -// TOTAL 281 bytes struct message { - char payload[281]; + char payload[1024]; size_t payload_len; struct sockaddr_storage dest; socklen_t dest_len; diff --git a/src/proto.h.orig b/src/proto.h.orig deleted file mode 100644 index ce2a713..0000000 --- a/src/proto.h.orig +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "routing.h" -#include <sys/socket.h> -#include <arpa/inet.h> - -// 192.0.2.0 -#define UNDEF_ADDR (struct in_addr){0xC0000200} -#define MAX_DISC 32 -#define MAX_INFLIGHT 32 - -#define PROTO_UNCTM 60 -#define PROTO_TMOUT 5 - -struct ping { - struct nodeid remote_id; - int attempt; - bool is_new; -}; - -union message_cont { - struct ping ping; -}; - -struct dht; -struct msgbuff; - -#define PROCESS_REPONSE(NAME) int (NAME)(struct dht* dht, time_t now, union message_cont* cont, char* packet, size_t packet_len, int socket, struct sockaddr* remote, socklen_t remote_len, struct msgbuff* msgbuff) -typedef PROCESS_REPONSE(resp); - -#define PROCESS_TIMEOUT(NAME) int (NAME)(struct dht* dht, struct nodeid* self, time_t now, union message_cont* cont, struct msgbuff* msgbuff) -typedef PROCESS_TIMEOUT(tmout); - -struct dht { - struct nodeid self; - int sfd; - - bool pause; - - bool reqalloc[MAX_INFLIGHT]; - struct { - struct sockaddr_storage addr; - socklen_t addr_len; - resp* fun; - time_t timeout; - tmout* timeout_fun; - union message_cont cont; - } requestdata[MAX_INFLIGHT]; -}; - -// @HACK: This isn't true anymore -// The longest message we can send is probably a response to find_node which -// consists of: -// d1:t <-- 4 bytes -// <tid length> <-- 2 bytes -// : <-- 1 byte -// <tid> <-- 16 bytes* -// 1:y1:r1:r <-- 9 bytes -// d2:id20: <-- 8 bytes -// <our nodeid> <-- 20 bytes -// 5:nodes208: <-- 11 bytes -// <payload> <-- 208 bytes -// ee <-- 2 bytes -// TOTAL 281 bytes -struct message { -<<<<<<< HEAD - char payload[1024]; -======= - char payload[281]; ->>>>>>> 685b13e (I don't remember) - size_t payload_len; - struct sockaddr_storage dest; - socklen_t dest_len; -}; - -void proto_begin(struct dht* dht, time_t now, struct message** output, const struct message* const output_end); -int proto_run(struct dht* dht, char* buffer, size_t buffer_len, struct sockaddr_in* remote, socklen_t remote_len, time_t now, struct message** output, const struct message* const output_end); -void proto_end(struct dht* dht); diff --git a/src/query.c b/src/query.c index 6ca6565..6cc256a 100644 --- a/src/query.c +++ b/src/query.c @@ -2,13 +2,14 @@ #include "benc.h" #include "log.h" #include "peers.h" +#include "sha256.h" #include <string.h> #include <errno.h> #include <assert.h> #include <arpa/inet.h> -int handle_request(struct nodeid* self, const char* method, const struct sockaddr* src, socklen_t src_len, const char* packet, size_t packet_len, char** response, size_t response_len) { +int handle_request(struct nodeid* self, struct tokens *tokens, time_t now, const char* method, const struct sockaddr* src, socklen_t src_len, const char* packet, size_t packet_len, char** response, size_t response_len) { if(strcmp(method, "ping") == 0) { struct bcursor bcursor; struct benc_node stream[256]; @@ -231,6 +232,13 @@ int handle_request(struct nodeid* self, const char* method, const struct sockadd return QUERY_EBADQ; } + struct addr src_addr; + { + struct sockaddr_in* ipv4 = (struct sockaddr_in*)src; + src_addr.ip = ipv4->sin_addr.s_addr; + src_addr.port = ipv4->sin_port; + } + char* end = (*response) + response_len; int rc = snprintf(*response, end-*response, "d2:id20:"); @@ -243,17 +251,20 @@ int handle_request(struct nodeid* self, const char* method, const struct sockadd *response += sizeof(struct nodeid); assert(*response < end); - rc = snprintf(*response, end-*response, "5:token1:t"); - if(rc < 0) - return QUERY_EBADQ; - *response += rc; - assert(*response < end); - struct addr* peers; size_t peers_len; get_peers(&infohash, &peers, &peers_len); + char token[SHA256_BLOCK_SIZE]; + token_create(tokens, now, &src_addr, token); + if(peers != NULL) { + rc = snprintf(*response, end-*response, "5:token%ld:%.*s", sizeof(token), (int)sizeof(token), token); + if(rc < 0) + return QUERY_EBADQ; + *response += rc; + assert(*response < end); + rc = snprintf(*response, end-*response, "6:valuesl"); if(rc < 0) return QUERY_EBADQ; @@ -297,6 +308,14 @@ int handle_request(struct nodeid* self, const char* method, const struct sockadd *response += sizeof(uint16_t); // 2 assert(*response < end); } + + int pos; + rc = snprintf(*response, end-*response, "5:token%ld:%n%*s", sizeof(token), &pos, (int)sizeof(token), " "); + memcpy((*response) + pos, token, sizeof(token)); + if(rc < 0) + return QUERY_EBADQ; + *response += rc; + assert(*response < end); } rc = snprintf(*response, end-*response, "e"); @@ -339,7 +358,7 @@ int handle_request(struct nodeid* self, const char* method, const struct sockadd uint16_t port; bool token_set = false; - char token; + char token[SHA256_BLOCK_SIZE]; while(bcursor.readhead->type != BNT_END) { switch(bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING, BNT_STRING, BNT_STRING, BNT_STRING}, (const char*[]){"implied_port", "info_hash", "port", "token"}, (const size_t[]){12, 9, 4, 5}, 4)) { @@ -403,13 +422,13 @@ int handle_request(struct nodeid* self, const char* method, const struct sockadd return QUERY_EBADQ; } - if(bcursor.readhead->size != 1) { + if(bcursor.readhead->size != SHA256_BLOCK_SIZE) { err("Bad query: Incorrect token length"); return QUERY_EBADQ; } token_set = true; - token = *bcursor.readhead->loc; + memcpy(&token, bcursor.readhead->loc, SHA256_BLOCK_SIZE); bcur_next(&bcursor, 1); break; @@ -421,21 +440,26 @@ int handle_request(struct nodeid* self, const char* method, const struct sockadd return QUERY_EBADQ; } - if(token != 't') { - err("Invalid token"); - return QUERY_EBADQ; - } - { struct sockaddr_in* ipv4 = (struct sockaddr_in*)src; struct addr src_addr; src_addr.ip = ipv4->sin_addr.s_addr; src_addr.port = ipv4->sin_port; + + if(token_validate(tokens, now, &src_addr, token) != TOK_VALI) { + err("Invalid token"); + return QUERY_EBADQ; + } + if(!implied_port) { src_addr.port = htons(port); } - add_peer(&infohash, &src_addr); + int rc = add_peer(&infohash, &src_addr); + if(rc == PEER_EFULL) { + } else if(rc != 0) { + fatal("Could not add peer (%d)", rc); + } } // Write out the response diff --git a/src/query.h b/src/query.h index 2a58e25..193df30 100644 --- a/src/query.h +++ b/src/query.h @@ -8,4 +8,10 @@ #define QUERY_EBADQ 1 #define QUERY_EUNK 2 -int handle_request(struct nodeid* self, const char* method, const struct sockaddr* src, socklen_t src_len, const char* packet, size_t packet_len, char** response, size_t response_len); +// @PASTE: This is taken from proto.h, we should move it elsewhere +#define TOK_VALI 1 +struct tokens; +void token_create(struct tokens* tokens, time_t now, struct addr* remote, char* token); +int token_validate(struct tokens* tokens, time_t now, struct addr* remote, char* token); + +int handle_request(struct nodeid* self, struct tokens* tokens, time_t now, const char* method, const struct sockaddr* src, socklen_t src_len, const char* packet, size_t packet_len, char** response, size_t response_len); diff --git a/src/routing.c b/src/routing.c index 4851dda..e2f436e 100644 --- a/src/routing.c +++ b/src/routing.c @@ -33,6 +33,7 @@ // <----------Detail----------> // +#include <arpa/inet.h> struct table { struct nodeid myID; struct entry table[RT_SIZE]; @@ -51,21 +52,21 @@ void routing_flush() { } // Calculate the common bit prefix between two node ids. -static uint8_t prefix(struct nodeid* a, struct nodeid* b) { +uint8_t prefix(struct nodeid* a, struct nodeid* b) { uint8_t c = 0; for(uint8_t i = 0; i < 5; i++) { - // Since the nodeids are stored in host byteorder in the words we have - // to make sure they're big endian before doing the prefix match, - // otherwise we end up with prefix matching that's different from the - // rest of the network - uint32_t word = htonl(a->inner[i]) ^ htonl(b->inner[i]); - - // This word is different, find the location of the difference - if (word != 0) - return c + __builtin_clz(word); - - // This word is completely the same - c += sizeof(word) * CHAR_BIT; + // Since the nodeids are stored in host byteorder in the words we have + // to make sure they're big endian before doing the prefix match, + // otherwise we end up with prefix matching that's different from the + // rest of the network + uint32_t word = htonl(a->inner[i]) ^ htonl(b->inner[i]); + + // This word is different, find the location of the difference + if (word != 0) + return c + __builtin_clz(word); + + // This word is completely the same + c += sizeof(word) * CHAR_BIT; } return c; @@ -113,6 +114,36 @@ struct entry* routing_get(struct nodeid* id) { return NULL; } +#if UINT8_MAX > RAND_MAX +#error UINT8_MAX is larger than RAND_MAX +#endif +static uint8_t rand_byte() { + int limit = (RAND_MAX / UINT8_MAX)*UINT8_MAX; + int val; + while((val = rand()) >= limit); + + return val % UINT8_MAX; +} + +struct nodeid rand_nodeid_in_bucket(struct nodeid *self, struct nodeid *other) { + struct nodeid target; + for(uint8_t *target_byte = (uint8_t*)⌖ target_byte < ((uint8_t*)&target)+sizeof(target); target_byte++) { + *target_byte = rand_byte(); + } + + uint8_t bucket = prefix(self, other); + uint8_t byte = bucket / 8; + uint8_t residual = bucket % 8; + + for(size_t i = 0; i < byte; i++) { + target.inner_b[i] = self->inner_b[i]; + } + uint8_t mask = 0xFF >> residual; + target.inner_b[byte] = (target.inner_b[byte] & mask) | (self->inner_b[byte] & ~mask); + + return target; +} + void routing_remove(struct nodeid* id) { struct entry* entry = routing_get(id); diff --git a/src/routing.c.orig b/src/routing.c.orig deleted file mode 100644 index 596548a..0000000 --- a/src/routing.c.orig +++ /dev/null @@ -1,267 +0,0 @@ -#include "routing.h" - -#include "log.h" - -#include <assert.h> -#include <limits.h> -#include <string.h> -#include <stdbool.h> -#include <stdlib.h> - -// The DHT routing table has a keyspace of 0 -- 2^160 split into buckets of 8. -// When a bucket becomes full, we split it in half. As we further expand the -// routing table we only continue to split the buckets on the side we fall on. -// -// Initially, this may sound like a binary tree (because we split it in two), -// but looking at it as a flat array leads to some interesting intuitions. -// Since we only expand one half of the "tree", the total size is bounded by -// the depth of the tree log2(2^160) == 160. -// -// As a flat array we notice the intrinsic properties of the routing table. -// With a bucket size of 8, the routing table contains 160 * 8 == 1280 nodes. -// As the node ids get less similar to our own our grouping of them becomes -// less detailed. While the bucket we are in contains node very close to us, -// the nodes furthest away from us are grouped in buckets with nodes they -// barely resemble. -// -// +----------------------------+ -// | n1 | n2 | n3 | ... | n1280 | -// +----------------------------+ -// More Less -// <--------Similarity--------> -// <----------Detail----------> -// - -<<<<<<< HEAD -struct table { - struct nodeid myID; - struct entry table[RT_SIZE]; -}; - -struct table* pTable; -======= -#include<arpa/inet.h> - -#define IDBITS 160 -#define BUCKETSIZE 8 -// The 3 here is log2(BUCKETSIZE), since the final bucket will contain all those combinations -#define BUCKETBITS 3 -#define ROUTINGSIZE (IDBITS * BUCKETSIZE) - -struct nodeid myID; -struct entry table[ROUTINGSIZE]; -int table_size = ROUTINGSIZE; ->>>>>>> 685b13e (I don't remember) - -void routing_init(struct nodeid* myid) { - pTable = malloc(sizeof(struct table)); - pTable->myID = *myid; - routing_flush(); -} - -void routing_flush() { - memset(pTable->table, 0, sizeof(pTable->table)); -} - -// Calculate the common bit prefix between two node ids. -static uint8_t prefix(struct nodeid* a, struct nodeid* b) { - uint8_t c = 0; - for(uint8_t i = 0; i < 5; i++) { - // Since the nodeids are stored in host byteorder in the words we have - // to make sure they're big endian before doing the prefix match, - // otherwise we end up with prefix matching that's different from the - // rest of the network - uint32_t word = htonl(a->inner[i]) ^ htonl(b->inner[i]); - - // This word is different, find the location of the difference - if (word != 0) - return c + __builtin_clz(word); - - // This word is completely the same - c += sizeof(word) * CHAR_BIT; - } - - return c; -} - -static int8_t scan(uint16_t baseIndex, struct nodeid* id) { - assert(baseIndex < RT_SIZE - RT_BSIZE); - int8_t index = -2; - - for(size_t i = baseIndex; i < baseIndex + RT_BSIZE; i++) { - if(!pTable->table[i].set) { - index = index == -2 ? i - baseIndex : index; - continue; - } - - if(memcmp(&pTable->table[i].id, id, sizeof(struct nodeid)) == 0) { - return -1; - } - } - - return index; -} - -static uint16_t base_bucket(struct nodeid* id) { - uint16_t bucketIndex = prefix(&pTable->myID, id); - assert(bucketIndex != RT_IDBITS); - - // If they are sufficiently similar they end up in the final bucket. Clamp the index to ensure. - bucketIndex = bucketIndex > (RT_IDBITS - RT_BBITS) ? (RT_IDBITS - RT_BBITS) : bucketIndex; - assert(bucketIndex <= RT_IDBITS - RT_BBITS); - - return bucketIndex * RT_BSIZE; -} - -struct entry* routing_get(struct nodeid* id) { - uint16_t baseIndex = base_bucket(id); - for(size_t i = baseIndex; i < baseIndex + RT_BSIZE; i++) { - if(!pTable->table[i].set) continue; - - if(memcmp(&pTable->table[i].id, id, sizeof(struct nodeid)) == 0) { - return &pTable->table[i]; - } - } - - return NULL; -} - -void routing_remove(struct nodeid* id) { - struct entry* entry = routing_get(id); - - entry->set = false; -} - -bool routing_interested(struct nodeid* id) { - uint16_t bucketIndex = prefix(&pTable->myID, id); - // The nodeid is the same as our own - if(bucketIndex == RT_IDBITS) { - return false; - } - - uint16_t baseIndex = base_bucket(id); - int8_t inBucketIndex = scan(baseIndex, id); - - if(inBucketIndex < 0) { - // The bucket either already contains the node, or it has no more space - return false; - } - - return true; -} - -// Offer the routing table a new node -bool routing_offer(struct nodeid* id, struct entry **dest) { - uint16_t bucketIndex = prefix(&pTable->myID, id); - // The nodeid is the same as our own - if(bucketIndex == RT_IDBITS) { - return false; - } - - uint16_t baseIndex = base_bucket(id); - int8_t inBucketIndex = scan(baseIndex, id); - - if(inBucketIndex < 0) { - // The bucket either already contains the node, or it has no more space - return false; - } - - struct entry* entry = &pTable->table[baseIndex + inBucketIndex]; - entry->set = true; - entry->id = *id; - - *dest = entry; - return true; -} - -struct item { - struct nodeid distance; - bool set; - uint16_t index; -}; -int compareItem(const void* a_v, const void* b_v) { - struct item* a = (struct item*)a_v; - struct item* b = (struct item*)b_v; - - // If either of the two are not set, the one that is set comes before the - // one that isn't. - if(!a->set || !b->set) return b->set - a->set; - - return memcmp(&a->distance, &b->distance, sizeof(struct nodeid)); -} - -size_t routing_closest(struct nodeid* needle, size_t n, struct entry** res) { - assert(n <= RT_SIZE); - static struct item items[RT_SIZE] = {0}; - for(uint16_t i = 0; i < RT_SIZE; i++) { - items[i].index = i; - } - - { - struct item* item; - struct entry* entry; - for(item = &items[0], entry = &pTable->table[0]; item < &items[RT_SIZE] && entry < &pTable->table[RT_SIZE]; item++, entry++){ - item->set = entry->set; - for(uint8_t j = 0; j < 5; j++) { - item->distance.inner[j] = entry->id.inner[j] ^ needle->inner[j]; - } - } - } - - // @PERFORMANCE: There's an algorithm known as quickselect which can select - // the top k elements from a list while only doing a partial sort. - // I imagine that would be more efficient than this full sort. - qsort(items, RT_SIZE, sizeof(struct item), compareItem); - - size_t read; - for(read = 0; read < n; read++) { - if(!items[read].set) - break; - res[read] = &pTable->table[items[read].index]; - } - - return read; -} - -void routing_oldest(struct entry** dest) { - *dest = NULL; - - for(struct entry* entry = pTable->table; entry < pTable->table+RT_SIZE; entry++){ - if(!entry->set) - continue; - - if(entry->expire == 0) - continue; - - if(*dest == NULL) { - *dest = entry; - continue; - } - - if(difftime((*dest)->expire, entry->expire) > 0.0) { - *dest = entry; - } - } -} - -void routing_status(int* filled, int* size, double* load_factor, size_t load_factor_len) { - *size = RT_SIZE; - - *filled = 0; - for(size_t i = 0; i < RT_SIZE; i++) { - if(pTable->table[i].set) - (*filled)++; - } - - int per_bucket = RT_SIZE / load_factor_len; - int overflow = RT_SIZE % load_factor_len; - struct entry* table_cursor = pTable->table; - for(int i = 0; i < load_factor_len; i++) { - int is_overflow = i < overflow; - for(int j = 0; j < per_bucket + is_overflow; j++) { - load_factor[i] += table_cursor->set; - table_cursor++; - } - load_factor[i] /= per_bucket + is_overflow; - } -} diff --git a/src/routing.h b/src/routing.h index bbb4c24..a435a94 100644 --- a/src/routing.h +++ b/src/routing.h @@ -46,3 +46,6 @@ void routing_status(int* filled, int* size, double* load_factor, size_t load_fac struct entry* routing_get(struct nodeid* id); void routing_remove(struct nodeid* self); + +struct nodeid rand_nodeid_in_bucket(struct nodeid *self, struct nodeid *other); +uint8_t prefix(struct nodeid* a, struct nodeid* b); diff --git a/test/proto.c b/test/proto.c index 49a77b9..c69a293 100644 --- a/test/proto.c +++ b/test/proto.c @@ -8,6 +8,56 @@ #define IP(a, b, c, d) htonl(a << 24 | b << 16 | c << 8 | d) +void test_create_first_ticket() { + time_t now = TOKEN_VTMO; + struct tokens tokens = {0}; + char hash[SHA256_BLOCK_SIZE] = {0}; + struct addr addr = (struct addr){.ip = IP(128,0,0,1), .port = 6881}; + struct addr addr2 = (struct addr){.ip = IP(128,0,0,2), .port = 6881}; + int rc; + + // First token create should create a new ticket + token_create(&tokens, now, &addr, hash); + + // It should be valid for the address it was issued for + rc = token_validate(&tokens, now, &addr, hash); + TEST_ASSERT_EQUAL(TOK_VALI, rc); + + // But not for some other addr + rc = token_validate(&tokens, now, &addr2, hash); + TEST_ASSERT_EQUAL(TOK_INVA, rc); + + // Time passes and our ticket becomes invalid for issuance + now += TOKEN_ITMO; + + char hash2[SHA256_BLOCK_SIZE]; + token_create(&tokens, now, &addr, hash2); + + // We should get a different token, since we have a new ticket + if(memcmp(hash, hash2, SHA256_BLOCK_SIZE) == 0) { + TEST_FAIL(); + } + + // Which should be valid + rc = token_validate(&tokens, now, &addr, hash2); + TEST_ASSERT_EQUAL(TOK_VALI, rc); + + // And so should the old one (since it's not timed out for validation) + rc = token_validate(&tokens, now, &addr, hash); + TEST_ASSERT_EQUAL(TOK_VALI, rc); + + // More time passes and the original token becomes invalid for validation + now += TOKEN_VTMO - TOKEN_ITMO; + + // Old token should now no longer be validated + rc = token_validate(&tokens, now, &addr, hash); + TEST_ASSERT_EQUAL(TOK_INVA, rc); + + // New token should + rc = token_validate(&tokens, now, &addr, hash2); + TEST_ASSERT_EQUAL(TOK_VALI, rc); +} + void test_begin_pings_bootstrap_node() { struct message outbuff[10] = {0}; @@ -48,6 +98,11 @@ void test_response_from_initial_probe() { TEST_ASSERT_EQUAL_PTR_MESSAGE(message_cursor, outbuff+1, "Sends one packet"); TEST_ASSERT_EQUAL(91, outbuff[0].payload_len); TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:ad2:id20:BBBBBBBBBBBBBBBBBBBB6:target20:", outbuff[0].payload, 43); + + struct nodeid target; + memcpy(&target, outbuff[0].payload+43, sizeof(struct nodeid)); + TEST_ASSERT_EQUAL_MEMORY_MESSAGE(&dht.self, &target, sizeof(struct nodeid), "Target should be our own id"); + TEST_ASSERT_EQUAL_CHAR_ARRAY("e1:q9:find_node1:t1:11:y1:qe", outbuff[0].payload+63, 28); // The queried node gets added to the routing table @@ -116,7 +171,7 @@ void test_ping() { TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+1); TEST_ASSERT_EQUAL(47, outbuff[0].payload_len); - TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:t2:aa1:y1:r1:rd2:id20:BBBBBBBBBBBBBBBBBBBBee", outbuff[0].payload, 47); + TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:rd2:id20:BBBBBBBBBBBBBBBBBBBBe1:t2:aa1:y1:re", outbuff[0].payload, 47); } void test_unknown_method() { @@ -143,7 +198,7 @@ void test_unknown_method() { TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+1); TEST_ASSERT_EQUAL(42, outbuff[0].payload_len); - TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:t2:aa1:y1:e1:eli204e14:Unknown Methodee", outbuff[0].payload, 42); + TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:eli204e14:Unknown Methode1:t2:aa1:y1:ee", outbuff[0].payload, 42); } void test_note_times_out() { @@ -164,7 +219,7 @@ void test_note_times_out() { // The node responds to the ping at t=5 { - char buff[] = "d1:y1:r1:t1:01:rd2:id20:aaaaaaaaaaaaaaaaaaaa5:nodes26:bbbbbbbbbbbbbbbbbbbb\xFF\xFF\xFF\xFF\x00\x00""ee"; + char buff[] = "d1:y1:r1:t1:01:rd2:id20:BB\x5F""aaaaaaaaaaaaaaaaa5:nodes26:bbbbbbbbbbbbbbbbbbbb\xFF\xFF\xFF\xFF\x00\x00""ee"; struct message* message_cursor = outbuff; proto_run(&dht, buff, sizeof(buff), (struct sockaddr_in*)&remote, remote_len, 5, &message_cursor, outbuff+2); } @@ -177,6 +232,12 @@ void test_note_times_out() { TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+2); TEST_ASSERT_EQUAL(91, outbuff[1].payload_len); TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:ad2:id20:BBBBBBBBBBBBBBBBBBBB6:target20:", outbuff[1].payload, 43); + + // The target should be in the same bucket as the queried node + struct nodeid target; + memcpy(&target, outbuff[1].payload+43, sizeof(struct nodeid)); + TEST_ASSERT_GREATER_THAN(19, prefix(&dht.self, &target)); + TEST_ASSERT_EQUAL_CHAR_ARRAY("e1:q9:find_node1:t1:01:y1:qe", outbuff[1].payload+63, 28); } @@ -199,6 +260,11 @@ void test_response_after_retry() { TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+1); TEST_ASSERT_EQUAL(91, outbuff[0].payload_len); TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:ad2:id20:BBBBBBBBBBBBBBBBBBBB6:target20:", outbuff[0].payload, 43); + + struct nodeid target; + memcpy(&target, outbuff[0].payload+43, sizeof(struct nodeid)); + TEST_ASSERT_EQUAL_MEMORY_MESSAGE(&dht.self, &target, sizeof(struct nodeid), "Target should be our own id"); + TEST_ASSERT_EQUAL_CHAR_ARRAY("e1:q9:find_node1:t1:01:y1:qe", outbuff[0].payload+63, 28); } @@ -367,10 +433,10 @@ void test_query_find_node() { TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+1); TEST_ASSERT_EQUAL(83, outbuff[0].payload_len); - TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:t2:aa1:y1:r1:rd2:id20:BBBBBBBBBBBBBBBBBBBB5:nodes26:aaaaaaaaaaaaaaaaaaaa", outbuff[0].payload, 75); - TEST_ASSERT_EQUAL_MEMORY(&((struct sockaddr_in*)&remote)->sin_addr.s_addr, outbuff[0].payload+75, 4); - TEST_ASSERT_EQUAL_MEMORY(&((struct sockaddr_in*)&remote)->sin_port, outbuff[0].payload+79, 2); - TEST_ASSERT_EQUAL_CHAR_ARRAY("ee", outbuff[0].payload+81, 2); + TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:rd2:id20:BBBBBBBBBBBBBBBBBBBB5:nodes26:aaaaaaaaaaaaaaaaaaaa", outbuff[0].payload, 62); + TEST_ASSERT_EQUAL_MEMORY(&((struct sockaddr_in*)&remote)->sin_addr.s_addr, outbuff[0].payload+62, 4); + TEST_ASSERT_EQUAL_MEMORY(&((struct sockaddr_in*)&remote)->sin_port, outbuff[0].payload+66, 2); + TEST_ASSERT_EQUAL_CHAR_ARRAY("e1:t2:aa1:y1:re", outbuff[0].payload+68, 15); } } @@ -383,7 +449,7 @@ void test_query_get_peers_have_one() { allocate_hashtable(); - struct dht dht; + struct dht dht = {0}; dht.self = (struct nodeid){.inner={0x42424242, 0x42424242, 0x42424242, 0x42424242, 0x42424242}}; routing_init(&dht.self); @@ -405,6 +471,7 @@ void test_query_get_peers_have_one() { now += 1; // Some other node then asks for peers + char token[SHA256_BLOCK_SIZE]; { struct sockaddr_in other; other.sin_family = AF_INET; @@ -419,16 +486,20 @@ void test_query_get_peers_have_one() { // We should have sent a response TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+1); - TEST_ASSERT_EQUAL(93, outbuff[0].payload_len); + TEST_ASSERT_EQUAL(125, outbuff[0].payload_len); char* cursor = outbuff[0].payload; - TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:t2:aa1:y1:r1:rd2:id20:BBBBBBBBBBBBBBBBBBBB5:token1:t5:nodes26:aaaaaaaaaaaaaaaaaaaa", cursor, 85); - cursor+=85; + TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:rd2:id20:BBBBBBBBBBBBBBBBBBBB5:nodes26:aaaaaaaaaaaaaaaaaaaa", cursor, 62); + cursor+=62; TEST_ASSERT_EQUAL_MEMORY(&((struct sockaddr_in*)&remote)->sin_addr.s_addr, cursor, 4); cursor+=4; TEST_ASSERT_EQUAL_MEMORY(&((struct sockaddr_in*)&remote)->sin_port, cursor, 2); cursor+=2; - TEST_ASSERT_EQUAL_CHAR_ARRAY("ee", cursor, 2); - cursor+=2; + TEST_ASSERT_EQUAL_CHAR_ARRAY("5:token32:", cursor, 10); + cursor+=10; + memcpy(token, cursor, SHA256_BLOCK_SIZE); + cursor+=SHA256_BLOCK_SIZE; + TEST_ASSERT_EQUAL_CHAR_ARRAY("e1:t2:aa1:y1:re", cursor, 15); + cursor+=15; } now += 1; @@ -440,14 +511,16 @@ void test_query_get_peers_have_one() { other.sin_port = htons(9090); // Node announces that it's a peer for that torrent - char buff[] = "d1:ad2:id20:abcdefghij012345678912:implied_porti1e9:info_hash20:aaaaaaaaaaaaaaaaaaaa4:porti1337e5:token1:te1:q13:announce_peer1:t2:aa1:y1:qe"; + char buff[] = "d1:ad2:id20:abcdefghij012345678912:implied_porti1e9:info_hash20:aaaaaaaaaaaaaaaaaaaa4:porti1337e5:token32: e1:q13:announce_peer1:t2:aa1:y1:qe"; + memcpy(buff+106, token, SHA256_BLOCK_SIZE); + dbg("PKT %s", buff); struct message* message_cursor = outbuff; proto_run(&dht, buff, sizeof(buff), (struct sockaddr_in*)&other, sizeof(other), now, &message_cursor, outbuff+2); TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+1); TEST_ASSERT_EQUAL(47, outbuff[0].payload_len); char* cursor = outbuff[0].payload; - TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:t2:aa1:y1:r1:rd2:id20:BBBBBBBBBBBBBBBBBBBBee", cursor, 47); + TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:rd2:id20:BBBBBBBBBBBBBBBBBBBBe1:t2:aa1:y1:re", cursor, 47); cursor+=47; } now += 1; @@ -466,9 +539,13 @@ void test_query_get_peers_have_one() { // We should have sent a response TEST_ASSERT_EQUAL_PTR(message_cursor, outbuff+1); - TEST_ASSERT_EQUAL(75, outbuff[0].payload_len); + TEST_ASSERT_EQUAL(107, outbuff[0].payload_len); char* cursor = outbuff[0].payload; - TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:t2:aa1:y1:r1:rd2:id20:BBBBBBBBBBBBBBBBBBBB5:token1:t6:valuesl6:\x80\x00\x00\x01\x23\x82""eee", cursor, 75); - cursor+=75; + TEST_ASSERT_EQUAL_CHAR_ARRAY("d1:rd2:id20:BBBBBBBBBBBBBBBBBBBB5:token32:", cursor, 42); + cursor+=42; + // Don't care what the token is + cursor+=32; + TEST_ASSERT_EQUAL_CHAR_ARRAY("6:valuesl6:\x80\x00\x00\x01\x23\x82""ee1:t2:aa1:y1:re", cursor, 33); + cursor+=33; } } diff --git a/test/query.c b/test/query.c index a3d3bb9..81fd09b 100644 --- a/test/query.c +++ b/test/query.c @@ -1,5 +1,6 @@ #include <unity.h> +#include "proto.h" #include "query.h" #include <string.h> @@ -8,7 +9,8 @@ #define IP(a, b, c, d) htonl(a << 24 | b << 16 | c << 8 | d) void test_malformed_empty() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -23,13 +25,14 @@ void test_malformed_empty() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EBADQ, rc); } void test_malformed_only_dict_start() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -44,13 +47,14 @@ void test_malformed_only_dict_start() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EBADQ, rc); } void test_malformed_empty_args_key() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -65,13 +69,14 @@ void test_malformed_empty_args_key() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EBADQ, rc); } void test_malformed_wrong_args_type() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -86,13 +91,14 @@ void test_malformed_wrong_args_type() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EBADQ, rc); } void test_malformed_empty_args() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -107,13 +113,14 @@ void test_malformed_empty_args() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EBADQ, rc); } void test_malformed_wrong_id_arg_type() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -128,13 +135,14 @@ void test_malformed_wrong_id_arg_type() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EBADQ, rc); } void test_malformed_wrong_id_length() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -149,13 +157,14 @@ void test_malformed_wrong_id_length() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EBADQ, rc); } void test_ping() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -170,7 +179,7 @@ void test_ping() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "ping", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(0, rc); TEST_ASSERT_EQUAL(29, response_cursor - response); @@ -178,7 +187,8 @@ void test_ping() { } void test_bad_method() { - + time_t now = 120; + struct tokens tokens = {0}; struct sockaddr_in src = { .sin_family = AF_INET, .sin_addr.s_addr = IP(255, 0, 0, 1), @@ -193,7 +203,7 @@ void test_bad_method() { char* response_cursor = response; char* response_end = response + sizeof(response); - int rc = handle_request(&self, "someWrongMethod", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); + int rc = handle_request(&self, &tokens, now, "someWrongMethod", (struct sockaddr*)&src, sizeof(src), packet, packet_len, &response_cursor, response_end-response_cursor); TEST_ASSERT_EQUAL(QUERY_EUNK, rc); } diff --git a/thirdparty/crypto-algorithms b/thirdparty/crypto-algorithms new file mode 160000 +Subproject cfbde48414baacf51fc7c74f275190881f037d3 |
