diff --git a/dht-example.c b/dht-example.c index fc3caea..c0b0d47 100644 --- a/dht-example.c +++ b/dht-example.c @@ -78,21 +78,79 @@ const unsigned char hash[20] = { interesting happens. Right now, it only happens when we get a new value or when a search completes, but this may be extended in future versions. */ static void -callback(void *closure, - int event, - const unsigned char *info_hash, - const void *data, size_t data_len) +main_callback(void *closure, + int event, + const unsigned char *info_hash, + const void *data, size_t data_len) { - if(event == DHT_EVENT_SEARCH_DONE) - printf("Search done.\n"); - else if(event == DHT_EVENT_SEARCH_DONE6) - printf("IPv6 search done.\n"); - else if(event == DHT_EVENT_VALUES) - printf("Received %d values.\n", (int)(data_len / 6)); - else if(event == DHT_EVENT_VALUES6) - printf("Received %d IPv6 values.\n", (int)(data_len / 18)); - else - printf("Unknown DHT event %d.\n", event); + printf("dht-example: "); + switch(event) { + case DHT_EVENT_SEARCH_DONE: + printf("IPv4 search done"); + break; + case DHT_EVENT_SEARCH_DONE6: + printf("IPv6 search done"); + break; + case DHT_EVENT_VALUES: + printf("received IPv4 search results (%d peers)", (int)(data_len / 6)); + break; + case DHT_EVENT_VALUES6: + printf("received IPv6 search results (%d peers)", (int)(data_len / 18)); + break; + default: + printf("unknown DHT event %d", event); + } + printf("\n"); +} + +/* Log callback. */ +static void +log_callback(const struct timeval *time, int type, const char *format, va_list ap) +{ + char *types; + char *cols; + switch(type) { + case DHT_LOG_TYPE_DEBUG: + types = "DEBUG"; + cols = "\e[1;30m"; + break; + case DHT_LOG_TYPE_INFO: + types = "INFO"; + cols = "\e[1;37m"; + break; + case DHT_LOG_TYPE_WARN: + types = "WARN"; + cols = "\e[1;33m"; + break; + case DHT_LOG_TYPE_ERROR: + types = "ERROR"; + cols = "\e[1;31m"; + break; + default: + types = "UNKNOWN"; + cols = NULL; + } + fprintf(stderr, "%s[%ld] [%s] ", (cols ? cols : ""), time->tv_sec, types); + vfprintf(stderr, format, ap); + fprintf(stderr, "%s%s", (cols ? "\e[0m" : ""), (strcmp(format, "\n") == 0 ? "" : "\n")); + fflush(stderr); +} + +/* Print statistics. */ +static void +print_stats() +{ + int buckets4, good4, dubious4, total4; + int buckets6, good6, dubious6, total6; + dht_stats(AF_INET, &buckets4, &good4, &dubious4, &total4); + dht_stats(AF_INET6, &buckets6, &good6, &dubious6, &total6); + + printf("\n\e[1m"); + printf("Statistics:\n"); + printf("buckets4: %3i, good4: %3i, dubious4: %3i, total4: %3i\n", buckets4, good4, dubious4, total4); + printf("buckets6: %3i, good6: %3i, dubious6: %3i, total6: %3i\n", buckets6, good6, dubious6, total6); + printf("buckets: %3i, good: %3i, dubious: %3i, total: %3i\n", buckets4+buckets6, good4+good6, dubious4+dubious6, total4+total6); + printf("\e[0m\n"); } static unsigned char buf[4096]; @@ -258,10 +316,9 @@ main(int argc, char **argv) i++; } - /* If you set dht_debug to a stream, every action taken by the DHT will - be logged. */ + /* Set log callback to receive log messages */ if(!quiet) - dht_debug = stdout; + dht_set_log_callback(log_callback); /* We need an IPv4 and an IPv6 socket, bound to a stable port. Rumour has it that uTorrent likes you better when it is the same as your @@ -395,9 +452,9 @@ main(int argc, char **argv) if(rc > 0) { buf[rc] = '\0'; rc = dht_periodic(buf, rc, (struct sockaddr*)&from, fromlen, - &tosleep, callback, NULL); + &tosleep, main_callback, NULL); } else { - rc = dht_periodic(NULL, 0, NULL, 0, &tosleep, callback, NULL); + rc = dht_periodic(NULL, 0, NULL, 0, &tosleep, main_callback, NULL); } if(rc < 0) { if(errno == EINTR) { @@ -416,28 +473,24 @@ main(int argc, char **argv) idea to reannounce every 28 minutes or so. */ if(searching) { if(s >= 0) - dht_search(hash, 0, AF_INET, callback, NULL); + dht_search(hash, 0, AF_INET, main_callback, NULL); if(s6 >= 0) - dht_search(hash, 0, AF_INET6, callback, NULL); + dht_search(hash, 0, AF_INET6, main_callback, NULL); searching = 0; } /* For debugging, or idle curiosity. */ if(dumping) { dht_dump_tables(stdout); + print_stats(); dumping = 0; } } - { - struct sockaddr_in sin[500]; - struct sockaddr_in6 sin6[500]; - int num = 500, num6 = 500; - int i; - i = dht_get_nodes(sin, &num, sin6, &num6); - printf("Found %d (%d + %d) good nodes.\n", i, num, num6); - } + /* Print final stats. */ + print_stats(); + /* Shutdown. */ dht_uninit(); return 0; diff --git a/dht.c b/dht.c index 62ec649..50baf59 100644 --- a/dht.c +++ b/dht.c @@ -125,6 +125,8 @@ extern const char *inet_ntop(int, const void *, char *, socklen_t); struct node { unsigned char id[20]; + unsigned char v[4]; /* client identifier and version info */ + int have_v; /* indicates wether v is set or not */ struct sockaddr_storage ss; int sslen; time_t time; /* time of last message received */ @@ -293,6 +295,8 @@ struct parsed_message { unsigned char values6[PARSE_VALUES6_LEN]; unsigned short values6_len; unsigned short want; + unsigned char v[4]; + int have_v; }; static int parse_message(const unsigned char *buf, int buflen, @@ -341,39 +345,187 @@ static time_t expire_stuff_time; static time_t token_bucket_time; static int token_bucket_tokens; -FILE *dht_debug = NULL; +/* Log incoming messages. */ +#ifndef DHT_LOG_INCOMING_MESSAGES +#define DHT_LOG_INCOMING_MESSAGES 0 +#endif + +/* Log outgoing messages. */ +#ifndef DHT_LOG_OUTGOING_MESSAGES +#define DHT_LOG_OUTGOING_MESSAGES 0 +#endif + +/* Mask socket addresses when logging (e.g. for screenshots). */ +#ifndef DHT_LOG_MASK_ADDRESSES +#define DHT_LOG_MASK_ADDRESSES 0 +#endif +/* Log callback. */ +static dht_log_callback_t *dht_log_callback = NULL; + +/* Set log callback. */ +void +dht_set_log_callback(dht_log_callback_t* callback) +{ + dht_log_callback = callback; +} + +/* Log macros. */ +#define debugf(format, ...) printlf(DHT_LOG_TYPE_DEBUG, format, ##__VA_ARGS__) +#define infof(format, ...) printlf(DHT_LOG_TYPE_INFO, format, ##__VA_ARGS__) +#define warnf(format, ...) printlf(DHT_LOG_TYPE_WARN, format, ##__VA_ARGS__) +#define errorf(format, ...) printlf(DHT_LOG_TYPE_ERROR, format, ##__VA_ARGS__) + +/* Log message (printlf = print log formatted). */ #ifdef __GNUC__ - __attribute__ ((format (printf, 1, 2))) + __attribute__ ((format(printf, 2, 3))) #endif static void -debugf(const char *format, ...) +printlf(int type, const char *format, ...) { - va_list args; - va_start(args, format); - if(dht_debug) - vfprintf(dht_debug, format, args); - va_end(args); - if(dht_debug) - fflush(dht_debug); + if(dht_log_callback) { + va_list ap; + va_start(ap, format); + dht_gettimeofday(&now, NULL); + (*dht_log_callback)(&now, type, format, ap); + va_end(ap); + } } -static void -debug_printable(const unsigned char *buf, int buflen) +/* Convert address family to string. */ +const char* +af_to_str(int af) { - int i; - if(dht_debug) { - for(i = 0; i < buflen; i++) - putc(buf[i] >= 32 && buf[i] <= 126 ? buf[i] : '.', dht_debug); + switch(af) { + case AF_INET: + return "AF_INET"; + case AF_INET6: + return "AF_INET6"; + default: + errno = EAFNOSUPPORT; + return ""; } } -static void -print_hex(FILE *f, const unsigned char *buf, int buflen) +/* Convert address family to IP version string. */ +const char* +af_to_ivs(int af) { - int i; - for(i = 0; i < buflen; i++) - fprintf(f, "%02x", buf[i]); + switch(af) { + case AF_INET: + return "IPv4"; + case AF_INET6: + return "IPv6"; + default: + errno = EAFNOSUPPORT; + return ""; + } +} + +/* Convert socket address to string. */ +/* String length: '[' + length of IPv6 address string (46, includes + '\0') + ']' + ':' + 5 characters for port -> 54 characters. */ +char sa_str[1 + INET6_ADDRSTRLEN + 1 + 1 + 5]; +const char* +sa_to_str(const struct sockaddr *sa) +{ + if(!sa) { + errno = EINVAL; + return ""; + } + + switch(sa->sa_family) { + case AF_INET: { +#if (DHT_LOG_MASK_ADDRESSES == 0) + struct sockaddr_in *sinp = (struct sockaddr_in*)sa; + char ipstr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &sinp->sin_addr, ipstr, sizeof(ipstr)); + snprintf(sa_str, sizeof(sa_str), "%s:%u", ipstr, ntohs(sinp->sin_port)); + return sa_str; +#else + return "xxx.xxx.xxx.xxx:xxxxx"; +#endif + } + case AF_INET6: { +#if (DHT_LOG_MASK_ADDRESSES == 0) + struct sockaddr_in6* sinp6 = (struct sockaddr_in6*)sa; + char ipstr6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &sinp6->sin6_addr, ipstr6, sizeof(ipstr6)); + snprintf(sa_str, sizeof(sa_str), "[%s]:%u", ipstr6, ntohs(sinp6->sin6_port)); + return sa_str; +#else + return "[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:xxxxx"; +#endif + } + default: + errno = EAFNOSUPPORT; + return ""; + } +} + +/* Convert byte array to printable string. */ +/* String length: 2048 bytes + '\0' -> 2049 characters. */ +char ba_prn_str[2049 + 1]; +const char* +ba_to_str(const unsigned char *ba, int balen) +{ + if(!ba || balen < 0) { + errno = EINVAL; + return ""; + } + + int size = (balen < sizeof(ba_prn_str) ? balen : sizeof(ba_prn_str)-1); + memcpy(ba_prn_str, ba, size); + for(int i = 0; i < size; i++) { + if(ba_prn_str[i] < 32 || ba_prn_str[i] > 126) + ba_prn_str[i] = '.'; + } + ba_prn_str[size] = '\0'; + return ba_prn_str; +} + +/* Convert byte array to hex string. */ +/* String length: 2048 bytes x 2 characters + '\0' -> 4097 characters. */ +char ba_hex_str[2048*2 + 1]; +const char* +ba_to_hex(const unsigned char *ba, int balen) +{ + if(!ba) { + errno = EINVAL; + return ""; + } + + //const char* hex_chr = "0123456789ABCDEF"; + const char* hex_chr = "0123456789abcdef"; + for(int i=0,j=0; i < balen && j < sizeof(ba_hex_str)-2; i++) { + ba_hex_str[j++] = hex_chr[ (ba[i]>>4) & 0x0F ]; + ba_hex_str[j++] = hex_chr[ ba[i] & 0x0F ]; + } + ba_hex_str[balen*2] = '\0'; + return ba_hex_str; +} + +/* Convert ID of bucket/node to hex string. */ +static const char* +id_to_hex(const unsigned char *id) +{ + return ba_to_hex(id, 20); +} + +/* Convert v of node to string. */ +/* String length: 2 characters for client id + '-' + 3 characters for major + version + '-' + 3 characters for minor version + '\0' -> 11 characters */ +char v_str[11]; +static const char* +v_to_str(const unsigned char *v) +{ + if(!v) { + errno = EINVAL; + return ""; + } + + snprintf(v_str, sizeof(v_str), "%c%c-%03u-%03u", v[0], v[1], v[2], v[3]); + return v_str; } static int @@ -458,6 +610,13 @@ common_bits(const unsigned char *id1, const unsigned char *id2) return 8 * i + j; } +/* Determine distance between two ids. */ +static int +distance(const unsigned char *id1, const unsigned char *id2) +{ + return 160 - common_bits(id1, id2); +} + /* Determine whether id1 or id2 is closer to ref */ static int xorcmp(const unsigned char *id1, const unsigned char *id2, @@ -641,7 +800,7 @@ send_cached_ping(struct bucket *b) if(b->cached.ss_family == 0) return 0; - debugf("Sending ping to cached node.\n"); + debugf("Sending ping to cached node %s", sa_to_str((struct sockaddr*)&b->cached)); make_tid(tid, "pn", 0); rc = send_ping((struct sockaddr*)&b->cached, b->cachedlen, tid, 4); b->cached.ss_family = 0; @@ -667,7 +826,7 @@ blacklist_node(const unsigned char *id, const struct sockaddr *sa, int salen) { int i; - debugf("Blacklisting broken node.\n"); + debugf("Blacklisting node %s", sa_to_str(sa)); if(id) { struct node *n; @@ -760,7 +919,7 @@ split_bucket_helper(struct bucket *b, struct node **nodes_return) unsigned char new_id[20]; if(!in_bucket(myid, b)) { - debugf("Attempted to split wrong bucket.\n"); + errorf("Attempted to split wrong bucket %s", id_to_hex(b->first)); return -1; } @@ -801,10 +960,10 @@ split_bucket(struct bucket *b) struct node *nodes = NULL; struct node *n = NULL; - debugf("Splitting.\n"); + debugf("Splitting bucket %s", id_to_hex(b->first)); rc = split_bucket_helper(b, &nodes); if(rc < 0) { - debugf("Couldn't split bucket"); + errorf("Failed to split bucket %s", id_to_hex(b->first)); return -1; } @@ -817,7 +976,8 @@ split_bucket(struct bucket *b) } rc = insert_node(n, &split); if(rc < 0) { - debugf("Couldn't insert node.\n"); + errorf("Failed to reinsert node %s into bucket %s after splitting bucket %s", + sa_to_str((struct sockaddr*)&n->ss), id_to_hex(split->first), id_to_hex(b->first)); free(n); n = NULL; } else if(rc > 0) { @@ -827,10 +987,10 @@ split_bucket(struct bucket *b) n = NULL; } else { struct node *insert = NULL; - debugf("Splitting (recursive).\n"); + debugf("Splitting bucket %s (recursive)", id_to_hex(split->first)); rc = split_bucket_helper(split, &insert); if(rc < 0) { - debugf("Couldn't split bucket.\n"); + errorf("Failed to split bucket %s", id_to_hex(split->first)); free(n); n = NULL; } else { @@ -844,7 +1004,7 @@ split_bucket(struct bucket *b) /* We just learnt about a node, not necessarily a new one. Confirm is 1 if the node sent a message, 2 if it sent us a reply. */ static struct node * -new_node(const unsigned char *id, const struct sockaddr *sa, int salen, +new_node(const unsigned char *id, const unsigned char *v, int have_v, const struct sockaddr *sa, int salen, int confirm) { struct bucket *b; @@ -868,11 +1028,12 @@ new_node(const unsigned char *id, const struct sockaddr *sa, int salen, if(confirm == 2) b->time = now.tv_sec; + /* Determine if we already know this node. */ n = b->nodes; while(n) { if(id_cmp(n->id, id) == 0) { + /* Known node. Update stuff. */ if(confirm || n->time < now.tv_sec - 15 * 60) { - /* Known node. Update stuff. */ memcpy((struct sockaddr*)&n->ss, sa, salen); if(confirm) n->time = now.tv_sec; @@ -884,6 +1045,10 @@ new_node(const unsigned char *id, const struct sockaddr *sa, int salen, } if(confirm == 2) add_search_node(id, sa, salen); + if(!n->have_v && have_v && v != NULL) { + memcpy(n->v, v, 4); + n->have_v = 1; + } return n; } n = n->next; @@ -928,7 +1093,7 @@ new_node(const unsigned char *id, const struct sockaddr *sa, int salen, dubious = 1; if(n->pinged_time < now.tv_sec - 15) { unsigned char tid[4]; - debugf("Sending ping to dubious node.\n"); + debugf("Sending ping to dubious node %s", sa_to_str((struct sockaddr*)&n->ss)); make_tid(tid, "pn", 0); send_ping((struct sockaddr*)&n->ss, n->sslen, tid, 4); @@ -964,6 +1129,12 @@ new_node(const unsigned char *id, const struct sockaddr *sa, int salen, if(n == NULL) return NULL; memcpy(n->id, id, 20); + if(have_v && v != NULL) { + memcpy(n->v, v, 4); + n->have_v = 1; + } else { + n->have_v = 0; + } memcpy(&n->ss, sa, salen); n->sslen = salen; n->time = confirm ? now.tv_sec : 0; @@ -1046,7 +1217,8 @@ insert_search_node(const unsigned char *id, int i, j; if(sa->sa_family != sr->af) { - debugf("Attempted to insert node in the wrong family.\n"); + errorf("Attempted to insert node %s in wrong address family (expected %s, got %s)", + sa_to_str(sa), af_to_str(sr->af), af_to_str(sa->sa_family)); return NULL; } @@ -1086,7 +1258,7 @@ insert_search_node(const unsigned char *id, } if(token) { if(token_len >= 40) { - debugf("Eek! Overlong token.\n"); + errorf("Overlong token (expected <%i, got %i)", 40, token_len); } else { memcpy(n->token, token, token_len); n->token_len = token_len; @@ -1106,7 +1278,7 @@ flush_search_node(struct search_node *n, struct search *sr) } static void -expire_searches(dht_callback_t *callback, void *closure) +expire_searches(dht_main_callback_t *callback, void *closure) { struct search *sr = searches, *previous = NULL; @@ -1118,7 +1290,8 @@ expire_searches(dht_callback_t *callback, void *closure) else searches = next; numsearches--; - if (!sr->done) { + if(!sr->done) { + infof("Search for id %s (%s) expired", id_to_hex(sr->id), af_to_ivs(sr->af)); if(callback) (*callback)(closure, sr->af == AF_INET ? @@ -1153,7 +1326,7 @@ search_send_get_peers(struct search *sr, struct search_node *n) n->request_time >= now.tv_sec - DHT_SEARCH_RETRANSMIT) return 0; - debugf("Sending get_peers.\n"); + debugf("Sending get_peers to node %s", sa_to_str((struct sockaddr*)&n->ss)); make_tid(tid, "gp", sr->tid); send_get_peers((struct sockaddr*)&n->ss, n->sslen, tid, 4, sr->id, -1, n->reply_time >= now.tv_sec - DHT_SEARCH_RETRANSMIT); @@ -1184,7 +1357,7 @@ add_search_node(const unsigned char *id, const struct sockaddr *sa, int salen) /* When a search is in progress, we periodically call search_step to send further requests. */ static void -search_step(struct search *sr, dht_callback_t *callback, void *closure) +search_step(struct search *sr, dht_main_callback_t *callback, void *closure) { int i, j; int all_done = 1; @@ -1222,7 +1395,7 @@ search_step(struct search *sr, dht_callback_t *callback, void *closure) n->acked = 1; if(!n->acked) { all_acked = 0; - debugf("Sending announce_peer.\n"); + debugf("Sending announce_peer to node %s", sa_to_str((struct sockaddr*)&n->ss)); make_tid(tid, "ap", sr->tid); send_announce_peer((struct sockaddr*)&n->ss, sizeof(struct sockaddr_storage), @@ -1256,6 +1429,7 @@ search_step(struct search *sr, dht_callback_t *callback, void *closure) return; done: + infof("Search for id %s (%s) complete", id_to_hex(sr->id), af_to_ivs(sr->af)); sr->done = 1; if(callback) (*callback)(closure, @@ -1315,7 +1489,7 @@ insert_search_bucket(struct bucket *b, struct search *sr) search is complete. */ int dht_search(const unsigned char *id, int port, int af, - dht_callback_t *callback, void *closure) + dht_main_callback_t *callback, void *closure) { struct search *sr; struct storage *st; @@ -1326,6 +1500,8 @@ dht_search(const unsigned char *id, int port, int af, return -1; } + infof("Starting search for id %s (%s)", id_to_hex(id), af_to_ivs(af)); + /* Try to answer this search locally. In a fully grown DHT this is very unlikely, but people are running modified versions of this code in private DHTs with very few nodes. What's wrong @@ -1337,7 +1513,8 @@ dht_search(const unsigned char *id, int port, int af, unsigned char buf[18]; int i; - debugf("Found local data (%d peers).\n", st->numpeers); + debugf("Found %d peers in local storage for search for id %s (%s)", + st->numpeers, id_to_hex(id), af_to_ivs(af)); for(i = 0; i < st->numpeers; i++) { swapped = htons(st->peers[i].port); @@ -1368,6 +1545,7 @@ dht_search(const unsigned char *id, int port, int af, if(sr) { /* We're reusing data from an old search. Reusing the same tid means that we can merge replies for both searches. */ + debugf("Reusing existing search for id %s (%s)", id_to_hex(id), af_to_ivs(af)); int i; sr->done = 0; again: @@ -1385,6 +1563,7 @@ dht_search(const unsigned char *id, int port, int af, n->acked = 0; } } else { + debugf("Creating new search for id %s (%s)", id_to_hex(id), af_to_ivs(af)); sr = new_search(); if(sr == NULL) { errno = ENOSPC; @@ -1534,7 +1713,7 @@ expire_storage(void) st = storage; numstorage--; if(numstorage < 0) { - debugf("Eek... numstorage became negative.\n"); + errorf("numstorage became negative while expiring storage"); numstorage = 0; } } else { @@ -1606,6 +1785,45 @@ token_match(const unsigned char *token, int token_len, return 0; } +/* Get statistics. */ +int +dht_stats(int af, int *numbuckets, int *numgood, int *numdubious, int *numtotal) +{ + /* Sanity checks. */ + if(af != AF_INET && af != AF_INET6) { + errno = EAFNOSUPPORT; + return -1; + } + + /* Determine stats. */ + int numbuckets_tmp = 0, numgood_tmp = 0, numdubious_tmp = 0, numtotal_tmp = 0; + struct bucket *b = (af == AF_INET ? buckets : buckets6); + while(b) { + struct node *n = b->nodes; + while(n) { + if(node_good(n)) + numgood_tmp++; + else + numdubious_tmp++; + numtotal_tmp++; + n = n->next; + } + numbuckets_tmp++; + b = b->next; + } + + /* Return results. */ + if(numbuckets) + (*numbuckets) = numbuckets_tmp; + if(numgood) + (*numgood) = numgood_tmp; + if(numdubious) + (*numdubious) = numdubious_tmp; + if(numtotal) + (*numtotal) = numtotal_tmp; + return 1; +} + int dht_nodes(int af, int *good_return, int *dubious_return, int *cached_return, int *incoming_return) @@ -1641,125 +1859,131 @@ dht_nodes(int af, int *good_return, int *dubious_return, int *cached_return, } static void -dump_bucket(FILE *f, struct bucket *b) +dump_buckets(int af) { - struct node *n = b->nodes; - fprintf(f, "Bucket "); - print_hex(f, b->first, 20); - fprintf(f, " count %d/%d age %d%s%s:\n", - b->count, b->max_count, (int)(now.tv_sec - b->time), - in_bucket(myid, b) ? " (mine)" : "", - b->cached.ss_family ? " (cached)" : ""); - while(n) { - char buf[512]; - unsigned short port; - fprintf(f, " Node "); - print_hex(f, n->id, 20); - if(n->ss.ss_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in*)&n->ss; - inet_ntop(AF_INET, &sin->sin_addr, buf, 512); - port = ntohs(sin->sin_port); - } else if(n->ss.ss_family == AF_INET6) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&n->ss; - inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 512); - port = ntohs(sin6->sin6_port); + struct bucket *b = (af == AF_INET ? buckets : buckets6 ); + if(!b) { + infof("\n"); + infof("No %s buckets to dump", af_to_ivs(af)); + return; + } + + const char *fmt_header = (af == AF_INET ? " %-4s %-40s %-4s %-21s %-10s %-10s %-10s %-5s %-4s" : + " %-4s %-40s %-4s %-47s %-10s %-10s %-10s %-5s %-4s"); + const char *fmt_entry = (af == AF_INET ? " %-4i %-40s %-4d %-21s %-10s %-10ld %-10ld %-5d %-4s" : + " %-4i %-40s %-4d %-47s %-10s %-10ld %-10ld %-5d %-4s"); + int bi = 0; + while(b) { + infof("\n"); + infof("Bucket #%d (%s), id %s, nodes %d/%d, age %d%s%s:", + bi, af_to_ivs(b->af), id_to_hex(b->first), + b->count, b->max_count, + (b->time ? (int)(now.tv_sec - b->time) : 0), + in_bucket(myid, b) ? " (mine)" : "", + b->cached.ss_family ? " (cached)" : ""); + int ni = 0; + struct node *n = b->nodes; + if(n) { + infof(fmt_header, "Node", "ID", "Dist", "IP-Address", "Client", "Age", "Age-Reply", "Pings", "Good"); + while(n) { + infof(fmt_entry, ni, id_to_hex(n->id), distance(myid, n->id), + sa_to_str((struct sockaddr*)&n->ss), + (n->have_v ? v_to_str(n->v) : "-"), + (n->time ? (long)(now.tv_sec - n->time) : 0), + (n->reply_time ? (long)(now.tv_sec - n->reply_time) : 0), + n->pinged, (node_good(n) ? "yes" : "no")); + n = n->next; + ni++; + } } else { - snprintf(buf, 512, "unknown(%d)", n->ss.ss_family); - port = 0; + infof(" "); } - - if(n->ss.ss_family == AF_INET6) - fprintf(f, " [%s]:%d ", buf, port); - else - fprintf(f, " %s:%d ", buf, port); - if(n->time != n->reply_time) - fprintf(f, "age %ld, %ld", - (long)(now.tv_sec - n->time), - (long)(now.tv_sec - n->reply_time)); - else - fprintf(f, "age %ld", (long)(now.tv_sec - n->time)); - if(n->pinged) - fprintf(f, " (%d)", n->pinged); - if(node_good(n)) - fprintf(f, " (good)"); - fprintf(f, "\n"); - n = n->next; + b = b->next; + bi++; } - } void -dht_dump_tables(FILE *f) +dht_dump_tables() { - int i; - struct bucket *b; - struct storage *st = storage; - struct search *sr = searches; + /* Print myid/my_v. */ + infof("\n"); + infof("My id: %s", id_to_hex(myid)); + infof("My v: %s", v_to_str(&my_v[5])); - fprintf(f, "My id "); - print_hex(f, myid, 20); - fprintf(f, "\n"); + /* Dump buckets/nodes. */ + dump_buckets(AF_INET); + dump_buckets(AF_INET6); - b = buckets; - while(b) { - dump_bucket(f, b); - b = b->next; - } - - fprintf(f, "\n"); - - b = buckets6; - while(b) { - dump_bucket(f, b); - b = b->next; + /* Dump searches. */ + struct search *sr = searches; + if(!sr) { + infof("\n"); + infof("No searches to dump"); } - + int sri = 0; while(sr) { - fprintf(f, "\nSearch%s id ", sr->af == AF_INET6 ? " (IPv6)" : ""); - print_hex(f, sr->id, 20); - fprintf(f, " age %d%s\n", (int)(now.tv_sec - sr->step_time), - sr->done ? " (done)" : ""); - for(i = 0; i < sr->numnodes; i++) { - struct search_node *n = &sr->nodes[i]; - fprintf(f, "Node %d id ", i); - print_hex(f, n->id, 20); - fprintf(f, " bits %d age ", common_bits(sr->id, n->id)); - if(n->request_time) - fprintf(f, "%d, ", (int)(now.tv_sec - n->request_time)); - fprintf(f, "%d", (int)(now.tv_sec - n->reply_time)); - if(n->pinged) - fprintf(f, " (%d)", n->pinged); - fprintf(f, "%s%s.\n", - find_node(n->id, sr->af) ? " (known)" : "", - n->replied ? " (replied)" : ""); + infof("\n"); + infof("Search #%d (%s), id %s, age %d%s:", sri, af_to_ivs(sr->af), id_to_hex(sr->id), + (int)(now.tv_sec - sr->step_time), sr->done ? " (done)" : ""); + if(sr->numnodes > 0) { + const char *fmt_header = (sr->af == AF_INET ? " %-4s %-40s %-4s %-21s %-10s %-10s %-5s %-5s %-7s" : + " %-4s %-40s %-4s %-47s %-10s %-10s %-5s %-5s %-7s"); + const char *fmt_entry = (sr->af == AF_INET ? " %-4i %-40s %-4d %-21s %-10ld %-10ld %-5d %-5s %-7s" : + " %-4i %-40s %-4d %-47s %-10ld %-10ld %-5d %-5s %-7s"); + infof(fmt_header, "Node", "ID", "Dist", "IP-Address", "Age-Req", "Age-Reply", "Pings", "Known", "Replied"); + for(int ni = 0; ni < sr->numnodes; ni++) { + struct search_node *n = &sr->nodes[ni]; + infof(fmt_entry, + ni, id_to_hex(n->id), distance(sr->id, n->id), + sa_to_str((struct sockaddr*)&n->ss), + (n->request_time ? (long)(now.tv_sec - n->request_time) : 0), + (n->reply_time ? (long)(now.tv_sec - n->reply_time) : 0), n->pinged, + (find_node(n->id, sr->af) ? "yes" : "no"), + (n->replied ? "yes" : "no")); + } + } else { + infof(" "); } sr = sr->next; + sri++; } + /* Dump storages. */ + struct storage *st = storage; + if(!st) { + infof("\n"); + infof("No storage contents to dump"); + } + int sti = 0; while(st) { - fprintf(f, "\nStorage "); - print_hex(f, st->id, 20); - fprintf(f, " %d/%d nodes:", st->numpeers, st->maxpeers); - for(i = 0; i < st->numpeers; i++) { - char buf[100]; - if(st->peers[i].len == 4) { - inet_ntop(AF_INET, st->peers[i].ip, buf, 100); - } else if(st->peers[i].len == 16) { - buf[0] = '['; - inet_ntop(AF_INET6, st->peers[i].ip, buf + 1, 98); - strcat(buf, "]"); - } else { - strcpy(buf, "???"); + infof("\n"); + infof("Storage #%d, id %s, peers %d/%d:", sti, id_to_hex(st->id), st->numpeers, st->maxpeers); + if(st->numpeers > 0) { + infof(" %-4s %-47s %-10s", "Peer", "IP-Address", "Age"); + for(int pi = 0; pi < st->numpeers; pi++) { + char sastr[INET6_ADDRSTRLEN + 8]; + if(st->peers[pi].len == 4) { + char ipstr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, st->peers[pi].ip, ipstr, sizeof(ipstr)); + snprintf(sastr, sizeof(sastr), "%s:%u", ipstr, st->peers[pi].port); + } else if(st->peers[pi].len == 16) { + char ipstr6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, st->peers[pi].ip, ipstr6, sizeof(ipstr6)); + snprintf(sastr, sizeof(sastr), "[%s]:%u", ipstr6, st->peers[pi].port); + } else { + strcpy(sastr, ""); + } + infof(" %-4i %-47s %-10ld", pi, sastr, (long)(now.tv_sec - st->peers[pi].time)); } - fprintf(f, " %s:%u (%ld)", - buf, st->peers[i].port, - (long)(now.tv_sec - st->peers[i].time)); + } else { + infof(" "); } st = st->next; + sti++; } - fprintf(f, "\n\n"); - fflush(f); + infof("\n"); } int @@ -1768,10 +1992,13 @@ dht_init(int s, int s6, const unsigned char *id, const unsigned char *v) int rc; if(dht_socket >= 0 || dht_socket6 >= 0 || buckets || buckets6) { + warnf("Unable to initialize DHT, already initialized"); errno = EBUSY; return -1; } + infof("Initializing DHT"); + searches = NULL; numsearches = 0; @@ -1779,7 +2006,7 @@ dht_init(int s, int s6, const unsigned char *id, const unsigned char *v) numstorage = 0; if(s >= 0) { - buckets = calloc(sizeof(struct bucket), 1); + buckets = calloc(1, sizeof(struct bucket)); if(buckets == NULL) return -1; buckets->max_count = 128; @@ -1787,7 +2014,7 @@ dht_init(int s, int s6, const unsigned char *id, const unsigned char *v) } if(s6 >= 0) { - buckets6 = calloc(sizeof(struct bucket), 1); + buckets6 = calloc(1, sizeof(struct bucket)); if(buckets6 == NULL) return -1; buckets6->max_count = 128; @@ -1842,10 +2069,13 @@ int dht_uninit() { if(dht_socket < 0 && dht_socket6 < 0) { + warnf("Unable to shutdown DHT, already shut down"); errno = EINVAL; return -1; } + infof("Shutting down DHT"); + dht_socket = -1; dht_socket6 = -1; @@ -1935,8 +2165,8 @@ neighbourhood_maintenance(int af) n = random_node(q); if(n) { unsigned char tid[4]; - debugf("Sending find_node for%s neighborhood maintenance.\n", - af == AF_INET6 ? " IPv6" : ""); + debugf("Sending find_node for %s neighborhood maintenance to node %s", + af_to_ivs(af), sa_to_str((struct sockaddr*)&n->ss)); make_tid(tid, "fn", 0); send_find_node((struct sockaddr*)&n->ss, n->sslen, tid, 4, id, want, @@ -2007,8 +2237,8 @@ bucket_maintenance(int af) want = WANT4 | WANT6; } - debugf("Sending find_node for%s bucket maintenance.\n", - af == AF_INET6 ? " IPv6" : ""); + debugf("Sending find_node for %s bucket maintenance to node %s", + af_to_ivs(af), sa_to_str((struct sockaddr*)&n->ss)); make_tid(tid, "fn", 0); send_find_node((struct sockaddr*)&n->ss, n->sslen, tid, 4, id, want, @@ -2029,7 +2259,7 @@ int dht_periodic(const void *buf, size_t buflen, const struct sockaddr *from, int fromlen, time_t *tosleep, - dht_callback_t *callback, void *closure) + dht_main_callback_t *callback, void *closure) { dht_gettimeofday(&now, NULL); @@ -2042,35 +2272,38 @@ dht_periodic(const void *buf, size_t buflen, goto dontread; if(node_blacklisted(from, fromlen)) { - debugf("Received packet from blacklisted node.\n"); + debugf("Received packet from blacklisted node %s", sa_to_str(from)); goto dontread; } if(((char*)buf)[buflen] != '\0') { - debugf("Unterminated message.\n"); + warnf("Received unterminated message from node %s", sa_to_str(from)); errno = EINVAL; return -1; } +#if (DHT_LOG_INCOMING_MESSAGES == 1) + debugf("<<< %s", ba_to_str(buf, buflen)); +#endif + memset(&m, 0, sizeof(m)); message = parse_message(buf, buflen, &m); if(message < 0 || message == ERROR || id_cmp(m.id, zeroes) == 0) { - debugf("Unparseable message: "); - debug_printable(buf, buflen); - debugf("\n"); + warnf("Received unparseable message from node %s, message: %s", + sa_to_str(from), ba_to_str(buf, buflen)); goto dontread; } if(id_cmp(m.id, myid) == 0) { - debugf("Received message from self.\n"); + warnf("Received message from self (%s)", sa_to_str(from)); goto dontread; } if(message > REPLY) { /* Rate limit requests. */ if(!token_bucket()) { - debugf("Dropping request due to rate limiting.\n"); + warnf("Dropping request from node %s due to rate limiting", sa_to_str(from)); goto dontread; } } @@ -2078,9 +2311,8 @@ dht_periodic(const void *buf, size_t buflen, switch(message) { case REPLY: if(m.tid_len != 4) { - debugf("Broken node truncates transaction ids: "); - debug_printable(buf, buflen); - debugf("\n"); + warnf("Blacklisting node %s for truncating transaction id, message: %s", + sa_to_str(from), ba_to_str(buf, buflen)); /* This is really annoying, as it means that we will time-out all our searches that go through this node. Kill it. */ @@ -2088,8 +2320,8 @@ dht_periodic(const void *buf, size_t buflen, goto dontread; } if(tid_match(m.tid, "pn", NULL)) { - debugf("Pong!\n"); - new_node(m.id, from, fromlen, 2); + debugf("Received pong from node %s", sa_to_str(from)); + new_node(m.id, m.v, m.have_v, from, fromlen, 2); } else if(tid_match(m.tid, "fn", NULL) || tid_match(m.tid, "gp", NULL)) { int gp = 0; @@ -2098,18 +2330,20 @@ dht_periodic(const void *buf, size_t buflen, gp = 1; sr = find_search(ttid, from->sa_family); } - debugf("Nodes found (%d+%d)%s!\n", - m.nodes_len/26, m.nodes6_len/38, - gp ? " for get_peers" : ""); + debugf("Received %d nodes (IPv4: %d, IPv6: %d)%s from node %s", + m.nodes_len/26 + m.nodes6_len/38, m.nodes_len/26, m.nodes6_len/38, + gp ? " for get_peers" : "", sa_to_str(from)); if(m.nodes_len % 26 != 0 || m.nodes6_len % 38 != 0) { - debugf("Unexpected length for node info!\n"); + warnf("Blacklisting node %s for sending node list of invalid length", + sa_to_str(from)); blacklist_node(m.id, from, fromlen); } else if(gp && sr == NULL) { - debugf("Unknown search!\n"); - new_node(m.id, from, fromlen, 1); + warnf("Received peers from node %s, but couldn't find any matching search", + sa_to_str(from)); + new_node(m.id, m.v, m.have_v, from, fromlen, 1); } else { int i; - new_node(m.id, from, fromlen, 2); + new_node(m.id, m.v, m.have_v, from, fromlen, 2); for(i = 0; i < m.nodes_len / 26; i++) { unsigned char *ni = m.nodes + i * 26; struct sockaddr_in sin; @@ -2119,7 +2353,7 @@ dht_periodic(const void *buf, size_t buflen, sin.sin_family = AF_INET; memcpy(&sin.sin_addr, ni + 20, 4); memcpy(&sin.sin_port, ni + 24, 2); - new_node(ni, (struct sockaddr*)&sin, sizeof(sin), 0); + new_node(ni, NULL, 0, (struct sockaddr*)&sin, sizeof(sin), 0); if(sr && sr->af == AF_INET) { insert_search_node(ni, (struct sockaddr*)&sin, @@ -2136,7 +2370,7 @@ dht_periodic(const void *buf, size_t buflen, sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, ni + 20, 16); memcpy(&sin6.sin6_port, ni + 36, 2); - new_node(ni, (struct sockaddr*)&sin6, sizeof(sin6), 0); + new_node(ni, NULL, 0, (struct sockaddr*)&sin6, sizeof(sin6), 0); if(sr && sr->af == AF_INET6) { insert_search_node(ni, (struct sockaddr*)&sin6, @@ -2154,13 +2388,13 @@ dht_periodic(const void *buf, size_t buflen, insert_search_node(m.id, from, fromlen, sr, 1, m.token, m.token_len); if(m.values_len > 0 || m.values6_len > 0) { - debugf("Got values (%d+%d)!\n", - m.values_len / 6, m.values6_len / 18); + infof("Received %d peers (IPv4: %d, IPv6: %d) from node %s for search for id %s", + m.values_len / 6 + m.values6_len / 18, m.values_len / 6, m.values6_len / 18, + sa_to_str(from), id_to_hex(sr->id)); if(callback) { if(m.values_len > 0) (*callback)(closure, DHT_EVENT_VALUES, sr->id, (void*)m.values, m.values_len); - if(m.values6_len > 0) (*callback)(closure, DHT_EVENT_VALUES6, sr->id, (void*)m.values6, m.values6_len); @@ -2169,14 +2403,15 @@ dht_periodic(const void *buf, size_t buflen, } } else if(tid_match(m.tid, "ap", &ttid)) { struct search *sr; - debugf("Got reply to announce_peer.\n"); + debugf("Received reply to announce_peer from node %s", sa_to_str(from)); sr = find_search(ttid, from->sa_family); if(!sr) { - debugf("Unknown search!\n"); - new_node(m.id, from, fromlen, 1); + warnf("Reply to announce_peer received from node %s does not match any known search", + sa_to_str(from)); + new_node(m.id, m.v, m.have_v, from, fromlen, 1); } else { int i; - new_node(m.id, from, fromlen, 2); + new_node(m.id, m.v, m.have_v, from, fromlen, 2); for(i = 0; i < sr->numnodes; i++) if(id_cmp(sr->nodes[i].id, m.id) == 0) { sr->nodes[i].request_time = 0; @@ -2189,47 +2424,48 @@ dht_periodic(const void *buf, size_t buflen, search_send_get_peers(sr, NULL); } } else { - debugf("Unexpected reply: "); - debug_printable(buf, buflen); + debugf("Received unexpected reply from node %s, message: %s", + sa_to_str(from), ba_to_str(buf, buflen)); debugf("\n"); } break; case PING: - debugf("Ping (%d)!\n", m.tid_len); - new_node(m.id, from, fromlen, 1); - debugf("Sending pong.\n"); + debugf("Received ping from node %s", sa_to_str(from)); + new_node(m.id, m.v, m.have_v, from, fromlen, 1); + debugf("Sending pong to node %s", sa_to_str(from)); send_pong(from, fromlen, m.tid, m.tid_len); break; case FIND_NODE: - debugf("Find node!\n"); - new_node(m.id, from, fromlen, 1); - debugf("Sending closest nodes (%d).\n", m.want); + debugf("Received find_node from node %s", sa_to_str(from)); + new_node(m.id, m.v, m.have_v, from, fromlen, 1); + debugf("Sending closest nodes to node %s", sa_to_str(from)); send_closest_nodes(from, fromlen, m.tid, m.tid_len, m.target, m.want, 0, NULL, NULL, 0); break; case GET_PEERS: - debugf("Get_peers!\n"); - new_node(m.id, from, fromlen, 1); + debugf("Received get_peers from node %s", sa_to_str(from)); + new_node(m.id, m.v, m.have_v, from, fromlen, 1); if(id_cmp(m.info_hash, zeroes) == 0) { - debugf("Eek! Got get_peers with no info_hash.\n"); + warnf("Get_peers received from node %s does not contain an info_hash", + sa_to_str(from)); send_error(from, fromlen, m.tid, m.tid_len, - 203, "Get_peers with no info_hash"); + 203, "Get_peers without info_hash"); break; } else { struct storage *st = find_storage(m.info_hash); unsigned char token[TOKEN_SIZE]; make_token(from, 0, token); if(st && st->numpeers > 0) { - debugf("Sending found%s peers.\n", - from->sa_family == AF_INET6 ? " IPv6" : ""); + debugf("Sending %s peers from local storage to node %s", + af_to_ivs(from->sa_family), sa_to_str(from)); send_closest_nodes(from, fromlen, m.tid, m.tid_len, m.info_hash, m.want, from->sa_family, st, token, TOKEN_SIZE); } else { - debugf("Sending nodes for get_peers.\n"); + debugf("Sending closest nodes for get_peers to node %s", sa_to_str(from)); send_closest_nodes(from, fromlen, m.tid, m.tid_len, m.info_hash, m.want, 0, NULL, token, TOKEN_SIZE); @@ -2237,18 +2473,20 @@ dht_periodic(const void *buf, size_t buflen, } break; case ANNOUNCE_PEER: - debugf("Announce peer!\n"); - new_node(m.id, from, fromlen, 1); + debugf("Received announce_peer from node %s", sa_to_str(from)); + new_node(m.id, m.v, m.have_v, from, fromlen, 1); if(id_cmp(m.info_hash, zeroes) == 0) { - debugf("Announce_peer with no info_hash.\n"); + warnf("Announce_peer received from node %s does not contain an info_hash", + sa_to_str(from)); send_error(from, fromlen, m.tid, m.tid_len, - 203, "Announce_peer with no info_hash"); + 203, "Announce_peer without info_hash"); break; } if(!token_match(m.token, m.token_len, from)) { - debugf("Incorrect token for announce_peer.\n"); + warnf("Announce_peer received from node %s contains an incorrect token", + sa_to_str(from)); send_error(from, fromlen, m.tid, m.tid_len, - 203, "Announce_peer with wrong token"); + 203, "Announce_peer with incorrect token"); break; } if(m.implied_port != 0) { @@ -2263,16 +2501,17 @@ dht_periodic(const void *buf, size_t buflen, } } if(m.port == 0) { - debugf("Announce_peer with forbidden port %d.\n", m.port); + warnf("Announce_peer received from node %s contains forbidden port %d", + sa_to_str(from), m.port); send_error(from, fromlen, m.tid, m.tid_len, - 203, "Announce_peer with forbidden port number"); + 203, "Announce_peer with forbidden port"); break; } storage_store(m.info_hash, from, m.port); /* Note that if storage_store failed, we lie to the requestor. This is to prevent them from backtracking, and hence polluting the DHT. */ - debugf("Sending peer announced.\n"); + debugf("Sending peer_announced to node %s", sa_to_str(from)); send_peer_announced(from, fromlen, m.tid, m.tid_len); } } @@ -2437,12 +2676,12 @@ dht_insert_node(const unsigned char *id, struct sockaddr *sa, int salen) { struct node *n; - if(sa->sa_family != AF_INET) { + if(sa->sa_family != AF_INET && sa->sa_family != AF_INET6) { errno = EAFNOSUPPORT; return -1; } - n = new_node(id, sa, salen, 0); + n = new_node(id, NULL, 0, sa, salen, 0); return !!n; } @@ -2451,7 +2690,7 @@ dht_ping_node(const struct sockaddr *sa, int salen) { unsigned char tid[4]; - debugf("Sending ping.\n"); + debugf("Sending ping to node %s", sa_to_str(sa)); make_tid(tid, "pn", 0); return send_ping(sa, salen, tid, 4); } @@ -2486,7 +2725,7 @@ dht_send(const void *buf, size_t len, int flags, abort(); if(node_blacklisted(sa, salen)) { - debugf("Attempting to send to blacklisted node.\n"); + warnf("Attempted to send to blacklisted node %s", sa_to_str(sa)); errno = EPERM; return -1; } @@ -2503,6 +2742,10 @@ dht_send(const void *buf, size_t len, int flags, return -1; } +#if (DHT_LOG_OUTGOING_MESSAGES == 1) + debugf(">>> %s", ba_to_str(buf, len)); +#endif + return dht_sendto(s, buf, len, flags, sa, salen); } @@ -2740,12 +2983,11 @@ send_closest_nodes(const struct sockaddr *sa, int salen, numnodes6 = buffer_closest_nodes(nodes6, numnodes6, id, b); } } - debugf(" (%d+%d nodes.)\n", numnodes, numnodes6); + debugf(" %d nodes (IPv4: %d, IPv6: %d)", + numnodes + numnodes6, numnodes, numnodes6); - return send_nodes_peers(sa, salen, tid, tid_len, - nodes, numnodes * 26, - nodes6, numnodes6 * 38, - af, st, token, token_len); + return send_nodes_peers(sa, salen, tid, tid_len, nodes, numnodes * 26, + nodes6, numnodes6 * 38, af, st, token, token_len); } int @@ -2897,7 +3139,8 @@ parse_message(const unsigned char *buf, int buflen, /* This code will happily crash if the buffer is not NUL-terminated. */ if(buf[buflen] != '\0') { - debugf("Eek! parse_message with unterminated buffer.\n"); + errorf("Attempted to parse message with unterminated buffer, message: %s", + ba_to_str(buf, buflen)); return -1; } @@ -3011,14 +3254,14 @@ parse_message(const unsigned char *buf, int buflen, memcpy((char*)m->values6 + j6, q + 1, l); j6 += l; } else { - debugf("Received weird value -- %d bytes.\n", (int)l); + warnf("Encountered weird value of %d bytes while parsing message", (int)l); } } else { break; } } if(i >= buflen || buf[i] != 'e') - debugf("eek... unexpected end for values.\n"); + warnf("Encountered unexpected end for values while parsing message"); m->values_len = j; m->values6_len = j6; } @@ -3035,11 +3278,20 @@ parse_message(const unsigned char *buf, int buflen, else if(buf[i] == '2' && memcmp(buf + i + 2, "n6", 2) == 0) m->want |= WANT6; else - debugf("eek... unexpected want flag (%c)\n", buf[i]); + warnf("Encountered unexpected want flag %c while parsing message", buf[i]); i += 2 + buf[i] - '0'; } if(i >= buflen || buf[i] != 'e') - debugf("eek... unexpected end for want.\n"); + warnf("Encountered unexpected end for want while parsing message"); + } + + p = dht_memmem(buf, buflen, "1:v4:", 5); + if(p) { + CHECK(p + 5, 4); + memcpy(m->v, p + 5, 4); + m->have_v = 1; + } else { + m->have_v = 0; } #undef CHECK @@ -3061,6 +3313,6 @@ parse_message(const unsigned char *buf, int buflen, return -1; overflow: - debugf("Truncated message.\n"); + warnf("Encountered unexpected end of message while parsing message"); return -1; } diff --git a/dht.h b/dht.h index 31d7c46..cb48b99 100644 --- a/dht.h +++ b/dht.h @@ -25,35 +25,46 @@ extern "C" { #endif typedef void -dht_callback_t(void *closure, int event, - const unsigned char *info_hash, - const void *data, size_t data_len); +dht_main_callback_t(void *closure, int event, + const unsigned char *info_hash, + const void *data, size_t data_len); -#define DHT_EVENT_NONE 0 -#define DHT_EVENT_VALUES 1 -#define DHT_EVENT_VALUES6 2 -#define DHT_EVENT_SEARCH_DONE 3 +#define DHT_EVENT_NONE 0 +#define DHT_EVENT_VALUES 1 +#define DHT_EVENT_VALUES6 2 +#define DHT_EVENT_SEARCH_DONE 3 #define DHT_EVENT_SEARCH_DONE6 4 -extern FILE *dht_debug; +typedef void +dht_log_callback_t(const struct timeval *time, int type, + const char *format, va_list ap); + +#define DHT_LOG_TYPE_DEBUG 0 +#define DHT_LOG_TYPE_INFO 1 +#define DHT_LOG_TYPE_WARN 2 +#define DHT_LOG_TYPE_ERROR 3 + +void dht_set_log_callback(dht_log_callback_t *callback); +/* Provided by library. */ int dht_init(int s, int s6, const unsigned char *id, const unsigned char *v); int dht_insert_node(const unsigned char *id, struct sockaddr *sa, int salen); int dht_ping_node(const struct sockaddr *sa, int salen); int dht_periodic(const void *buf, size_t buflen, const struct sockaddr *from, int fromlen, time_t *tosleep, - dht_callback_t *callback, void *closure); + dht_main_callback_t *callback, void *closure); int dht_search(const unsigned char *id, int port, int af, - dht_callback_t *callback, void *closure); + dht_main_callback_t *callback, void *closure); +int dht_stats(int af, int *numbuckets, int *numgood, int *numdubious, int *numtotal); int dht_nodes(int af, int *good_return, int *dubious_return, int *cached_return, int *incoming_return); -void dht_dump_tables(FILE *f); +void dht_dump_tables(); int dht_get_nodes(struct sockaddr_in *sin, int *num, struct sockaddr_in6 *sin6, int *num6); int dht_uninit(void); -/* This must be provided by the user. */ +/* Provided by user. */ int dht_sendto(int sockfd, const void *buf, int len, int flags, const struct sockaddr *to, int tolen); int dht_blacklisted(const struct sockaddr *sa, int salen);