]> granicus.if.org Git - transmission/commitdiff
(trunk third-party) #2302: upgrade libdht to newer version
authorCharles Kerr <charles@transmissionbt.com>
Sat, 19 Sep 2009 18:09:57 +0000 (18:09 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Sat, 19 Sep 2009 18:09:57 +0000 (18:09 +0000)
third-party/dht/CHANGES
third-party/dht/README
third-party/dht/dht.c

index 9cbff34046e6d5b2916d646e794985faddfbb977..46a23899c0b0bc63c0b74a69576bdb0cfc51535b 100644 (file)
@@ -1,3 +1,19 @@
+dht-0.9 (unreleased):
+
+  * Fixed incorrect computation of number of nodes.
+  * Made the initial bucket split more eagerly.
+
+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.
index 893c4e02bb16053796ec7cf42133782dd54c019c..20ec9267c218c87dc1933b3e7213ce1e16dfc32a 100644 (file)
@@ -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.
index eadb46f72e6d25dddc071de13410b405f6dd8869..d1c57e8dac94fe87edc805026accf42db7e0c914 100644 (file)
@@ -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;
 
@@ -644,8 +650,10 @@ new_node(int s, const unsigned char *id, struct sockaddr_in *sin,
             }
             n = n->next;
         }
-        
-        if(!dubious && mybucket) {
+
+        /* If there's only one bucket, split even if there remain doubtful
+           nodes.  This violates the spec, but it speeds up bootstrapping. */
+        if(mybucket && (!dubious || buckets->next == NULL)) {
             debugf("Splitting.\n");
             b = split_bucket(s, b);
             mybucket_grow_time = now.tv_sec;
@@ -724,10 +732,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 +807,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 +939,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;
-
-    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];
+    struct search *sr, *oldest = NULL;
+
+    /* 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;
+    }
+
+    /* 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;
+        }
     }
-    return sr;
+
+    /* Oh, well, never mind.  Reuse the oldest slot. */
+    return oldest;
 }
 
 /* Insert the contents of a bucket into a search structure. */
@@ -945,23 +991,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 +1019,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;
     }
 
@@ -989,7 +1036,7 @@ dht_search(int s, const unsigned char *id, int port,
     b = find_bucket(id);
     insert_search_bucket(b, sr);
 
-    if(sr->numnodes < 8) {
+    if(sr->numnodes < SEARCH_NODES) {
         struct bucket *p = previous_bucket(b);
         if(b->next)
             insert_search_bucket(b->next, sr);
@@ -1107,24 +1154,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. */
@@ -1206,7 +1255,7 @@ dht_nodes(int *good_return, int *dubious_return, int *cached_return,
     if(cached_return)
         *cached_return = cached;
     if(incoming_return)
-        *incoming_return = cached;
+        *incoming_return = incoming;
     return good + dubious;
 }
                 
@@ -1214,9 +1263,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 +1300,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 +1319,7 @@ dht_dump_tables(FILE *f)
                    find_node(n->id) ? " (known)" : "",
                    n->replied ? " (replied)" : "");
         }
+        sr = sr->next;
     }
 
     
@@ -1305,6 +1355,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 +1428,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;
 }
 
@@ -1546,7 +1603,7 @@ dht_periodic(int s, int available, time_t *tosleep,
                 debugf("Got reply to announce_peer.\n");
                 sr = find_search(ttid);
                 if(!sr) {
-                    debugf("Unknown search!");
+                    debugf("Unknown search!\n");
                     new_node(s, id, &source, 1);
                 } else {
                     int i;
@@ -1571,7 +1628,7 @@ dht_periodic(int s, int available, time_t *tosleep,
         case PING:
             debugf("Ping (%d)!\n", tid_len);
             new_node(s, id, &source, 1);
-            debugf("Sending pong!\n");
+            debugf("Sending pong.\n");
             send_pong(s, (struct sockaddr*)&source, sizeof(source),
                       tid, tid_len);
             break;
@@ -1654,26 +1711,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;
         }
     }