diff --git a/routing/kademlia.c b/routing/kademlia.c index 42140f0..d02e574 100644 --- a/routing/kademlia.c +++ b/routing/kademlia.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,22 @@ static struct sockaddr_storage bootstrap_nodes[MAX_BOOTSTRAP_NODES]; static int num_bootstrap_nodes = 0; +pthread_t pth; +time_t tosleep = 0; +int ksock = -1; +int net_family = 0; +volatile int searching = 0; // search lock, -1 to busy, 0 to free, 1 to running. +volatile char hash[20]; // hash to be search or announce. +volatile int announce_port = 0; + +struct bs_list { + char *ip; + uint16_t port; +} bootstrap_list[] = { + { "127.0.0.1", 1234 }, + { "127.0.0.1", 4321 } +}; + /* The call-back function is called by the DHT whenever something 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. */ @@ -43,6 +60,126 @@ callback(void *closure, } } +void *kademlia_thread (void *ptr) +{ + int rc; + struct timeval tv; + fd_set readfds; + char buf[4096]; + struct sockaddr from; + socklen_t fromlen; + + for(;;) { + tv.tv_sec = tosleep; + tv.tv_usec = random() % 1000000; + + FD_ZERO(&readfds); + FD_SET(ksock, &readfds); + rc = select(ksock + 1, &readfds, NULL, NULL, &tv); + if(rc < 0) { + if(errno != EINTR) { + perror("select"); + sleep(1); + } + } + + if(rc > 0 && FD_ISSET(ksock, &readfds)) { + fromlen = sizeof(from); + rc = recvfrom(ksock, buf, sizeof(buf) - 1, 0, + (struct sockaddr*)&from, &fromlen); + buf[rc] = '\0'; + rc = dht_periodic(buf, rc, (struct sockaddr*)&from, fromlen, + &tosleep, callback, NULL); + } else { + rc = dht_periodic(NULL, 0, NULL, 0, &tosleep, callback, NULL); + } + if(rc < 0) { + if(errno == EINTR) { + continue; + } else { + perror("dht_periodic"); + if(rc == EINVAL || rc == EFAULT) + abort(); + tosleep = 1; + } + } + + /* This is how you trigger a search for a torrent hash. If port + (the second argument) is non-zero, it also performs an announce. + Since peers expire announced data after 30 minutes, it's a good + idea to reannounce every 28 minutes or so. */ + if(searching > 0) { + char h[sizeof hash]; + int i; + for (i = 0 ; i < sizeof hash ; i++) { + h[i] = hash[i]; + } + dht_search(h, announce_port, net_family, callback, NULL); + searching = 0; + } + } +} + +int bootstrap_kademlia(int sock, int family, char* peer_id, int timeout) +{ + int rc, i, len; + unsigned char id[20]; + struct sockaddr_in sa; + + len = sizeof(bootstrap_list) / sizeof(bootstrap_list[0]); // array length + + if (len > MAX_BOOTSTRAP_NODES) { + len = MAX_BOOTSTRAP_NODES; // limit array length + } + + memset(&sa, 0, sizeof sa); + for (i = 0 ; i < len ; i++) { + if (family == AF_INET6 && inet_pton(AF_INET6, bootstrap_list[i].ip, &(sa.sin_addr.s_addr)) == 1) { + sa.sin_family = AF_INET6; + } else if (inet_pton(AF_INET, bootstrap_list[i].ip, &(sa.sin_addr.s_addr)) == 1) { + sa.sin_family = AF_INET; + } else { + continue; // not an ipv6 or ipv4? + } + + sa.sin_port = htons (bootstrap_list[i].port); + + memcpy(&bootstrap_nodes[num_bootstrap_nodes++], &sa, sizeof(sa)); + } + + dht_hash (id, sizeof(id), peer_id, strlen(peer_id), NULL, 0, NULL, 0); + + if (family == AF_INET6) { + rc = dht_init(-1, sock, id, NULL); + } else { + rc = dht_init(sock, -1, id, NULL); + } + if (rc < 0) { + return rc; + } + + /* For bootstrapping, we need an initial list of nodes. This could be + hard-wired, but can also be obtained from the nodes key of a torrent + file, or from the PORT bittorrent message. + + Dht_ping_node is the brutal way of bootstrapping -- it actually + sends a message to the peer. If you're going to bootstrap from + a massive number of nodes (for example because you're restoring from + a dump) and you already know their ids, it's better to use + dht_insert_node. If the ids are incorrect, the DHT will recover. */ + for(i = 0; i < num_bootstrap_nodes; i++) { + dht_ping_node((struct sockaddr*)&bootstrap_nodes[i], + sizeof (bootstrap_nodes[i])); + usleep(random() % 100000); + } + + ksock = sock; + net_family = family; + tosleep = timeout; + + return pthread_create(&pth, NULL, kademlia_thread, NULL); +} + /* Functions called by the DHT. */ int dht_blacklisted (const struct sockaddr *sa, int salen)