summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-11-29 22:38:38 +0100
committerAnton Luka Šijanec <anton@sijanec.eu>2022-11-29 22:38:38 +0100
commit9047933b0ca588b1cd84c24a29898ef463f2ced9 (patch)
treed9c7379f78ac69e9a2de5d0754533a73f2afaae2
parentgrem spat, dht.c nepreverjen (diff)
downloadtravnik-9047933b0ca588b1cd84c24a29898ef463f2ced9.tar
travnik-9047933b0ca588b1cd84c24a29898ef463f2ced9.tar.gz
travnik-9047933b0ca588b1cd84c24a29898ef463f2ced9.tar.bz2
travnik-9047933b0ca588b1cd84c24a29898ef463f2ced9.tar.lz
travnik-9047933b0ca588b1cd84c24a29898ef463f2ced9.tar.xz
travnik-9047933b0ca588b1cd84c24a29898ef463f2ced9.tar.zst
travnik-9047933b0ca588b1cd84c24a29898ef463f2ced9.zip
-rw-r--r--src/dht.c189
1 files changed, 161 insertions, 28 deletions
diff --git a/src/dht.c b/src/dht.c
index 61a318e..6086f2f 100644
--- a/src/dht.c
+++ b/src/dht.c
@@ -21,8 +21,8 @@ time_t seconds () {
return tp.tv_sec;
}
-int family (struct in6_addr addr) {
- return memcmp("\0\0\0\0\0\0\0\0\0\0\xFF\xFF", addr.s6_addr, 12) ? AF_INET6 : AF_INET;
+int family (const char * addr) {
+ return memcmp("\0\0\0\0\0\0\0\0\0\0\xFF\xFF", addr, 12) ? AF_INET6 : AF_INET;
}
#define K 8
@@ -221,12 +221,17 @@ struct dht * dht_init (const struct bencoding * c) {
const struct bencoding * id = bpath(c, "id");
if (id & id->type & string && id->valuelen == 20)
memcpy(d->id, id->value, 20);
- bforeach (bpath(c, "bootstrap_nodes"), str) {
+ bforeach (bpath(c, "nodes"), str) {
struct sockaddr_in6 addr;
char remote[INET6_ADDRSTRLEN + 7];
strncpy(remote, str->value, str->valuelen);
- if (inet_pton(AF_INET6, remote, &addr) == 1)
- find_node(d, &addr, d->id); // NOTE02
+ char * port = strchr(remote, ':');
+ if (port) {
+ if (inet_pton(AF_INET6, remote, &addr) == 1) {
+ addr.sin6_port = htons(atoi(++port));
+ ping_node(d, &addr);
+ }
+ }
}
}
return d;
@@ -267,8 +272,31 @@ void dht_free (struct dht * d) {
* @return bencoding object, whose memory ownership is transfered to the caller, which must call bencoding_free() on it.
*/
-void persistent (const struct dht * d) {
-
+struct bencoding * persistent (const struct dht * d) {
+ struct bencoding * b = calloc(1, sizeof *b);
+ b->type = dict;
+ struct bencoding * id = calloc(1, sizeof *id);
+ id->type = string;
+ id->value = malloc(20);
+ id->valuelen = 20;
+ memcpy(id->value, d->id);
+ id->key = bstr(strdup("id"));
+ binsert(b, id);
+ struct bencoding * nodes = calloc(1, sizeof *nodes);
+ nodes->type = list;
+ struct bucket * bucket = d->buckets;
+ while (bucket) {
+ struct node * node = bucket->nodes;
+ while (node) {
+ char remote[INET6_ADDRSTRLEN + 7];
+ if (inet_ntop(AF_INET6, &node->addr, remote, sizeof node->addr))
+ binsert(nodes, bstr(strdup(remote)));
+ node = node->next;
+ }
+ bucket = bucket->next;
+ }
+ binsert(b, nodes);
+ return b;
}
/**
@@ -415,16 +443,24 @@ void replied (const struct dht * d, const char * id, const struct sockaddr_in6 *
}
/**
- * ping a node by sending a get_peers
+ * ping a raw node by sending a get_peers
*
* instead of sending a ping query, we send a find_node query. this gets us useful information of peers around our ID instead of just a blank ping reply. infolgedessen we don't have to actively search for our neighbour nodes, since we'll get them through pings anyways
*
+ * instead of sending a find_node for an ID close to ours, we could send a find_node for a random ID far from us. though those buckets will probably quickly be filled by torrent searches.
+ *
* @param d [in] library handle
- * @param n [in] node to ping
+ * @param a [in] address of node
*/
-void ping_node (struct dht * d, const struct node * n) {
- find_node(d, &n->addr, d->id); // see NOTE02
+void ping_node (const struct dht * d, const struct sockaddr_in6 * a) {
+ char target[20];
+ memcpy(target, d->id, 20);
+ if (target[19] & 1) // flip the last bit, so the other node doesn't just return
+ target[19] &= 0xFE; // our ID but K ids around it
+ else
+ target[19] |= 1;
+ find_node(d, a, target);
}
/**
@@ -459,6 +495,12 @@ void find_node (const struct dht * d, const struct sockaddr_in6 * a, const char
id->key = bstr(strdup("key"));
memcpy((id->value = malloc(20)), d->id, 20);
binsert(a, id);
+ struct bencoding * want = calloc(1, sizeof *want); // BEP-0032
+ want->key = bstr(strdup("want"));
+ want->type = list;
+ binsert(want, bstr(strdup("n4")));
+ binsert(want, bstr(strdup("n6")));
+ binsert(a, want);
struct bencoding * target = calloc(1, sizeof *target);
target->key = bstr(strdup("target"));
target->type = string;
@@ -561,20 +603,22 @@ int bucket_good (const struct dht * d, const struct bucket * b) {
}
/**
- * when we are sure that a node exists on a specific ip:port and we are unsure if the node is already in the routing table, we call this function, which makes a query to this node if it's a candidate for filling the routing table. this doesn't yet add it to the routing table, because we are unsure if it's a good node / can respond to queries. replied() is called if a node replied to our query.
+ * when we are sure that a node exists on a specific ip:port and we know it's port, but we are unsure if the node is already in the routing table, we call this function, which makes a query to this node if it's a candidate for filling the routing table. this doesn't yet add it to the routing table, because we are unsure if it's a good node / can respond to queries. replied() is called if a node replied to our query.
*
* @param d [in] library handle
* @param a [in] pointer to sockaddr of the node
+ * @param id [in] id of the node, 20 bytes is read from this address
*/
-void potential_node (const struct dht * d, const struct sockaddr_in6 * a) {
+void potential_node (const struct dht * d, const struct sockaddr_in6 * a, const char * id) {
struct bucket * bucket = d->buckets;
- if (family(a->sin6_addr) == AF_INET6)
+ if (family(a->sin6_addr.s6_addr) == AF_INET6)
bucket = b->buckets6;
- find(rid->value, &bucket, NULL);
+ if (find(id, &bucket, NULL))
+ return;
if (!bucket_good(bucket));
- find_node(d, a, d->id); // NOTE02: an alternative to searching for our
-} // ID would be to search for a random ID
+ ping_node(d, a);
+}
/**
* adds a torrent to a list of torrents
@@ -618,6 +662,47 @@ struct torrent * find_torrent (struct dht * d, const char * h) {
}
/**
+ * returns a dict containing nodes or nodes6 bencoding list (with a key) with compact nodes in the bucket. if exact node is found, only that one is in the list.
+ *
+ * @param d [in] library handle
+ * @param id [in] target node id, 20 bytes is read from this location
+ * @param f [in] address family, AF_INET or AF_INET6
+ * @return bencoding object whose memory ownership and free responsibility is transfered to the caller
+ */
+
+struct bencoding * nodes (const struct dht * d, const char * id, sa_family_t f) {
+ struct bencoding * nodes = calloc(1, sizeof *nodes);
+ nodes->type = list;
+ nodes->key = bstr(strdup(f == AF_INET ? "nodes" : "nodes6"));
+ binsert(r, nodes);
+ struct bucket * bucket = f == AF_INET ? d->buckets : d->bucket6;
+ struct node * found = find(id, &bucket, NULL);
+#define ADDRLEN(f) (f == AF_INET ? 4 : 16)
+ if (found) {
+ struct bencoding * compact = calloc(1, sizeof *compact);
+ compact->type = string;
+ compact->value = malloc((compact->valuelen = 20+ADDRLEN(f)+2));
+ memcpy(compact->value, found->id, 20);
+ memcpy(compact->value+20, found->addr.sin6_addr.s6_addr+(16-ADDRLEN(f)), ADDRLEN(f));
+ memcpy(compact->value+20+ADDRLEN(f), found->addr.sin6_port, 2);
+ binsert(nodes, compact);
+ } else {
+ struct node * node = bucket->nodes;
+ while (node) {
+ struct bencoding * compact = calloc(1, sizeof *compact);
+ compact->type = string;
+ compact->value = malloc((compact->valuelen = 20+ADDRLEN(f)+2));
+ memcpy(compact->value, found->id, 20);
+ memcpy(compact->value+20, found->addr.sin6_addr.s6_addr+(16-ADDRLEN(f)), ADDRLEN(f));
+ memcpy(compact->value+20+ADDRLEN(f), found->addr.sin6_port, 2);
+ binsert(nodes, compact);
+ node = node->next;
+ }
+ }
+ return nodes;
+}
+
+/**
* handles an incoming packet
*
* @param d [in] library handle
@@ -650,13 +735,12 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) {
char * qtype = "";
if (q && q->type & string)
qtype = q->value;
+ struct bencoding * rid = bpath(b, "a/id");
+ if (rid && rid->type & string && rid->valuelen == 20)
+ potential_node(d, &addr, rid->value);
switch (qtype[0]) {
case 'P': // ping
case 'p':
- struct bencoding * rid = bpath(bpath(b, "a"), "id");
- if (rid && rid->type & string && rid->valuelen == 20) {
- potential_node(d, rid->value, &addr);
- }
else { // see NOTE01
int len = b2json_length(b);
char j[len+1];
@@ -664,19 +748,17 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) {
j[len] = '\0';
L("%s did not send a valid id in %s", remote, j);
}
- struct bencoding * id = calloc(1, sizeof *d);
- id->type = dict;
+ struct bencoding * id = calloc(1, sizeof *id);
+ id->type = string;
id->key = bstr(strdup("id"));
- id->valuelen = 20;
- id->value = malloc(20);
- memcpy(id->value, d->id, 20);
- struct bencoding * r = calloc(1, sizeof *d);
+ memcpy((id->value = malloc((id->valuelen = 20))), d->id, 20);
+ struct bencoding * r = calloc(1, sizeof *r);
r->type = dict;
r->key = bstr(strdup("r"));
binsert(r, id);
struct bencoding * y = bstr(strdup("r"));
y->key = bstr(strdup("y"));
- struct bencoding * response = calloc(1, sizeof *r);
+ struct bencoding * response = calloc(1, sizeof *response);
response->type = dict;
binsert(response, y);
binsert(response, r);
@@ -684,6 +766,57 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) {
sendb(d, response, &addr, addrlen);
free_bencoding(response);
break;
+ case 'F': // find_node
+ case 'f':
+ struct bencoding * target = bpath(b, "a/target");
+ if (!target || !(target->type & string) || target->valuelen != 20)
+ break; // see NOTE01
+ struct bencoding * response = calloc(1, sizeof *response);
+ response->type = dict;
+ struct bencoding * y = bstr(strdup("r"));
+ y->key = bstr(strdup("y"));
+ binsert(response, y);
+ binsert(response, bclone(bpath(b, "r")));
+ struct bencoding * r = calloc(1, sizeof *r);
+ r->type = dict;
+ struct bencoding * id = calloc(1, sizeof *id);
+ id->type = string;
+ id->key = bstr(strdup("id"));
+ memcpy((id->value = malloc((id->valuelen = 20))), d->id, 20);
+ binsert(r, id);
+ binsert(response, r);
+ if (family(addr.sin6_addr.s6_addr) == AF_INET || bval(bpath("a/want"), "v4"))
+ binsert(response, nodes(d, target->value, AF_INET));
+ 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);
+ break;
+ case 'G': // get_peers
+ case 'g':
+ struct bencoding * hash = bpath(b, "a/info_hash");
+ if (!hash || !(hash->type & string) || target->valuelen != 20)
+ break; // see NOTE01
+ struct bencoding * response = calloc(1, sizeof *response);
+ response->type = dict;
+ struct benncoding * y = bstr(strdup("r"));
+ y->key = bstr(strdup("y"));
+ binsert(response, y);
+ binsert(response, bclone(bpath(b, "r")));
+ struct bencoding * r = calloc(1, sizeof *r);
+ r->type = dict;
+ struct bencoding * id = calloc(1, sizeof *id);
+ id->type = string;
+ id->key = bstr(strdup("id"));
+ memcpy((id->value = malloc((id->valuelen = 20))), d->id, 20);
+ binsert(r, id);
+ binsert(response, r);
+ if (family(addr.sin6_addr.s6_addr) == AF_INET || bval(bpath("a/want"), "v4"))
+ binsert(response, nodes(d, target->value, AF_INET));
+ if (family(addr.sin6_addr.s6_addr) == AF_INET6 || bval(bpath("a/want"), "v6")) {
+ binsert(response, nodes(d, target->value, AF_INET6));
+ // TODO: token, peers
+ sendb(d, response, &addr, addrlen);
+ break;
default: // see NOTE01
int len = b2json_length(b);
char json[len+1];