From ad9c2c45fa85f877fa0880269369e6dc9492a87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Sun, 1 Jan 2023 22:28:35 +0100 Subject: pridobivanje peerov deluje --- .gitignore | 1 + makefile | 11 +- misc/sends_to_weird_addresses.pcapng | Bin 0 -> 3252 bytes src/bencoding.c | 6 +- src/dht.c | 748 +++++++++++++++++++++++++---------- src/main.c | 62 ++- utils/midpoint.c | 71 ++++ 7 files changed, 666 insertions(+), 233 deletions(-) create mode 100644 misc/sends_to_weird_addresses.pcapng create mode 100644 utils/midpoint.c diff --git a/.gitignore b/.gitignore index c5bf36c..8e860cc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ core travnik.patch configfile.ben doc/ +possible_torrents.L diff --git a/makefile b/makefile index 59ee3d1..aba35cd 100644 --- a/makefile +++ b/makefile @@ -1,10 +1,17 @@ DESTDIR=/ CC=cc +MYCFLAGS=-Wall -Wextra -Wformat -pedantic -g -Isrc -Itmp -Itiny-AES-c +MYLDFLAGS=-lresolv default: mkdir -p tmp # -fsanitize-address - $(CC) -Wall -Wextra -Wformat -pedantic -g -Isrc -Itmp -Itiny-AES-c $(CFLAGS) src/main.c -otravnik -lm -lresolv $(LDFLAGS) + $(CC) $(MYCFLAGS) $(CFLAGS) src/main.c -otravnik $(MYLDFLAGS) $(LDFLAGS) + +utils: + $(CC) $(MYCFLAGS) $(CFLAGS) utils/midpoint.c -otmp/midpoint $(MYLDFLAGS) $(LDFLAGS) + $(CC) $(MYCFLAGS) $(CFLAGS) utils/bencoding.c -otmp/bencoding $(MYLDFLAGS) $(LDFLAGS) + $(CC) $(MYCFLAGS) $(CFLAGS) utils/dns.c -otmp/dns $(MYLDFLAGS) $(LDFLAGS) install: mkdir -p $(DESTDIR)/usr/bin/ @@ -21,4 +28,4 @@ prepare: valgrind: valgrind --error-exitcode=59 --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt $(CMD) -.PHONY: default install distclean clean prepare valgrind +.PHONY: default install distclean clean prepare valgrind utils diff --git a/misc/sends_to_weird_addresses.pcapng b/misc/sends_to_weird_addresses.pcapng new file mode 100644 index 0000000..afdb393 Binary files /dev/null and b/misc/sends_to_weird_addresses.pcapng differ diff --git a/src/bencoding.c b/src/bencoding.c index 66b43b3..b32e2ea 100644 --- a/src/bencoding.c +++ b/src/bencoding.c @@ -738,10 +738,8 @@ struct bencoding * bclone (struct bencoding * b) { c->child = bclone(b->child); c->child->parent = c; } - if (b->next) { - c->next = bclone(b->child); - c->next->parent = c; - } + if (b->next && (c->next = bclone(b->child)) && (c->next->parent = c)) + {} c->valuelen = b->valuelen; if (b->value) { c->value = malloc(c->valuelen+1); diff --git a/src/dht.c b/src/dht.c index b11580a..8cfe1ca 100644 --- a/src/dht.c +++ b/src/dht.c @@ -11,6 +11,7 @@ #include #include #include +#include #define ECB 1 #define AES128 1 #include @@ -28,6 +29,27 @@ int family (const unsigned char * addr) { #define K 8 +/** + * writes a hexadecimal representation of a binary string. does not NULL terminate destination. + * + * h and b may not overlap. period. + * + * @param h [out] destination (requires 2l space) + * @param b [in] source + * @param l [in] length of source + */ + +void bin2hex (char * h, const unsigned char * b, size_t l) { + for (size_t i = 0; i < l; i++) { + int ms = b[i] >> 4; + int ls = b[i] & 0xF; + assert(ms < 16); + assert(ls < 16); + *h++ = ms < 10 ? '0'+ms : 'a'+ms-10; + *h++ = ls < 10 ? '0'+ls : 'a'+ls-10; + } +} + /** * node representation */ @@ -50,7 +72,7 @@ struct node * node_init (void) { if (!n) return NULL; n->last_received = seconds(); - n->addr.sin6_family = AF_INET; + n->addr.sin6_family = AF_INET6; return n; } @@ -64,6 +86,23 @@ void node_free (struct node * n) { free(n); } +/** + * print node to stream, for debugging + * + * @param s [out] the stream to which to print to + * @param n [in] the node to be printed + */ + +void node_print (FILE * s, const struct node * n) { + char buf[41]; + buf[40] = '\0'; + bin2hex(buf, n->id, 20); + char remote[INET6_ADDRSTRLEN + 64]; + if (!inet_ntop(n->addr.sin6_family, n->addr.sin6_addr.s6_addr, remote, INET6_ADDRSTRLEN+7)) + snprintf(remote, sizeof remote, "(inet_ntop: %s)", strerror(errno)); + fprintf(s, "%s %s/%d unans=%d", buf, remote, ntohs(n->addr.sin6_port), n->unanswered); +} + /** * returns which 20 byte bytestring is closer with XOR metric to target 20 byte bytestring * @@ -131,6 +170,16 @@ struct peer { struct peer * next; }; +/** + * init a torrent peer + */ + +struct peer * peer_init () { + struct peer * p = calloc(1, sizeof *p); + p->addr.sin6_family = AF_INET6; + return p; +} + /** * free a torrent peer */ @@ -139,6 +188,20 @@ void peer_free (struct peer * p) { free(p); } +/** + * print a peer, for debugging purposes + * + * @param s [out] stream + * @param p [in] peer + */ + +void peer_print (FILE * s, const struct peer * p) { + char remote[INET6_ADDRSTRLEN + 64]; + if (!inet_ntop(p->addr.sin6_family, p->addr.sin6_addr.s6_addr, remote, INET6_ADDRSTRLEN+7)) + snprintf(remote, sizeof remote, "(inet_ntop: %s)", strerror(errno)); + fprintf(s, "%s:%d", remote, ntohs(p->addr.sin6_port)); +} + /** * reason why a torrent is in our database. 0 just means it is stored as a result of an announce */ @@ -191,6 +254,35 @@ int torrent_compare (const void * a, const void * b) { return memcmp(((const struct torrent *) a)->hash, ((const struct torrent *) b)->hash, 20); } +/** + * prints a torrent, for debugging purposes + * + * @param s [out] stream to which to write to + * @param t [in] the torrent to print + */ + +void torrent_print (FILE * s, const struct torrent * t) { + char buf[41]; + buf[40] = '\0'; + bin2hex(buf, t->hash, 20); + printf("magnet:?xt=urn:btih:%s\n\t**** PEERS ****\n", buf); + struct peer * p = t->peers; + while (p) { + fprintf(s, "\t"); + peer_print(s, p); + fprintf(s, "\n"); + p = p->next; + } + printf("\t**** NODES ****\n"); + struct node * n = t->nodes; + while (n) { + fprintf(s, "\t"); + node_print(s, n); + fprintf(s, "\n"); + n = n->next; + } +} + /** * handle for the library */ @@ -219,6 +311,53 @@ struct dht { unsigned txb; /**< statistics: total number of bytes in transmitted UDP bodies */ }; +/** + * prints a dht, for debugging + * + * @param s [out] stream + * @param d [in] dht + */ + +void dht_print (FILE * s, const struct dht * d) { + char buf[41]; + buf[40] = '\0'; + bin2hex(buf, d->id, 20); + char secret[17*2]; + secret[17*2+1] = '\0'; + bin2hex(secret, d->secret, 16); + fprintf(s, "id=%s socket=%d dl=%d t=%u p=%u tmax=%u pmax=%u p/t-max=%u runsec=%ld rxp=%u txp=%u rxb=%u txb=%u secret=%s\n", buf, d->socket, d->dl, d->torrents_num, d->peers_num, d->torrents_max, d->peers_max, d->peers_per_torrent_max, seconds()-d->time, d->rxp, d->txp, d->rxb, d->txb, secret); + printf("**** TORRENTS ****\n"); + struct torrent * t = d->torrents; + while (t) { + torrent_print(s, t); + t = t->next; + } + fprintf(s, "**** NODES ****\n"); + int nodes = 0; + for (int i = 0; i <= 1; i++) { + struct bucket * b = d->buckets; + if (i) + b = d->buckets6; + int buckets = 0; + while (b) { + buckets++; + bin2hex(buf, b->id, 20); + fprintf(s, "\tBUCKET id=%s\n", buf); + struct node * n = b->nodes; + while (n) { + nodes++; + fprintf(s, "\t"); + node_print(s, n); + fprintf(s, "\n"); + n = n->next; + } + b = b->next; + } + fprintf(s, "\t**** COUNT OF %s BUCKETS: %d\n", i ? "IPv4" : "IPV6", buckets); + } + fprintf(s, "**** COUNT OF NODES: %d\n", nodes); +} + /** * a dummy function that does nothing that is set as the default for possible_torrent in struct dht * @@ -238,7 +377,7 @@ void possible_torrent (struct dht * d __attribute__((unused)), const unsigned ch * @param ... [in] variable arguments to go with f */ -#define L(o, f, ...) do {char t[512]; time_t n = time(NULL); strftime(t, 512, "%c", localtime(&n)); fprintf(o, "[%s] %s()%s:%d: " f "\n", t, __func__, __FILE__, __LINE__ __VA_OPT__(,) __VA_ARGS__);} while (0) +#define L(Lo, Lf, ...) do {char Lt[512]; time_t Ln = time(NULL); strftime(Lt, 512, "%c", localtime(&Ln)); fprintf(Lo, "[%s] %s()%s:%d: " Lf "\n", Lt, __func__, __FILE__, __LINE__ __VA_OPT__(,) __VA_ARGS__);} while (0) /** * sends a bencoding object to the remote node. does not free the input bencoding. inserts a v key to the input bencoding. @@ -249,10 +388,10 @@ void possible_torrent (struct dht * d __attribute__((unused)), const unsigned ch */ void sendb (struct dht * d, struct bencoding * b, const struct sockaddr_in6 * a) { - char remote[INET6_ADDRSTRLEN + 7]; + char remote[INET6_ADDRSTRLEN + 64]; if (!inet_ntop(a->sin6_family, &a->sin6_addr, remote, INET6_ADDRSTRLEN+7)) snprintf(remote, sizeof remote, "(inet_ntop: %s)", strerror(errno)); - sprintf(remote+strlen(remote), ":%d", ntohs(((struct sockaddr_in6 *) a)->sin6_port)); + sprintf(remote+strlen(remote), "/%d", ntohs(((struct sockaddr_in6 *) a)->sin6_port)); struct bencoding * v = bstr(strdup("TK00")); v->key = bstr(strdup("v")); binsert(b, v); @@ -275,6 +414,8 @@ void sendb (struct dht * d, struct bencoding * b, const struct sockaddr_in6 * a) #pragma GCC diagnostic pop } +#define GENERIC_T "a@4" "a.si" /**< generic token that we send if we have no statekeeping to do - developer's email address */ + /** * sends a find_node query to a "raw node" * @@ -286,14 +427,13 @@ void sendb (struct dht * d, struct bencoding * b, const struct sockaddr_in6 * a) void find_node (struct dht * d, const struct sockaddr_in6 * addr, const unsigned char * query) { struct bencoding * b = calloc(1, sizeof *b); b->type = dict; - struct bencoding * t = calloc(1, sizeof *t); - memcpy((t->value = malloc(20)), query, (t->valuelen = 20)); + struct bencoding * t = bstr(strdup(GENERIC_T)); t->key = bstr(strdup("t")); t->type = string; binsert(b, t); struct bencoding * y = bstr(strdup("q")); - t->key = bstr(strdup("y")); - t->type = string; + y->key = bstr(strdup("y")); + y->type = string; binsert(b, y); struct bencoding * q = bstr(strdup("find_node")); q->key = bstr(strdup("q")); @@ -301,10 +441,11 @@ void find_node (struct dht * d, const struct sockaddr_in6 * addr, const unsigned binsert(b, q); struct bencoding * a = calloc(1, sizeof *a); a->type = dict; + a->key = bstr(strdup("a")); struct bencoding * id = calloc(1, sizeof *id); id->type = string; id->valuelen = 20; - id->key = bstr(strdup("key")); + id->key = bstr(strdup("id")); memcpy((id->value = malloc(20)), d->id, 20); binsert(a, id); struct bencoding * want = calloc(1, sizeof *want); // BEP-0032 @@ -316,8 +457,7 @@ void find_node (struct dht * d, const struct sockaddr_in6 * addr, const unsigned struct bencoding * target = calloc(1, sizeof *target); target->key = bstr(strdup("target")); target->type = string; - target->valuelen = 20; - memcpy((target->value = malloc(20)), query, 20); + memcpy((target->value = malloc(20)), query, (target->valuelen = 20)); binsert(a, target); binsert(b, a); sendb(d, b, addr); @@ -399,13 +539,15 @@ struct dht * dht_init (const struct bencoding * c) { if (id && id->type & string && id->valuelen == 20) memcpy(d->id, id->value, 20); bforeach (bpath(c, "nodes"), str) { - struct sockaddr_in6 addr; + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6 + }; char remote[INET6_ADDRSTRLEN + 7]; strncpy(remote, str->value, str->valuelen); char * port = strchr(remote, '/'); port[0] = '\0'; if (port) { - if (inet_pton(AF_INET6, remote, &addr) == 1) { + if (inet_pton(AF_INET6, remote, addr.sin6_addr.s6_addr) == 1) { addr.sin6_port = htons(atoi(++port)); ping_node(d, &addr); } @@ -483,18 +625,22 @@ struct bencoding * persistent (const struct dht * d) { struct bencoding * nodes = calloc(1, sizeof *nodes); nodes->type = list; nodes->key = bstr(strdup("nodes")); - 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.sin6_addr, remote, INET6_ADDRSTRLEN+7)) { - sprintf(remote+strlen(remote), "/%u", ntohs(node->addr.sin6_port)); - binsert(nodes, bstr(strdup(remote))); + for (int i = 0; i <= 1; i++) { + struct bucket * bucket = d->buckets; + if (i) + bucket = d->buckets6; + while (bucket) { + struct node * node = bucket->nodes; + while (node) { + char remote[INET6_ADDRSTRLEN + 7]; + if (inet_ntop(AF_INET6, &node->addr.sin6_addr, remote, INET6_ADDRSTRLEN+7)) { + sprintf(remote+strlen(remote), "/%u", ntohs(node->addr.sin6_port)); + binsert(nodes, bstr(strdup(remote))); + } + node = node->next; } - node = node->next; + bucket = bucket->next; } - bucket = bucket->next; } binsert(b, nodes); return b; @@ -591,48 +737,86 @@ struct node * find (const unsigned char * id, struct bucket ** b, struct node ** } if (n) *n = prev; - return node; + if (node && !memcmp((node)->id, id, 20)) + return node; + else + return NULL; } /** - * find the middle point of a bucket/or two 20 byte bytestrings + * add two 20 byte numbers * - * @param r [out] midpoint - * @param a [in] lower boundary - * @param b [in] upper boundary. if NULL, 0xffffffffffffffffffffffffffffffffffffffff is assumed + * @param a [io] result and first operand + * @param b [in] second operand */ -void midpoint (unsigned char * r, const unsigned char * a, const unsigned char * b) { - unsigned char up[20]; - for (int i = 0; i < 20; i++) - up[i] = 0xFF; - if (b) - memcpy(up, b, 20); +void add (unsigned char * a, const unsigned char * b) { unsigned carry = 0; for (int i = 19; i >= 0; i--) { - if (carry + (unsigned) up[i] < (unsigned) a[i]) { - r[i] = (unsigned) up[i] + 255 - (unsigned) a[i]; - carry = 1; - } else { - r[i] = (unsigned) up[i] - (unsigned) a[i]; - carry = 0; - if (r[i] & 1 && i != 19) - r[i+1] |= 1 << 7; - r[i] >>= 1; - } + unsigned cursum = (unsigned) a[i] + (unsigned) b[i] + carry; + carry = 0; + if (cursum > 255) { + carry = cursum / 256; + a[i] = cursum % 256; + } else + a[i] = cursum; } - carry = 0; +} + +/** + * subtract two 20 byte numbers + * + * @param a [io] result and first operand + * @param b [in] second operand + */ + +void subtract (unsigned char * a, const unsigned char * b) { + int carry = 0; for (int i = 19; i >= 0; i--) { - if (carry + (unsigned) r[i] + (unsigned) a[i] > 255) { - r[i] = r[i] + a[i] - 255; - carry = 1; - } else { - r[i] = r[i] + a[i]; - carry = 0; + int cur = (int) a[i] - ((int) b[i] + carry); + carry = 0; + while (cur < 0) { + cur += 256; + carry++; } + a[i] = cur; + } +} + +/** + * divide a 20 byte number with 2 + * + * @param a [io] result and operand + */ + +void divide (unsigned char * a) { + for (int i = 19; i >= 0; i--) { + a[i] >>= 1; + if (i && a[i-1] & 1) + a[i] |= 1 << 7; } } +/** + * find the middle point of a bucket/or two 20 byte bytestrings + * + * arguments must not overlap + * + * @param r [out] midpoint. caller must provide 20 bytes here + * @param a [in] lower boundary + * @param b [in] upper boundary. if NULL, 0xffffffffffffffffffffffffffffffffffffffff is assumed + */ + +void midpoint (unsigned char * r, const unsigned char * a, const unsigned char * b) { + for (int i = 0; i < 20; i++) + r[i] = 0xFF; + if (b) + memcpy(r, b, 20); + subtract(r, a); + divide(r); + add(r, a); +} + /** * splits a bucket * @@ -724,11 +908,14 @@ int bucket_good (const struct dht * d, const struct bucket * b) { if (in_bucket(d->id, b)) { struct node * n = b->nodes; while (n->next) { - if (distance(n->id, n->next->id) <= 2) // we allow holes of 1, since we don't actually store our ID in the bucket + if (distance(n->id, n->next->id) > 1) // we allow holes of 1, since we don't actually store our ID in the bucket return 0; // it's really impossible that a non-malicious node would by chance get so close + n = n->next; } + // L(d->log, "our bucket filled"); return 1; } + // L(d->log, "not our bucket and filled"); return 1; } else { if (node_count(b->nodes) < K) @@ -750,12 +937,20 @@ int bucket_good (const struct dht * d, const struct bucket * b) { * * if the node is found in a bucket, it's last received time and unanswered are updated * + * TODO: this could be refactored so that it reuses logic from potential_node, like done in replied_torrent_node() below + * * @param d [in] handle * @param id [in] node id that was received * @param addr [in] address from which the id was received */ void replied (const struct dht * d, const unsigned char * id, const struct sockaddr_in6 * addr) { + if (!memcmp(d->id, id, 20)) // WE COULDN'T'VE POSSIBLY REPLIED TO OURSELVES! + return; + char buf[41]; + buf[40] = '\0'; + bin2hex(buf, id, 20); + // L(d->log, "%s replied", buf); struct bucket * b = d->buckets; if (family(addr->sin6_addr.s6_addr) == AF_INET6) b = d->buckets6; @@ -764,10 +959,13 @@ void replied (const struct dht * d, const unsigned char * id, const struct socka if (found) { found->last_received = seconds(); found->unanswered = 0; + // L(d->log, "found"); return; } - if (bucket_good(d, b)) + if (bucket_good(d, b)) { + // L(d->log, "bucket good"); return; + } struct node * node = node_init(); memcpy(&node->addr, addr, sizeof *addr); memcpy(node->id, id, 20); @@ -784,12 +982,6 @@ void replied (const struct dht * d, const unsigned char * id, const struct socka return; } if (in_bucket(d->id, b)) { - struct node * n = b->nodes; - while (n->next) - if (distance(n->id, n->next->id) != 1) // at least one gap, if not, then we have the innermost bucket full - goto ok; - return; - ok: node_free(node); split(b); replied(d, id, addr); // find bucket again @@ -797,43 +989,46 @@ void replied (const struct dht * d, const unsigned char * id, const struct socka } /** - * 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. + * when we are sure that a node exists on a specific ip:port and we know it's id, 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. * * see NOTE02 - * - * if any of d or id is NULL, it's assumed we don't have this node. this is used for bootstrapping - * + * * @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 (struct dht * d, const struct sockaddr_in6 * a, const unsigned char * id) { - if (!id || !d) - goto j; // just do it + if (!memcmp(d->id, id, 20)) // we are not a potential node of ourselves + return; struct bucket * bucket = d->buckets; if (family(a->sin6_addr.s6_addr) == AF_INET6) bucket = d->buckets6; if (find(id, &bucket, NULL)) return; - if (!bucket_good(d, bucket)) { - j: + if (!bucket_good(d, bucket)) ping_node(d, a); - } } +#define MAXT 15 /**< see NOTE04 */ + /** * find a torrent based on hash * + * NOTE04: full length 20 byte binary hash can't be stored in "t" because some nodes' software discards t if it's longer than MAXT bytes + * + * that's why this function accepts shorter lengths to compare. + * * @param d [in] the library handle - * @param h [in] a 20 byte infohash + * @param h [in] an l-byte infohash + * @param l [in] number of bytes of infohash to compare - for truncated hashes. full hash is 20 bytes long. * @return pointer to torrent or NULL */ -struct torrent * find_torrent (struct dht * d, const unsigned char * h) { +struct torrent * find_torrent (struct dht * d, const unsigned char * h, int l) { struct torrent * t = d->torrents; while (t) { - if (!memcmp(t->hash, h, 20)) + if (!memcmp(t->hash, h, l)) return t; t = t->next; } @@ -891,7 +1086,7 @@ void oom (struct dht * d) { */ struct torrent * add_torrent (struct dht * d, struct torrent * t) { - struct torrent * found = find_torrent(d, t->hash); + struct torrent * found = find_torrent(d, t->hash, 20); if (found) { found->type |= t->type; torrent_free(t); @@ -921,27 +1116,174 @@ struct torrent * add_torrent (struct dht * d, struct torrent * t) { struct peer * add_peer (struct dht * d, struct torrent * t, struct peer * p) { struct peer * peer = t->peers; - unsigned i = 0; while (peer) { if (!memcmp(&peer->addr, &p->addr, sizeof p->addr)) { peer_free(p); return peer; } - if (peer->next && !peer->next->next) - if (++i >= d->peers_per_torrent_max) { - d->peers_num--; - peer_free(peer->next); - peer->next = NULL; - } + peer = peer->next; } - peer->next = t->peers; - t->peers = peer; + p->next = t->peers; + t->peers = p; d->peers_num++; if (d->peers_num >= d->peers_max) oom(d); return peer; } +/** + * sends a get_peers query to a raw node + * + * @param d [in] handle + * @param a [in] address + * @param q [in] 20 byte hash we query for + */ + +void get_peers (struct dht * d, const struct sockaddr_in6 * addr, const unsigned char * q) { + struct bencoding * b = calloc(1, sizeof *b); + b->type = dict; + struct bencoding * t = calloc(1, sizeof *t); + memcpy((t->value = malloc(MAXT)), q, (t->valuelen = MAXT)); + t->key = bstr(strdup("t")); + t->type = string; + binsert(b, t); + struct bencoding * y = bstr(strdup("q")); + y->key = bstr(strdup("y")); + y->type = string; + binsert(b, y); + struct bencoding * q_elem = bstr(strdup("get_peers")); + q_elem->key = bstr(strdup("q")); + q_elem->type = string; + binsert(b, q_elem); + struct bencoding * a = calloc(1, sizeof *a); + a->key = bstr(strdup("a")); + a->type = dict; + struct bencoding * id = calloc(1, sizeof *id); + id->type = string; + id->valuelen = 20; + id->key = bstr(strdup("id")); + 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 * info_hash = calloc(1, sizeof *info_hash); + info_hash->key = bstr(strdup("info_hash")); + info_hash->type = string; + memcpy((info_hash->value = malloc(20)), q, (info_hash->valuelen = 20)); + binsert(a, info_hash); + binsert(b, a); + sendb(d, b, addr); + free_bencoding(b); +} + +/** + * called from compact() on response to get_peers, this function sends a get_peers to a node if it would improve the node pool of a torrent + * + * @param d [in] library handle. if NULL, get_peers is not sent and it's expected that caller will insert a new node into torrent (because one good will be removed if the potential node is closer and there's no bad node and nodelist is full) + * @param t [in] torrent + * @param a [in] address of node + * @param i [in] 20 byte id of node + * @return the position to which the new node should be written. the caller, if overwrites the pointed memory with this address, must update ->next of inserted node to the value that this address pointed to before + */ + +struct node ** potential_torrent_node (struct dht * d, struct torrent * t, const struct sockaddr_in6 * a, const unsigned char * i) { + if (!t->nodes) { + if (d) + get_peers(d, a, t->hash); + return &t->nodes; + } + int l = 0; + struct node * n = t->nodes; + struct node ** rs = NULL; // return if enough space + struct node ** farthest = &t->nodes; + struct node * prev = NULL; + while (n) { + l++; + if (node_grade(n) == bad) { + if (prev) + prev->next = n->next; + else + t->nodes = n->next; + node_free(n); + l--; + if (prev) + n = prev->next; + else + n = t->nodes; + continue; + } + int cp = memcmp(n->id, i, 20); + if (!cp) + return NULL; + if (!n->next) + rs = &n->next; + else { + int cn = memcmp(i, n->next->id, 20); + if (!cn) + return NULL; + if (cp == 1 && cn == -1) + rs = &n->next; + } + if (!closer(n->id, (*farthest)->id, t->hash)) { + if (prev) + farthest = &t->nodes; + else + farthest = &prev->next; + } + prev = n; + n = n->next; + } + if (l < K) { + if (d) + get_peers(d, a, t->hash); + return rs; + } + if (!closer(i, (*farthest)->id, t->hash)) { + if (!d) { + node_free(*farthest); + *farthest = prev; + if (d) + get_peers(d, a, t->hash); + return farthest; + } + } + return NULL; +} + +/** + * called on a response to get_peers, the node that responded is added to a list of good nodes of a torrent if it's a potential torrent node, if it's already in the list, it's stats are updated. + * + * note that torrent nodelists have no housekeeping like the routing table has with refresh(), so bad nodes are removed only in potential_torrent_node. + * + * @param t [in] torrent + * @param a [in] address of node + * @param i [in] node id + */ + +void replied_torrent_node (struct torrent * t, const struct sockaddr_in6 * a, const unsigned char * i) { + struct node * existing = t->nodes; + int r; + while (existing && (r = memcmp(existing->id, i, 20)) < 0) + existing = existing->next; + if (!r) { + existing->last_received = seconds(); + existing->unanswered = 0; + return; + } + struct node ** dst = potential_torrent_node(NULL, t, a, i); + if (!dst) + return; + struct node * n = node_init(); + memcpy(n->id, i, 20); + memcpy(&n->addr, a, sizeof n->addr); + n->next = *dst; + *dst = n; +} + /** * parses a compact node description in either ipv4 or ipv6 from nodes or nodes6 and invoke actions. call this for every string in nodes and nodes6 array in incoming response packets. * @@ -954,46 +1296,54 @@ struct peer * add_peer (struct dht * d, struct torrent * t, struct peer * p) { */ void compact (struct dht * d, const char * value, int len, struct torrent * t) { - if (len != 4+2+20 || len != 16+2+20) { + if (len != 4+2+20 && len != 16+2+20) { L(d->log, "received packet contained an invalid compact node"); return; } - struct node * node = node_init(); - memcpy(node->addr.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xFF\xFF", 12); - node->addr.sin6_port = *((uint16_t *) (value + len-2)); - memcpy(node->addr.sin6_addr.s6_addr+(len == 4+2+20 ? 8 : 0), value + 20, len == 4+2+20 ? 4 : 16); - memcpy(node->id, value, 20); - potential_node(d, &node->addr, node->id); // NOTE02 at the beginning, a lot of packets will be sent, since every reply of potential_node will generate K replies. naively this would generate an exponentially increasing number of packets, in increasing powers of 8 (8**n). to prevent an absolute resource hog, this is only done when node would be useful and would contribute to the routing table - if (t) { - int i = 0; - struct node ** replaceable = NULL; - struct node ** farthest = &t->nodes; - struct node ** index = &t->nodes; - while (*index) { - i++; - if (node_grade(*index) == bad) - replaceable = index; - if (!closer((*index)->id, (*farthest)->id, t->hash)) - farthest = index; - index = &(*index)->next; - } - if (i <= K) { - node->next = t->nodes; - t->nodes = node->next; - } else if (replaceable) { - node->next = (*replaceable)->next; - node_free(*replaceable); - *replaceable = node; - find_node(d, &node->addr, t->hash); - } else if (!closer(node->id, (*farthest)->id, t->hash)) { - node->next = (*farthest)->next; - node_free(*farthest); - *farthest = node; - find_node(d, &node->addr, t->hash); + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6 + }; + memcpy(addr.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xFF\xFF", 12); + addr.sin6_port = *((uint16_t *) (value + len-2)); + memcpy(addr.sin6_addr.s6_addr+(len == 4+2+20 ? 12 : 0), value + 20, len == 4+2+20 ? 4 : 16); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-sign" + potential_node(d, &addr, value); // NOTE02 at the beginning, a lot of packets will be sent, since every reply of potential_node will generate K replies. naively this would generate an exponentially increasing number of packets, in increasing powers of 8 (8**n). to prevent an absolute resource hog, this is only done when node would be useful and would contribute to the routing table + if (t) + potential_torrent_node(d, t, &addr, value); +#pragma GCC diagnostic pop + /* + if (t) { + int i = 0; + struct node ** replaceable = NULL; + struct node ** farthest = &t->nodes; + struct node ** index = &t->nodes; + while (*index) { + i++; + if (node_grade(*index) == bad) + replaceable = index; + if (!closer((*index)->id, (*farthest)->id, t->hash)) + farthest = index; + index = &(*index)->next; + } + if (i <= K) { + node->next = t->nodes; + t->nodes = node->next; + } else if (replaceable) { + node->next = (*replaceable)->next; + node_free(*replaceable); + *replaceable = node; + find_node(d, &node->addr, t->hash); + } else if (!closer(node->id, (*farthest)->id, t->hash)) { + node->next = (*farthest)->next; + node_free(*farthest); + *farthest = node; + find_node(d, &node->addr, t->hash); + } else + node_free(node); } else node_free(node); - } else - node_free(node); + */ } /** @@ -1009,7 +1359,7 @@ struct bencoding * nodes (const struct dht * d, const unsigned char * id, sa_fam struct bencoding * nodes = calloc(1, sizeof *nodes); nodes->type = list; nodes->key = bstr(strdup(f == AF_INET ? "nodes" : "nodes6")); - struct bucket * bucket = f == AF_INET ? d->buckets : d->buckets6; + struct bucket * bucket = (f == AF_INET ? d->buckets : d->buckets6); struct node * found = find(id, &bucket, NULL); #define ADDRLEN(f) (f == AF_INET ? 4 : 16) if (found) { @@ -1026,9 +1376,9 @@ struct bencoding * nodes (const struct dht * d, const unsigned char * id, sa_fam 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); + memcpy(compact->value, node->id, 20); + memcpy(compact->value+20, node->addr.sin6_addr.s6_addr+(16-ADDRLEN(f)), ADDRLEN(f)); + memcpy(compact->value+20+ADDRLEN(f), &node->addr.sin6_port, 2); binsert(nodes, compact); node = node->next; } @@ -1048,13 +1398,12 @@ struct bencoding * nodes (const struct dht * d, const unsigned char * id, sa_fam void announce_peer (struct dht * d, const struct sockaddr_in6 * addr, struct bencoding * t, const unsigned char * h) { struct bencoding * b = calloc(1, sizeof *b); b->type = dict; - struct bencoding * t_elem = bstr(strdup("a@4_.si")); + struct bencoding * t_elem = bstr(strdup(GENERIC_T)); t_elem->key = bstr(strdup("t")); - t_elem->value[3] = 'a'; binsert(b, t_elem); struct bencoding * y = bstr(strdup("q")); - t->key = bstr(strdup("y")); - t->type = string; + y->key = bstr(strdup("y")); + y->type = string; binsert(b, y); struct bencoding * q = bstr(strdup("announce_peer")); q->key = bstr(strdup("q")); @@ -1065,7 +1414,7 @@ void announce_peer (struct dht * d, const struct sockaddr_in6 * addr, struct ben struct bencoding * id = calloc(1, sizeof *id); id->type = string; id->valuelen = 20; - id->key = bstr(strdup("key")); + id->key = bstr(strdup("id")); memcpy((id->value = malloc(20)), d->id, 20); binsert(a, id); struct bencoding * want = calloc(1, sizeof *want); // BEP-0032 @@ -1117,15 +1466,23 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { struct bencoding * b = bdecode(pkt, len, replace); struct bencoding * v = bpath(b, "v"); char * node_ver = ""; - char remote[INET_ADDRSTRLEN + INET6_ADDRSTRLEN + 7 + (v && v->type & string) ? v->valuelen : 0]; + char remote[INET_ADDRSTRLEN + INET6_ADDRSTRLEN + 64 + (v && v->type & string ? v->valuelen : 0)]; if (!inet_ntop(addr.sin6_family, &addr.sin6_addr, remote, INET6_ADDRSTRLEN+7+INET_ADDRSTRLEN)) { snprintf(remote, sizeof remote, "(inet_ntop: %s)", strerror(errno)); - sprintf(remote+strlen(remote), ":%d", ntohs(addr.sin6_port)); + sprintf(remote+strlen(remote), "/%d", ntohs(addr.sin6_port)); } if (v && v->type & string) { node_ver = v->value; sprintf(remote+strlen(remote), "-%s", node_ver); } + if (b) { + int len = b2json_length(b); + char out[len+1]; + char * end = b2json(out, b); + *end = '\0'; + assert(out+len == end); + L(d->log, "handle(%s): %s", remote, out); + } struct bencoding * y = bpath(b, "y"); char * msg_type = ""; if (y && y->type & string) @@ -1187,6 +1544,7 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { binsert(response, y); binsert(response, bclone(bpath(b, "t"))); r = calloc(1, sizeof *r); + r->key = bstr(strdup("r")); r->type = dict; id = calloc(1, sizeof *id); id->type = string; @@ -1228,6 +1586,7 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { binsert(response, y); binsert(response, bclone(bpath(b, "t"))); r = calloc(1, sizeof *r); + r->key = bstr(strdup("r")); r->type = dict; id = calloc(1, sizeof *id); id->type = string; @@ -1249,21 +1608,23 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpointer-sign" - struct torrent * torrent = find_torrent(d, hash->value); + struct torrent * torrent = find_torrent(d, hash->value, 20); #pragma GCC diagnostic pop - struct peer * peer = torrent->peers; - struct bencoding * values = calloc(1, sizeof *values); - values->type = list; - while (peer) { // TODO implement peer preference: prefer sending peers that responded to us - if (family(peer->addr.sin6_addr.s6_addr) == family(addr.sin6_addr.s6_addr)) { // possible - struct bencoding * value = calloc(1, sizeof *value); - memcpy((value->value = malloc((value->valuelen = ADDRLEN(family(peer->addr.sin6_addr.s6_addr))+2))), peer->addr.sin6_addr.s6_addr, ADDRLEN(family(peer->addr.sin6_addr.s6_addr))); - memcpy(value->value+ADDRLEN(family(peer->addr.sin6_addr.s6_addr)), &peer->addr.sin6_port, 2); - binsert(values, value); // possible stack overflow if there are a lot of peers, see limit in bdecode() wrapper - } // TODO add a random IP address for plausible deniability - peer = peer->next; + if (torrent) { + struct peer * peer = torrent->peers; + struct bencoding * values = calloc(1, sizeof *values); + values->type = list; + while (peer) { // TODO implement peer preference: prefer sending peers that responded to us + if (family(peer->addr.sin6_addr.s6_addr) == family(addr.sin6_addr.s6_addr)) { // possible + struct bencoding * value = calloc(1, sizeof *value); + memcpy((value->value = malloc((value->valuelen = ADDRLEN(family(peer->addr.sin6_addr.s6_addr))+2))), peer->addr.sin6_addr.s6_addr, ADDRLEN(family(peer->addr.sin6_addr.s6_addr))); + memcpy(value->value+ADDRLEN(family(peer->addr.sin6_addr.s6_addr)), &peer->addr.sin6_port, 2); + binsert(values, value); // possible stack overflow if there are a lot of peers, see limit in bdecode() wrapper + } // TODO add a random IP address for plausible deniability + peer = peer->next; + } + binsert(r, values); } - binsert(r, values); struct bencoding * tok = calloc(1, sizeof *tok); tok->type = string; tok->key = bstr(strdup("token")); @@ -1305,7 +1666,7 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { torrent = calloc(1, sizeof *torrent); memcpy(torrent->hash, hash->value, 20); torrent = add_torrent(d, torrent); - peer = calloc(1, sizeof *peer); + struct peer * peer = calloc(1, sizeof *peer); memcpy(&peer->addr, &addr, sizeof 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); @@ -1337,26 +1698,37 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { struct torrent * torrent = NULL; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpointer-sign" - if ((t && t->type & string && t->valuelen == 20) && memcmp(d->id, t->value, 20) && (torrent = find_torrent(d, t->value)) && torrent->type) { + if (t && t->type & string && t->valuelen == MAXT) { + if ((torrent = find_torrent(d, t->value, MAXT)) && torrent->type) { #pragma GCC diagnostic pop - 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.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xFF\xFF", 12); - peer->addr.sin6_port = *((uint16_t *) (p->value + p->valuelen-2)); - memcpy(peer->addr.sin6_addr.s6_addr+(p->valuelen == 6 ? 8 : 0), p->value, p->valuelen == 6 ? 4 : 16); - add_peer(d, torrent, peer); + if (rid && rid->type & string && rid->valuelen == 20) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-sign" + replied_torrent_node(torrent, &addr, rid->value); +#pragma GCC diagnostic pop + } + bforeach (bpath(b, "r/values"), p) { + if (!(p->type & string) || (p->valuelen != 6 && p->valuelen != 18)) + break; + struct peer * peer = peer_init(); + memcpy(peer->addr.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xFF\xFF", 12); + peer->addr.sin6_port = *((uint16_t *) (p->value + p->valuelen-2)); + memcpy(peer->addr.sin6_addr.s6_addr+(p->valuelen == 6 ? 12 : 0), p->value, p->valuelen == 6 ? 4 : 16); + add_peer(d, torrent, peer); + } + if (torrent->type & announce) + announce_peer(d, &addr, bclone(bpath(b, "r/token")), torrent->hash); } - bforeach (bpath(b, "r/nodes" /* haha subreddit */), n) - if (n->type & string) - compact(d, n->value, n->valuelen, torrent); - bforeach(bpath(b, "r/nodes6"), n) - if (n->type & string) - compact(d, n->value, n->valuelen, torrent); - if (torrent->type & announce) - announce_peer(d, &addr, bclone(bpath(b, "r/token")), torrent->hash); } + struct bencoding * nodes = bpath(b, "r/nodes"); + struct bencoding * nodes6 = bpath(b, "r/nodes6"); + // L(d->log, "r/nodes length: %zu, r/nodes6 length: %zu", nodes ? nodes->valuelen : 0, nodes6 ? nodes6->valuelen : 0); + if (nodes && nodes->type & string && !(nodes->valuelen % 26)) + for (unsigned i = 0; i < nodes->valuelen; i += 26) + compact(d, nodes->value+i, 26, torrent); + if (nodes6 && nodes6->type & string && !(nodes6->valuelen % 38)) + for (unsigned i = 0; i < nodes6->valuelen; i += 38) + compact(d, nodes6->value+i, 38, torrent); break; case 'E': case 'e': @@ -1429,7 +1801,7 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { }; memcpy(a.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xFF\xFF", 12); memcpy(a.sin6_addr.s6_addr+12, rr.rdata, 4); - potential_node(NULL, &a, NULL); + ping_node(d, &a); break; case 16: if (!inet_ntop(AF_INET6, rr.rdata, address, INET6_ADDRSTRLEN+INET_ADDRSTRLEN+7)) @@ -1441,7 +1813,7 @@ void handle (struct dht * d, char * pkt, int len, struct sockaddr_in6 addr) { .sin6_port = *((uint16_t *) pkt) }; memcpy(aaaa.sin6_addr.s6_addr, rr.rdata, 16); - potential_node(NULL, &a, NULL); + ping_node(d, &aaaa); break; default: // SRV if (rr.rdlength < 3*2+3) // . indicates @@ -1517,55 +1889,6 @@ int refresh (struct bucket * b) { return nrgood; } -/** - * sends a get_peers query to a raw node - * - * @param d [in] handle - * @param a [in] address - * @param q [in] 20 byte hash we query for - */ - -void get_peers (struct dht * d, const struct sockaddr_in6 * addr, const unsigned char * q) { - struct bencoding * b = calloc(1, sizeof *b); - b->type = dict; - struct bencoding * t = calloc(1, sizeof *t); - memcpy((t->value = malloc(20)), q, (t->valuelen = 20)); - t->key = bstr(strdup("t")); - t->type = string; - binsert(b, t); - struct bencoding * y = bstr(strdup("q")); - t->key = bstr(strdup("y")); - t->type = string; - binsert(b, y); - struct bencoding * q_elem = bstr(strdup("get_peers")); - q_elem->key = bstr(strdup("q")); - q_elem->type = string; - binsert(b, q_elem); - struct bencoding * a = calloc(1, sizeof *a); - a->type = dict; - struct bencoding * id = calloc(1, sizeof *id); - id->type = string; - id->valuelen = 20; - 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 * info_hash = calloc(1, sizeof *info_hash); - info_hash->key = bstr(strdup("info_hash")); - info_hash->type = string; - info_hash->valuelen = 20; - memcpy((info_hash->value = malloc(20)), q, 20); - binsert(a, info_hash); - binsert(b, a); - sendb(d, b, addr); - free_bencoding(b); -} - /** * does periodic work for the library, called every 13 minutes * @@ -1621,6 +1944,8 @@ void periodic (struct dht * d) { int sent = 0; while (n) { sent++; + n->unanswered++; + n->last_sent = seconds(); get_peers(d, &n->addr, t->hash); n = n->next; } @@ -1630,6 +1955,8 @@ void periodic (struct dht * d) { struct node * n = b->nodes; \ while (sent < K && n) { \ sent++; \ + n->unanswered++; \ + n->last_sent = seconds(); \ get_peers(d, &n->addr, t->hash); \ n = n->next; \ }} @@ -1642,6 +1969,8 @@ void periodic (struct dht * d) { n = b->nodes; while (sent < K && n) { sent++; + n->unanswered++; + n->last_sent = seconds(); get_peers(d, &n->addr, t->hash); n = n->next; } @@ -1668,7 +1997,6 @@ void periodic (struct dht * d) { */ void work (struct dht * d) { - L(d->log, "work()"); char packet[65536]; struct sockaddr_in6 addr; socklen_t addrlen = sizeof addr; diff --git a/src/main.c b/src/main.c index c14af38..f613e4e 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,7 @@ #include int samomor = 0; int periodično = 0; +int sigusr1 = 0; void handler (int s) { switch (s) { case SIGINT: @@ -21,8 +22,17 @@ void handler (int s) { case SIGALRM: periodično++; break; + case SIGUSR1: + sigusr1++; + break; } } +void found_torrent (struct dht * d __attribute__((unused)), const unsigned char * h) { + char buf[41]; + bin2hex(buf, h, 20); + buf[40] = '\0'; + L(stdout, "magnet:?xt=urn:btih:%s", buf); +} int main (int argc, char ** argv) { int r = 0; struct dht * dht = NULL; @@ -36,25 +46,44 @@ int main (int argc, char ** argv) { error_at_line(2, errno, __FILE__, __LINE__, "sigaction(SIGINT)"); if (sigaction(SIGTERM, &sigact, NULL) == -1) error_at_line(3, errno, __FILE__, __LINE__, "sigaction(SIGTERM)"); + if (sigaction(SIGUSR1, &sigact, NULL) == -1) + error_at_line(4, errno, __FILE__, __LINE__, "sigaction(SIGUSR1)"); + struct itimerval itimerval = { + .it_interval = { + .tv_sec = 13*60 + } + }; + if (setitimer(ITIMER_REAL, &itimerval, NULL)) + error_at_line(5, errno, __FILE__, __LINE__, "setitimer"); + sigset_t sigset; + if (sigemptyset(&sigset) == -1) + error_at_line(6, errno, __FILE__, __LINE__, "sigemptyset"); + if (sigaddset(&sigset, SIGUSR1) == -1) + error_at_line(7, errno, __FILE__, __LINE__, "sigaddset(SIGUSR1)"); + if (sigaddset(&sigset, SIGALRM) == -1) + error_at_line(8, errno, __FILE__, __LINE__, "sigaddset(SIGALRM)"); + if (sigprocmask(SIG_UNBLOCK, &sigset, NULL) == -1) + error_at_line(9, errno, __FILE__, __LINE__, "sigprocmask"); if (argc != 1+1) - error_at_line(4, 0, __FILE__, __LINE__, "%s configfile.ben", S0(argv[0])); + error_at_line(10, 0, __FILE__, __LINE__, "%s configfile.ben > possible_torrents.L", S0(argv[0])); int cf = open(argv[1], O_RDWR | O_CLOEXEC | O_CREAT, 00664); if (cf == -1) - error_at_line(5, errno, __FILE__, __LINE__, "open(%s)", argv[1]); + error_at_line(11, errno, __FILE__, __LINE__, "open(%s)", argv[1]); struct stat statbuf; if (fstat(cf, &statbuf) == -1) { error_at_line(0, errno, __FILE__, __LINE__, "fstat(cf, &statbuf)"); - r = 6; + r = 12; goto r; } char * cfr = NULL; if (statbuf.st_size && !(cfr = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, cf, 0))) { error_at_line(0, errno, __FILE__, __LINE__, "mmap(NULL, %ld, PROT_READ, MAP_SHARED, cf, 0)", statbuf.st_size); - r = 7; + r = 13; goto r; } struct bencoding * config = bdecode(cfr, statbuf.st_size, replace); dht = dht_init(config); + dht->possible_torrent = found_torrent; free_bencoding(config); struct torrent * torrent = calloc(1, sizeof *torrent); memcpy(torrent->hash, "\xdd\x82\x55\xec\xdc\x7c\xa5\x5f\xb0\xbb\xf8\x13\x23\xd8\x70\x62\xdb\x1f\x6d\x1c", 20); @@ -65,17 +94,15 @@ int main (int argc, char ** argv) { .fd = dht->socket, .events = POLLIN }; - struct itimerval itimerval = { - .it_interval = { - .tv_sec = 13*60 - } - }; - setitimer(ITIMER_REAL, &itimerval, NULL); w: - while (poll(&pollfd, 1, -1) == 1) { + while (poll(&pollfd, 1, -1) == 1) work(dht); - } if (errno == EINTR) { + if (sigusr1) { + sigusr1 = 0; + dht_print(stdout, dht); + goto w; + } if (periodično) { periodično = 0; work(dht); @@ -83,11 +110,11 @@ w: } if (!samomor) { error_at_line(0, errno, __FILE__, __LINE__, "poll"); - r = 108; + r = 114; } } else { error_at_line(0, errno, __FILE__, __LINE__, "poll"); - r = 109; + r = 115; goto r; } config = persistent(dht); @@ -95,18 +122,18 @@ w: dht = NULL; if (cfr && munmap(cfr, statbuf.st_size) == -1) { error_at_line(0, errno, __FILE__, __LINE__, "munmap(cf, %ld)", statbuf.st_size); - r = 110; + r = 116; goto r; } cfr = NULL; if (ftruncate(cf, (statbuf.st_size = bencode_length(config))) == -1) { error_at_line(0, errno, __FILE__, __LINE__, "ftruncate(cf, %ld)", statbuf.st_size); - r = 111; + r = 117; goto r; } if (!(cfr = mmap(NULL, statbuf.st_size, PROT_WRITE, MAP_SHARED, cf, 0))) { error_at_line(0, errno, __FILE__, __LINE__, "mmap(NULL, %ld, PROT_READ, MAP_SHARED, cf, 0)", statbuf.st_size); - r = 112; + r = 118; goto r; } bencode(cfr, config); @@ -121,5 +148,6 @@ w: error_at_line(0, errno, __FILE__, __LINE__, "munmap(cf, %ld)", statbuf.st_size); if (close(cf) == -1) error_at_line(0, errno, __FILE__, __LINE__, "close(cf)"); + L(stderr, "exiting cleanly with status %d\n", r); return r; } diff --git a/utils/midpoint.c b/utils/midpoint.c new file mode 100644 index 0000000..1a2e6f3 --- /dev/null +++ b/utils/midpoint.c @@ -0,0 +1,71 @@ +#include +#include +#define S0(x) (x ? x : "") + +/** + * converts a hexadecimal string to bytes + * + * b and h may not overlap, unless they are the same address + * + * @param b [out] array of bytes to write to with capacity l + * @param h [in] array of hex to read from with 2l hex digits + * @param l [in] length of output array + */ + +void hex2bin (unsigned char * b, const char * h, int l) { + for (int i = 0; i < l; i++) { + char ms = *h++; + char ls = *h++; + b[i] = (ms >= 'a' ? ms - 'a' + 10 : (ms >= 'A' ? ms - 'A' + 10 : ms - '0')) << 4; + b[i] |= (ls >= 'a' ? ls - 'a' + 10 : (ls >= 'A' ? ls - 'A' + 10 : ls - '0')); + } +} + +int main (int argc, char ** argv) { + if (argc < 3) + error_at_line(1, 0, __FILE__, __LINE__, "%s [b]", S0(argv[0])); + if (argv[1][0] == 'b' || argv[1][0] == 'B') { + unsigned char a[strlen(argv[2])/2+1]; + a[strlen(argv[2])/2] = '\0'; + hex2bin(a, argv[2], strlen(argv[2])/2); + if (strlen(argv[2]) != 40 || (argv[2] && strlen(argv[2]) != 40)) { + printf("%s\n", a); + return 0; + } + return 0; + } + if (strlen(argv[2]) != 40) + error_at_line(3, 0, __FILE__, __LINE__, "strlen(a) != 40 && !bin"); + if (argv[1][0] == 'd' || argv[1][0] == 'D') { + unsigned char a[20]; + hex2bin(a, argv[2], 20); + divide(a); + char out[41]; + out[40] = '\0'; + bin2hex(out, a, 20); + printf("%s\n", out); + return 0; + } + if (!argv[3]) + error_at_line(2, 0, __FILE__, __LINE__, "!b && !bin && !divide"); + if (strlen(argv[3]) != 40) + error_at_line(3, 0, __FILE__, __LINE__, "strlen(b) != 40 && !bin && !divide"); + unsigned char a[20]; + unsigned char b[20]; + hex2bin(a, argv[2], 20); + hex2bin(b, argv[3], 20); + char out[41]; + out[40] = '\0'; + unsigned char r[20]; + if (argv[1][0] == 'a' || argv[1][0] == 'A') { + memcpy(r, a, 20); + add(r, b); + } else if (argv[1][0] == 's' || argv[1][0] == 'S') { + memcpy(r, a, 20); + subtract(r, b); + } else { + midpoint(r, a, b); + } + bin2hex(out, r, 20); + printf("%s\n", out); +} -- cgit v1.2.3