summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesper Jensen <jesper@jnsn.dev>2024-01-28 09:01:22 +0100
committerJesper Jensen <jesper@jnsn.dev>2025-04-12 10:21:20 +0200
commitcd622b745254baafa21adf4b1b724e079652321a (patch)
treeb1faf03808a5f8729cf8a69f971c00cb9d662683
parent47d4780eb448f839fc6b0644135395f879080ba4 (diff)
I don't know
-rw-r--r--Makefile9
-rw-r--r--src/main.c55
-rw-r--r--src/main.c.orig202
-rw-r--r--src/peers.c5
-rw-r--r--src/peers.h5
-rw-r--r--src/proto.c221
-rw-r--r--src/proto.c.orig718
-rw-r--r--src/proto.h45
-rw-r--r--src/proto.h.orig78
-rw-r--r--src/query.c56
-rw-r--r--src/query.h8
-rw-r--r--src/routing.c57
-rw-r--r--src/routing.c.orig267
-rw-r--r--src/routing.h3
-rw-r--r--test/proto.c113
-rw-r--r--test/query.c46
m---------thirdparty/crypto-algorithms0
17 files changed, 457 insertions, 1431 deletions
diff --git a/Makefile b/Makefile
index ebbf641..9ffc7e0 100644
--- a/Makefile
+++ b/Makefile
@@ -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 $<
diff --git a/src/main.c b/src/main.c
index 4a28a1d..8545bf4 100644
--- a/src/main.c
+++ b/src/main.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; 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; 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; 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