summaryrefslogtreecommitdiffstats
path: root/src/dht.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dht.c')
-rw-r--r--src/dht.c70
1 files changed, 56 insertions, 14 deletions
diff --git a/src/dht.c b/src/dht.c
index e787203..b1b1fa4 100644
--- a/src/dht.c
+++ b/src/dht.c
@@ -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");