1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-03-03 02:05:19 +00:00

(trunk third-party) #2302: upgrade from dht 0.6 to 0.8.

This commit is contained in:
Charles Kerr 2009-07-29 03:06:18 +00:00
parent 0b57127650
commit c33f27c06c
3 changed files with 129 additions and 62 deletions

View file

@ -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.

View 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.

162
third-party/dht/dht.c vendored
View 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;
@ -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;
}
}