diff options
| author | Jesper Jensen <jesper@jnsn.dev> | 2023-07-23 12:15:34 +0200 |
|---|---|---|
| committer | Jesper Jensen <jesper@jnsn.dev> | 2025-04-12 10:21:20 +0200 |
| commit | 47d4780eb448f839fc6b0644135395f879080ba4 (patch) | |
| tree | 2c9a84bf45a4b8af5411331db50173fc5a34f630 /src/query.c | |
| parent | 3515701479fe04f73e9194ee3074457bb85030f6 (diff) | |
I don't remember
Diffstat (limited to 'src/query.c')
| -rw-r--r-- | src/query.c | 285 |
1 files changed, 284 insertions, 1 deletions
diff --git a/src/query.c b/src/query.c index 6012540..6ca6565 100644 --- a/src/query.c +++ b/src/query.c @@ -1,12 +1,14 @@ #include "query.h" #include "benc.h" #include "log.h" +#include "peers.h" #include <string.h> #include <errno.h> #include <assert.h> +#include <arpa/inet.h> -int handle_request(struct nodeid* self, const char* method, const char* packet, size_t packet_len, char** response, size_t response_len) { +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) { if(strcmp(method, "ping") == 0) { struct bcursor bcursor; struct benc_node stream[256]; @@ -173,6 +175,287 @@ int handle_request(struct nodeid* self, const char* method, const char* packet, *response += rc; assert(*response < end); } else if(strcmp(method, "get_peers") == 0) { + struct bcursor bcursor; + struct benc_node stream[256]; + bcur_open(&bcursor, packet, packet+packet_len, stream, 256); + + if(bcursor.readhead->type != BNT_DICT) { + err("Bad query: Packet is not a dict"); + return QUERY_EBADQ; + } + if(bcur_next(&bcursor, 1) < 0) { + err("Bad query: No token after outer dict start"); + return QUERY_EBADQ; + } + + if(bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"a"}, (const size_t[]){1}, 1) != 0) { + err("Bad query: No arguments to request"); + return QUERY_EBADQ; + } + bcur_next(&bcursor, 1); + if(bcursor.readhead->type != BNT_DICT) { + err("Bad query: Wrong value type for request"); + return QUERY_EBADQ; + } + // Skip the dict element + bcur_next(&bcursor, 1); + bool infohash_set = false; + struct infohash infohash; + while(bcursor.readhead->type != BNT_END) { + switch(bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"info_hash"}, (const size_t[]){9}, 1)) { + case 0: + // Skip the key + bcur_next(&bcursor, 1); + + if(bcursor.readhead->type != BNT_STRING) { + err("Bad query: Wrong value type for info_hash"); + return QUERY_EBADQ; + } + + if(bcursor.readhead->size != 20) { + err("Bad query: Incorrect target length"); + return QUERY_EBADQ; + } + + infohash_set = true; + memcpy(&infohash, bcursor.readhead->loc, 20); + + // Skip the value + bcur_next(&bcursor, 1); + break; + } + } + + if(!infohash_set) { + err("info_hash argument not provided"); + return QUERY_EBADQ; + } + + char* end = (*response) + response_len; + + int rc = snprintf(*response, end-*response, "d2:id20:"); + if(rc < 0) + return QUERY_EBADQ; + *response += rc; + assert(*response < end); + + memcpy(*response, self, sizeof(struct nodeid)); + *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); + + if(peers != NULL) { + rc = snprintf(*response, end-*response, "6:valuesl"); + if(rc < 0) + return QUERY_EBADQ; + *response += rc; + assert(*response < end); + + for(int i = 0; i < peers_len; i++) { + *(*response) = '6'; + *(*response+1) = ':'; + (*response) += 2; + memcpy(*response, &peers[i].ip, sizeof(uint32_t)); + *response += sizeof(uint32_t); // 4 + assert(*response < end); + memcpy(*response, &peers[i].port, sizeof(uint16_t)); + *response += sizeof(uint16_t); // 2 + assert(*response < end); + } + + (**response) = 'e'; + (*response)++; + assert(*response < end); + } else { + // If we didn't get any peers we send back the closest nodes + struct entry* closest[8]; + int found = routing_closest((struct nodeid*)&infohash, 8, closest); + + rc = snprintf(*response, end-*response, "5:nodes%d:", found*26); + if(rc < 0) + return QUERY_EBADQ; + *response += rc; + assert(*response < end); + + for(int i = 0; i < found; i++) { + memcpy(*response, &closest[i]->id, sizeof(struct nodeid)); + *response += sizeof(struct nodeid); // 20 + assert(*response < end); + memcpy(*response, &closest[i]->addr.ip, sizeof(uint32_t)); + *response += sizeof(uint32_t); // 4 + assert(*response < end); + memcpy(*response, &closest[i]->addr.port, sizeof(uint16_t)); + *response += sizeof(uint16_t); // 2 + assert(*response < end); + } + } + + rc = snprintf(*response, end-*response, "e"); + if(rc < 0) + return QUERY_EBADQ; + *response += rc; + assert(*response < end); + } else if(strcmp(method, "announce_peer") == 0) { + struct bcursor bcursor; + struct benc_node stream[256]; + bcur_open(&bcursor, packet, packet+packet_len, stream, 256); + + if(bcursor.readhead->type != BNT_DICT) { + err("Bad query: Packet is not a dict"); + return QUERY_EBADQ; + } + if(bcur_next(&bcursor, 1) < 0) { + err("Bad query: No token after outer dict start"); + return QUERY_EBADQ; + } + + if(bcur_find_key(&bcursor, (const enum benc_nodetype[]){BNT_STRING}, (const char*[]){"a"}, (const size_t[]){1}, 1) != 0) { + err("Bad query: No arguments to request"); + return QUERY_EBADQ; + } + bcur_next(&bcursor, 1); + if(bcursor.readhead->type != BNT_DICT) { + err("Bad query: Wrong value type for request"); + return QUERY_EBADQ; + } + // Skip the dict element + bcur_next(&bcursor, 1); + + bool implied_port = false; + + bool infohash_set = false; + struct infohash infohash; + + bool port_set = false; + uint16_t port; + + bool token_set = false; + char token; + + 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)) { + case 0: + // Skip the key + bcur_next(&bcursor, 1); + + if(bcursor.readhead->type != BNT_INT) { + err("Bad query: Wrong value type for implied_port"); + return QUERY_EBADQ; + } + + if(*bcursor.readhead->loc == '0') { + implied_port = false; + } else { + implied_port = true; + } + + // Skip the value + bcur_next(&bcursor, 1); + break; + case 1: + // Skip the key + bcur_next(&bcursor, 1); + + if(bcursor.readhead->type != BNT_STRING) { + err("Bad query: Wrong value type for info_hash"); + return QUERY_EBADQ; + } + + if(bcursor.readhead->size != 20) { + err("Bad query: Incorrect info_hash length"); + return QUERY_EBADQ; + } + + infohash_set = true; + memcpy(&infohash, bcursor.readhead->loc, 20); + + bcur_next(&bcursor, 1); + break; + case 2: + // Skip the key + bcur_next(&bcursor, 1); + + if(bcursor.readhead->type != BNT_INT) { + err("Bad query: Wrong value type for port"); + return QUERY_EBADQ; + } + + port_set = true; + port = strtol(bcursor.readhead->loc, NULL, 10); + + bcur_next(&bcursor, 1); + break; + case 3: + // Skip the key + bcur_next(&bcursor, 1); + + if(bcursor.readhead->type != BNT_STRING) { + err("Bad query: Wrong value type for token"); + return QUERY_EBADQ; + } + + if(bcursor.readhead->size != 1) { + err("Bad query: Incorrect token length"); + return QUERY_EBADQ; + } + + token_set = true; + token = *bcursor.readhead->loc; + + bcur_next(&bcursor, 1); + break; + } + } + + if(!infohash_set || (!implied_port && !port_set) || !token_set) { + err("Missing argument to query"); + 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(!implied_port) { + src_addr.port = htons(port); + } + add_peer(&infohash, &src_addr); + } + + // Write out the response + char* end = (*response) + response_len; + + int rc = snprintf(*response, end-*response, "d2:id20:"); + if(rc < 0) + return QUERY_EBADQ; + *response += rc; + assert(*response < end); + + memcpy(*response, self, sizeof(struct nodeid)); + *response += sizeof(struct nodeid); + assert(*response < end); + + (**response) = 'e'; + (*response)++; + assert(*response < end); + + return 0; } else { return QUERY_EUNK; } |
