diff options
Diffstat (limited to '')
-rw-r--r-- | src/dht.c | 189 |
1 files changed, 161 insertions, 28 deletions
@@ -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]; |