From c33f27c06cef3cd4eda6c3ad98c1b8ff56a3f2dd Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 29 Jul 2009 03:06:18 +0000 Subject: [PATCH] (trunk third-party) #2302: upgrade from dht 0.6 to 0.8. --- third-party/dht/CHANGES | 11 +++ third-party/dht/README | 18 ++--- third-party/dht/dht.c | 162 +++++++++++++++++++++++++++------------- 3 files changed, 129 insertions(+), 62 deletions(-) diff --git a/third-party/dht/CHANGES b/third-party/dht/CHANGES index 9cbff3404..34c35cead 100644 --- a/third-party/dht/CHANGES +++ b/third-party/dht/CHANGES @@ -1,3 +1,14 @@ +28 July 2009: dht-0.8 + + * Fixed a crash when expiring the first search on the list. + * Fixed freeing of the search list when uniniting with dofree = 1. + +24 June 2009: dht-0.7 + + * Removed the fixed limit on the number of concurrent searches, we now + use a linked list. + * Fixed build on FreeBSD (thanks to Humihara and Charles Kerr). + 22 May 2009: dht-0.6 * Fixed a buffer overflow (when reading) in parse_message. diff --git a/third-party/dht/README b/third-party/dht/README index 893c4e02b..20ec9267c 100644 --- a/third-party/dht/README +++ b/third-party/dht/README @@ -93,7 +93,7 @@ interesting happens (see below). This schedules a search for information about the info-hash specified in id. If port is not 0, it specifies the TCP port on which the current peer -is litening; in that case, when the search is complete it will be announced +is listening; in that case, when the search is complete it will be announced to the network. The port is in host order, beware if you got it from a struct sockaddr_in. @@ -101,7 +101,7 @@ In either case, data is passed to the callback function as soon as it is available, possibly in multiple pieces. The callback function will additionally be called when the search is complete. -Up to DHT_MAX_SEARCHES (20) searches can be in progress at a given time; +Up to DHT_MAX_SEARCHES (1024) searches can be in progress at a given time; any more, and dht_search will return -1. If you specify a new search for the same info hash as a search still in progress, the previous search is combined with the new one -- you will only receive a completion indication @@ -123,12 +123,12 @@ It also includes the number of nodes that recently send us an unsolicited request; this can be used to determine if the UDP port used for the DHT is firewalled. -If you want to display a single figure to the user, you should display good -+ doubtful, which is the total number of nodes in your routing table. Some -clients try to estimate the total number of nodes, but this doesn't make -much sense -- since the result is exponential in the number of nodes in the -routing table, small variations in the latter cause huge jumps in the -former. +If you want to display a single figure to the user, you should display +good + doubtful, which is the total number of nodes in your routing table. +Some clients try to estimate the total number of nodes, but this doesn't +make much sense -- since the result is exponential in the number of nodes +in the routing table, small variations in the latter cause huge jumps in +the former. * dht_get_nodes @@ -186,8 +186,6 @@ make most full cone NATs happy. Some of the code has had very little testing. If it breaks, you get to keep both pieces. -There is currently no good way to save and restore your routing table. - IPv6 support is deliberately not included: designing a double-stack distributed hash table raises some tricky issues, and doing it naively may break connectivity for everyone. diff --git a/third-party/dht/dht.c b/third-party/dht/dht.c index eadb46f72..e8e0fc7f4 100644 --- a/third-party/dht/dht.c +++ b/third-party/dht/dht.c @@ -122,6 +122,7 @@ struct search { int done; struct search_node nodes[SEARCH_NODES]; int numnodes; + struct search *next; }; struct peer { @@ -135,6 +136,16 @@ struct peer { #define DHT_MAX_PEERS 2048 #endif +/* The maximum number of searches we keep data about. */ +#ifndef DHT_MAX_SEARCHES +#define DHT_MAX_SEARCHES 1024 +#endif + +/* The time after which we consider a search to be expirable. */ +#ifndef DHT_SEARCH_EXPIRE_TIME +#define DHT_SEARCH_EXPIRE_TIME (62 * 60) +#endif + struct storage { unsigned char id[20]; int numpeers; @@ -207,12 +218,7 @@ static unsigned char oldsecret[8]; static struct bucket *buckets = NULL; static struct storage *storage; -/* The maximum number of concurrent searches. */ -#ifndef DHT_MAX_SEARCHES -#define DHT_MAX_SEARCHES 20 -#endif - -static struct search searches[DHT_MAX_SEARCHES]; +static struct search *searches = NULL; static int numsearches; static unsigned short search_id; @@ -724,10 +730,11 @@ expire_buckets(int s) static struct search * find_search(unsigned short tid) { - int i; - for(i = 0; i < numsearches; i++) { - if(searches[i].tid == tid) - return &searches[i]; + struct search *sr = searches; + while(sr) { + if(sr->tid == tid) + return sr; + sr = sr->next; } return NULL; } @@ -798,6 +805,27 @@ flush_search_node(struct search_node *n, struct search *sr) sr->numnodes--; } +static void +expire_searches(void) +{ + struct search *sr = searches, *previous = NULL; + + while(sr) { + struct search *next = sr->next; + if(sr->step_time < now.tv_sec - DHT_SEARCH_EXPIRE_TIME) { + if(previous) + previous->next = next; + else + searches = next; + free(sr); + numsearches--; + } else { + previous = sr; + } + sr = next; + } +} + /* This must always return 0 or 1, never -1, not even on failure (see below). */ static int search_send_get_peers(int s, struct search *sr, struct search_node *n) @@ -909,20 +937,36 @@ search_step(int s, struct search *sr, dht_callback *callback, void *closure) } static struct search * -find_free_search_slot(void) +new_search(void) { - int i; - struct search *sr = NULL; + struct search *sr, *oldest = NULL; - if(numsearches < DHT_MAX_SEARCHES) - return &searches[numsearches++]; - - for(i = 0; i < numsearches; i++) { - if(searches[i].done && - (sr == NULL || searches[i].step_time < sr->step_time)) - sr = &searches[i]; + /* Find the oldest done search */ + sr = searches; + while(sr) { + if(sr->done && + (oldest == NULL || oldest->step_time > sr->step_time)) + oldest = sr; + sr = sr->next; } - return sr; + + /* The oldest slot is expired. */ + if(oldest && oldest->step_time < now.tv_sec - DHT_SEARCH_EXPIRE_TIME) + return oldest; + + /* Allocate a new slot. */ + if(numsearches < DHT_MAX_SEARCHES) { + sr = calloc(1, sizeof(struct search)); + if(sr != NULL) { + sr->next = searches; + searches = sr; + numsearches++; + return sr; + } + } + + /* Oh, well, never mind. Reuse the oldest slot. */ + return oldest; } /* Insert the contents of a bucket into a search structure. */ @@ -945,23 +989,23 @@ dht_search(int s, const unsigned char *id, int port, { struct search *sr; struct bucket *b; - int i; - for(i = 0; i < numsearches; i++) { - if(id_cmp(searches[i].id, id) == 0) + sr = searches; + while(sr) { + if(id_cmp(sr->id, id) == 0) break; + sr = sr->next; } - if(i < numsearches) { + if(sr) { /* We're reusing data from an old search. Reusing the same tid means that we can merge replies for both searches. */ - int j; - sr = searches + i; + int i; sr->done = 0; again: - for(j = 0; j < sr->numnodes; j++) { + for(i = 0; i < sr->numnodes; i++) { struct search_node *n; - n = &sr->nodes[j]; + n = &sr->nodes[i]; /* Discard any doubtful nodes. */ if(n->pinged >= 3 || n->reply_time < now.tv_sec - 7200) { flush_search_node(n, sr); @@ -973,14 +1017,15 @@ dht_search(int s, const unsigned char *id, int port, n->acked = 0; } } else { - sr = find_free_search_slot(); + sr = new_search(); if(sr == NULL) { errno = ENOSPC; return -1; } - memset(sr, 0, sizeof(struct search)); sr->tid = search_id++; + sr->step_time = 0; memcpy(sr->id, id, 20); + sr->done = 0; sr->numnodes = 0; } @@ -1107,24 +1152,26 @@ expire_storage(void) static void broken_node(int s, const unsigned char *id, struct sockaddr_in *sin) { - int i, j; + int i; debugf("Blacklisting broken node.\n"); if(id) { - /* Make the node easy to discard. */ struct node *n; + struct search *sr; + /* Make the node easy to discard. */ n = find_node(id); if(n) { n->pinged = 3; pinged(s, n, NULL); } /* Discard it from any searches in progress. */ - for(i = 0; i < numsearches; i++) { - for(j = 0; j < searches[i].numnodes; j++) - if(id_cmp(searches[i].nodes[j].id, id) == 0) - flush_search_node(&searches[i].nodes[j], - &searches[i]); + sr = searches; + while(sr) { + for(i = 0; i < sr->numnodes; i++) + if(id_cmp(sr->nodes[i].id, id) == 0) + flush_search_node(&sr->nodes[i], sr); + sr = sr->next; } } /* And make sure we don't hear from it again. */ @@ -1214,9 +1261,10 @@ dht_nodes(int *good_return, int *dubious_return, int *cached_return, void dht_dump_tables(FILE *f) { - int i, j; + int i; struct bucket *b = buckets; struct storage *st = storage; + struct search *sr = searches; fprintf(f, "My id "); print_hex(f, myid, 20); @@ -1250,15 +1298,14 @@ dht_dump_tables(FILE *f) } b = b->next; } - for(i = 0; i < numsearches; i++) { - struct search *sr = &searches[i]; - fprintf(f, "\nSearch %d id ", i); + while(sr) { + fprintf(f, "\nSearch id "); print_hex(f, sr->id, 20); fprintf(f, " age %d%s\n", (int)(now.tv_sec - sr->step_time), sr->done ? " (done)" : ""); - for(j = 0; j < sr->numnodes; j++) { - struct search_node *n = &sr->nodes[j]; - fprintf(f, "Node %d id ", j); + 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) @@ -1270,6 +1317,7 @@ dht_dump_tables(FILE *f) find_node(n->id) ? " (known)" : "", n->replied ? " (replied)" : ""); } + sr = sr->next; } @@ -1305,6 +1353,7 @@ dht_init(int s, const unsigned char *id, const unsigned char *v) if(buckets == NULL) return -1; + searches = NULL; numsearches = 0; storage = NULL; @@ -1377,7 +1426,13 @@ dht_uninit(int s, int dofree) free(st->peers); free(st); } - + + while(searches) { + struct search *sr = searches; + searches = searches->next; + free(sr); + } + return 1; } @@ -1654,26 +1709,29 @@ dht_periodic(int s, int available, time_t *tosleep, if(now.tv_sec >= expire_stuff_time) { expire_buckets(s); expire_storage(); + expire_searches(); } if(search_time > 0 && now.tv_sec >= search_time) { - int i; - for(i = 0; i < numsearches; i++) { - struct search *sr = &searches[i]; + struct search *sr; + sr = searches; + while(sr) { if(!sr->done && sr->step_time + 5 <= now.tv_sec) { search_step(s, sr, callback, closure); } + sr = sr->next; } search_time = 0; - - for(i = 0; i < numsearches; i++) { - struct search *sr = &searches[i]; + + sr = searches; + while(sr) { if(!sr->done) { time_t tm = sr->step_time + 15 + random() % 10; if(search_time == 0 || search_time > tm) search_time = tm; } + sr = sr->next; } }