diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dht.c | 70 |
1 files changed, 56 insertions, 14 deletions
@@ -212,6 +212,7 @@ struct dht { unsigned torrents_max; /**< max number of torrents that we are allowed to store */ unsigned peers_max; /**< max number of peers that we are allowed to store */ struct torrent * last_torrent; /**< to quickly go to the end of the doubly linked list of torrents is helpful when reaching torrents_max and needing to pop oldest */ + unsigned peers_per_torrent_max; /**< max number of peers to store per torrent - applies to ->type and !->type torrents */ }; /** @@ -244,6 +245,9 @@ struct dht * dht_init (const struct bencoding * c) { d->buckets = bucket_init(); d->buckets6 = bucket_init(); d->possible_torrent = &possible_torrent; + d->torrents_max = UINT_MAX; // this is hardcore - so many torrents makes LL traversal too slow + d->peers_max UINT_MAX; // there's no way there even are this many peers on the entire network at a time xDDDDDDDDDDD + d->peers_per_torrent_max = UINT_MAX; errno = 0; if (!d) return NULL; @@ -366,7 +370,7 @@ struct bencoding * persistent (const struct dht * d) { void token (const struct dht * d, unsigned * t, const char * addr) { struct AES_ctx aes; memcpy(t, addr, 16); - AES_init_ctx(&aes, dht->secret); + AES_init_ctx(&aes, d->secret); AES_ECB_encrypt(&aes, t); } @@ -545,6 +549,8 @@ void split (struct bucket * b) { void replied (const struct dht * d, const unsigned char * id, const struct sockaddr_in6 * addr) { struct bucket * b = d->buckets; + if (family(addr->sin6_addr.s6_addr) == AF_INET6) + b = d->buckets6; struct node * n; struct node * found = find(id, &b, &n); if (found) { @@ -600,7 +606,7 @@ void ping_node (const struct dht * d, const struct sockaddr_in6 * a) { target[19] &= 0xFE; // our ID but K ids around it else target[19] |= 1; - find_node(d, a, target); + find_node(d, a, target, NULL); } /** @@ -609,15 +615,18 @@ void ping_node (const struct dht * d, const struct sockaddr_in6 * a) { * @param d [in] handle * @param a [in] address of the remote node * @param query [in] 20 byte id we are querying + * @param t [in] token bencoding element (t key in dict). if NULL, a static token will be sent. ->key must be set. memory ownership is transfered away from the caller and the object is freed by this function. */ -void find_node (const struct dht * d, const struct sockaddr_in6 * a, const unsigned char * query) { +void find_node (const struct dht * d, const struct sockaddr_in6 * a, const unsigned char * query, struct bencoding * t) { struct bencoding * b = calloc(1, sizeof *b); b->type = dict; - struct bencoding * t = bstr(strdup("t@_a.si")); - t->key = bstr(strdup("t")); - t->value[2] = '4'; - t->type = string; + if (!t) { + t = bstr(strdup("t@_a.si")); + t->key = bstr(strdup("t")); + t->value[2] = '4'; + t->type = string; + } binsert(b, t); struct bencoding * y = bstr(strdup("q")); t->key = bstr(strdup("y")); @@ -753,7 +762,7 @@ int bucket_good (const struct dht * d, const struct bucket * b) { void potential_node (const struct dht * d, const struct sockaddr_in6 * a, const unsigned char * id) { struct bucket * bucket = d->buckets; if (family(a->sin6_addr.s6_addr) == AF_INET6) - bucket = b->buckets6; + bucket = d->buckets6; if (find(id, &bucket, NULL)) return; if (!bucket_good(bucket)); @@ -859,11 +868,19 @@ void remove_torrent (struct dht * d, struct torrent * t) { struct peer * add_peer (struct dht * d, struct torrent * t, struct peer * p) { struct peer * peer = torrent->peers; - while (peer) + unsigned i = 0; + while (peer) { if (!memcmp(&peer->addr, &addr, sizeof addr)) { - free(p); + peer_free(p); return peer; } + if (peer->next && !peer->next->next) + if (++i >= d->peers_per_torrent_max) { + d->peers--; + free_peer(peer->next); + peer->next = NULL; + } + } peer->next = torrent->peers; torrent->peers = peer; d->peers++; @@ -1001,6 +1018,7 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { if (family(addr.sin6_addr.s6_addr) == AF_INET6 || bval(bpath("a/want"), "v6")) { binsert(response, nodes(d, target->value, AF_INET6)); sendb(d, response, &addr, addrlen); + free_bencoding(response); break; case 'G': // get_peers case 'g': @@ -1047,6 +1065,7 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { token(d, (tok->value = malloc((tok->valuelen = 16))), addr.sin6_addr.s6_addr); binsert(r, tok); sendb(d, response, &addr, addrlen); + free_bencoding(response); break; case 'A': // announce case 'a': @@ -1071,7 +1090,6 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { struct bencoding * y = bstr(strdup("r")); y->key = bstr(strdup("y")); binsert(response, y); - sendb(response); // first we send confirmation, then we store struct torrent * torrent = calloc(1, sizeof *torrent); memcpy(torrent->hash, hash->value, 20); torrent = add_torrent(d, torrent); @@ -1080,20 +1098,44 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { if (bpath(b, "a/port") && !bpath(b, "a/implied_port") || !bpath(b, "a/implied_port")->intvalue) peer->addr.sin6_port = htons(bpath(b, "a/port")->intvalue); add_peer(d, torrent, peer); + sendb(response); + free_bencoding(response); break; default: // see NOTE01 int len = b2json_length(b); char json[len+1]; b2json(json, b); json[len] = '\0'; - L(dht->log, "%s sent an unknown query type: %s"); + L(d->log, "%s sent an unknown query type: %s"); break; } break; case 'R': case 'r': - // TODO - break; + struct bencoding * rid = bpath(b, "r/id"); + if (rid && rid->type & string && rid->valuelen == 20) + replied(d, rid->value, &addr); // since here I'm only really interested about nodes and values + struct bencoding * t = bpath(b, "t"); + if (!(t && t->type & string && t->valuelen == 20)) + break; + if (!(torrent = find_torrent(d, t->value))) + break; + if (!torrent->type) // yeet, l33t haxx0r tried to announce a torrent by sending a crafted response to a ficticios get_peers query + break; // this could enable the DHT node to be used as a DDoS spreader. uninterested torrents can't be updated this way + bforeach (bpath(b, "r/values"), p) { + if (!(p->type & string) || (p->valuelen != 6 && p->valuelen != 18)) + break; + struct peer * peer = calloc(1, sizeof *peer); + memcpy(peer->addr.sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF", 12); + peer->addr.sin6_port = *((uint16_t *) (peer->value + peer->valuelen-2)); + memcpy(peer->addr.sin6_addr+(p->valuelen == 6 ? 8 : 0), p->valuelen == 6 ? 4 : 16); + add_peer(d, torrent, peer); + } + bforeach (bpath(b, "r/nodes" /* haha subreddit */), n) { + if (!(n->type & string) || (n->valuelen != 4+2+20 && n->valuelen != 16+2+20)) + break; + // TODO nodes and nodes6 + } case 'E': case 'e': struct bdecoding * e = bpath(b, "e"); |