source: trunk/third-party/dht/dht-example.c @ 11663

Last change on this file since 11663 was 11663, checked in by jch, 11 years ago

Import dht-0.17.

File size: 11.7 KB
Line 
1/* This example code was written by Juliusz Chroboczek.
2   You are free to cut'n'paste from it to your heart's content. */
3
4/* For crypt */
5#define _GNU_SOURCE
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <errno.h>
10#include <string.h>
11#include <unistd.h>
12#include <fcntl.h>
13#include <sys/time.h>
14#include <arpa/inet.h>
15#include <sys/types.h>
16#include <sys/socket.h>
17#include <netdb.h>
18#include <sys/signal.h>
19
20#include "dht.h"
21
22#define MAX_BOOTSTRAP_NODES 20
23static struct sockaddr_storage bootstrap_nodes[MAX_BOOTSTRAP_NODES];
24static int num_bootstrap_nodes = 0;
25
26static volatile sig_atomic_t dumping = 0, searching = 0, exiting = 0;
27
28static void
29sigdump(int signo)
30{
31    dumping = 1;
32}
33
34static void
35sigtest(int signo)
36{
37    searching = 1;
38}
39
40static void
41sigexit(int signo)
42{
43    exiting = 1;
44}
45
46static void
47init_signals(void)
48{
49    struct sigaction sa;
50    sigset_t ss;
51
52    sigemptyset(&ss);
53    sa.sa_handler = sigdump;
54    sa.sa_mask = ss;
55    sa.sa_flags = 0;
56    sigaction(SIGUSR1, &sa, NULL);
57
58    sigemptyset(&ss);
59    sa.sa_handler = sigtest;
60    sa.sa_mask = ss;
61    sa.sa_flags = 0;
62    sigaction(SIGUSR2, &sa, NULL);
63
64    sigemptyset(&ss);
65    sa.sa_handler = sigexit;
66    sa.sa_mask = ss;
67    sa.sa_flags = 0;
68    sigaction(SIGINT, &sa, NULL);
69}
70
71const unsigned char hash[20] = {
72    0x54, 0x57, 0x87, 0x89, 0xdf, 0xc4, 0x23, 0xee, 0xf6, 0x03,
73    0x1f, 0x81, 0x94, 0xa9, 0x3a, 0x16, 0x98, 0x8b, 0x72, 0x7b
74};
75
76/* The call-back function is called by the DHT whenever something
77   interesting happens.  Right now, it only happens when we get a new value or
78   when a search completes, but this may be extended in future versions. */
79static void
80callback(void *closure,
81         int event,
82         unsigned char *info_hash,
83         void *data, size_t data_len)
84{
85    if(event == DHT_EVENT_SEARCH_DONE)
86        printf("Search done.\n");
87    else if(event == DHT_EVENT_VALUES)
88        printf("Received %d values.\n", (int)(data_len / 6));
89}
90
91static unsigned char buf[4096];
92
93int
94main(int argc, char **argv)
95{
96    int i, rc, fd;
97    int s = -1, s6 = -1, port;
98    int have_id = 0;
99    unsigned char myid[20];
100    time_t tosleep = 0;
101    char *id_file = "dht-example.id";
102    int opt;
103    int quiet = 0, ipv4 = 1, ipv6 = 1;
104    struct sockaddr_in sin;
105    struct sockaddr_in6 sin6;
106    struct sockaddr_storage from;
107    socklen_t fromlen;
108
109    memset(&sin, 0, sizeof(sin));
110    sin.sin_family = AF_INET;
111
112    memset(&sin6, 0, sizeof(sin6));
113    sin6.sin6_family = AF_INET6;
114
115
116
117    while(1) {
118        opt = getopt(argc, argv, "q46b:i:");
119        if(opt < 0)
120            break;
121
122        switch(opt) {
123        case 'q': quiet = 1; break;
124        case '4': ipv6 = 0; break;
125        case '6': ipv4 = 0; break;
126        case 'b': {
127            char buf[16];
128            int rc;
129            rc = inet_pton(AF_INET, optarg, buf);
130            if(rc == 1) {
131                memcpy(&sin.sin_addr, buf, 4);
132                break;
133            }
134            rc = inet_pton(AF_INET6, optarg, buf);
135            if(rc == 1) {
136                memcpy(&sin6.sin6_addr, buf, 16);
137                break;
138            }
139            goto usage;
140        }
141            break;
142        case 'i':
143            id_file = optarg;
144            break;
145        default:
146            goto usage;
147        }
148    }
149
150    /* Ids need to be distributed evenly, so you cannot just use your
151       bittorrent id.  Either generate it randomly, or take the SHA-1 of
152       something. */
153    fd = open(id_file, O_RDONLY);
154    if(fd >= 0) {
155        rc = read(fd, myid, 20);
156        if(rc == 20)
157            have_id = 1;
158        close(fd);
159    }
160   
161    fd = open("/dev/urandom", O_RDONLY);
162    if(fd < 0) {
163        perror("open(random)");
164        exit(1);
165    }
166
167    if(!have_id) {
168        int ofd;
169
170        rc = read(fd, myid, 20);
171        if(rc < 0) {
172            perror("read(random)");
173            exit(1);
174        }
175        have_id = 1;
176        close(fd);
177
178        ofd = open(id_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
179        if(ofd >= 0) {
180            rc = write(ofd, myid, 20);
181            if(rc < 20)
182                unlink(id_file);
183            close(ofd);
184        }
185    }
186
187    {
188        unsigned seed;
189        read(fd, &seed, sizeof(seed));
190        srandom(seed);
191    }
192
193    close(fd);
194
195    if(argc < 2)
196        goto usage;
197
198    i = optind;
199
200    if(argc < i + 1)
201        goto usage;
202
203    port = atoi(argv[i++]);
204    if(port <= 0 || port >= 0x10000)
205        goto usage;
206
207    while(i < argc) {
208        struct addrinfo hints, *info, *infop;
209        memset(&hints, 0, sizeof(hints));
210        hints.ai_socktype = SOCK_DGRAM;
211        if(!ipv6)
212            hints.ai_family = AF_INET;
213        else if(!ipv4)
214            hints.ai_family = AF_INET6;
215        else
216            hints.ai_family = 0;
217        rc = getaddrinfo(argv[i], argv[i + 1], &hints, &info);
218        if(rc != 0) {
219            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc));
220            exit(1);
221        }
222
223        i++;
224        if(i >= argc)
225            goto usage;
226
227        infop = info;
228        while(infop) {
229            memcpy(&bootstrap_nodes[num_bootstrap_nodes],
230                   infop->ai_addr, infop->ai_addrlen);
231            infop = infop->ai_next;
232            num_bootstrap_nodes++;
233        }
234        freeaddrinfo(info);
235
236        i++;
237    }
238
239    /* If you set dht_debug to a stream, every action taken by the DHT will
240       be logged. */
241    if(!quiet)
242        dht_debug = stdout;
243
244    /* We need an IPv4 and an IPv6 socket, bound to a stable port.  Rumour
245       has it that uTorrent works better when it is the same as your
246       Bittorrent port. */
247    if(ipv4) {
248        s = socket(PF_INET, SOCK_DGRAM, 0);
249        if(s < 0) {
250            perror("socket(IPv4)");
251        }
252    }
253
254    if(ipv6) {
255        s6 = socket(PF_INET6, SOCK_DGRAM, 0);
256        if(s6 < 0) {
257            perror("socket(IPv6)");
258        }
259    }
260
261    if(s < 0 && s6 < 0) {
262        fprintf(stderr, "Eek!");
263        exit(1);
264    }
265
266
267    if(s >= 0) {
268        sin.sin_port = htons(port);
269        rc = bind(s, (struct sockaddr*)&sin, sizeof(sin));
270        if(rc < 0) {
271            perror("bind(IPv4)");
272            exit(1);
273        }
274    }
275
276    if(s6 >= 0) {
277        int rc;
278        int val = 1;
279
280        rc = setsockopt(s6, IPPROTO_IPV6, IPV6_V6ONLY,
281                        (char *)&val, sizeof(val));
282        if(rc < 0) {
283            perror("setsockopt(IPV6_V6ONLY)");
284            exit(1);
285        }
286
287        /* BEP-32 mandates that we should bind this socket to one of our
288           global IPv6 addresses.  In this simple example, this only
289           happens if the user used the -b flag. */
290
291        sin6.sin6_port = htons(port);
292        rc = bind(s6, (struct sockaddr*)&sin6, sizeof(sin6));
293        if(rc < 0) {
294            perror("bind(IPv6)");
295            exit(1);
296        }
297    }
298
299    /* Init the dht.  This sets the socket into non-blocking mode. */
300    rc = dht_init(s, s6, myid, (unsigned char*)"JC\0\0");
301    if(rc < 0) {
302        perror("dht_init");
303        exit(1);
304    }
305
306    init_signals();
307
308    /* For bootstrapping, we need an initial list of nodes.  This could be
309       hard-wired, but can also be obtained from the nodes key of a torrent
310       file, or from the PORT bittorrent message.
311
312       Dht_ping_node is the brutal way of bootstrapping -- it actually
313       sends a message to the peer.  If you're going to bootstrap from
314       a massive number of nodes (for example because you're restoring from
315       a dump) and you already know their ids, it's better to use
316       dht_insert_node.  If the ids are incorrect, the DHT will recover. */
317    for(i = 0; i < num_bootstrap_nodes; i++) {
318        dht_ping_node((struct sockaddr*)&bootstrap_nodes[i],
319                      sizeof(bootstrap_nodes[i]));
320        usleep(random() % 100000);
321    }
322
323    while(1) {
324        struct timeval tv;
325        fd_set readfds;
326        tv.tv_sec = tosleep;
327        tv.tv_usec = random() % 1000000;
328
329        FD_ZERO(&readfds);
330        if(s >= 0)
331            FD_SET(s, &readfds);
332        if(s6 >= 0)
333            FD_SET(s6, &readfds);
334        rc = select(s > s6 ? s + 1 : s6 + 1, &readfds, NULL, NULL, &tv);
335        if(rc < 0) {
336            if(errno != EINTR) {
337                perror("select");
338                sleep(1);
339            }
340        }
341       
342        if(exiting)
343            break;
344
345        if(rc > 0) {
346            fromlen = sizeof(from);
347            if(s >= 0 && FD_ISSET(s, &readfds))
348                rc = recvfrom(s, buf, sizeof(buf) - 1, 0,
349                              (struct sockaddr*)&from, &fromlen);
350            else if(s6 >= 0 && FD_ISSET(s6, &readfds))
351                rc = recvfrom(s6, buf, sizeof(buf) - 1, 0,
352                              (struct sockaddr*)&from, &fromlen);
353            else
354                abort();
355        }
356
357        if(rc > 0) {
358            buf[rc] = '\0';
359            rc = dht_periodic(buf, rc, (struct sockaddr*)&from, fromlen,
360                              &tosleep, callback, NULL);
361        } else {
362            rc = dht_periodic(NULL, 0, NULL, 0, &tosleep, callback, NULL);
363        }
364        if(rc < 0) {
365            if(errno == EINTR) {
366                continue;
367            } else {
368                perror("dht_periodic");
369                if(rc == EINVAL || rc == EFAULT)
370                    abort();
371                tosleep = 1;
372            }
373        }
374
375        /* This is how you trigger a search for a torrent hash.  If port
376           (the second argument) is non-zero, it also performs an announce.
377           Since peers expire announced data after 30 minutes, it's a good
378           idea to reannounce every 28 minutes or so. */
379        if(searching) {
380            if(s >= 0)
381                dht_search(hash, 0, AF_INET, callback, NULL);
382            if(s6 >= 0)
383                dht_search(hash, 0, AF_INET6, callback, NULL);
384            searching = 0;
385        }
386
387        /* For debugging, or idle curiosity. */
388        if(dumping) {
389            dht_dump_tables(stdout);
390            dumping = 0;
391        }
392    }
393
394    {
395        struct sockaddr_in sin[500];
396        struct sockaddr_in6 sin6[500];
397        int num = 500, num6 = 500;
398        int i;
399        i = dht_get_nodes(sin, &num, sin6, &num6);
400        printf("Found %d (%d + %d) good nodes.\n", i, num, num6);
401    }
402
403    dht_uninit();
404    return 0;
405   
406 usage:
407    printf("Usage: dht-example [-q] [-4] [-6] [-i filename] [-b address]...\n"
408           "                   port [address port]...\n");
409    exit(1);
410}
411
412/* We need to provide a reasonably strong cryptographic hashing function.
413   Here's how we'd do it if we had RSA's MD5 code. */
414#if 0
415void
416dht_hash(void *hash_return, int hash_size,
417         const void *v1, int len1,
418         const void *v2, int len2,
419         const void *v3, int len3)
420{
421    static MD5_CTX ctx;
422    MD5Init(&ctx);
423    MD5Update(&ctx, v1, len1);
424    MD5Update(&ctx, v2, len2);
425    MD5Update(&ctx, v3, len3);
426    MD5Final(&ctx);
427    if(hash_size > 16)
428        memset((char*)hash_return + 16, 0, hash_size - 16);
429    memcpy(hash_return, ctx.digest, hash_size > 16 ? 16 : hash_size);
430}
431#else
432/* But for this example, we might as well use something weaker. */
433void
434dht_hash(void *hash_return, int hash_size,
435         const void *v1, int len1,
436         const void *v2, int len2,
437         const void *v3, int len3)
438{
439    const char *c1 = v1, *c2 = v2, *c3 = v3;
440    char key[9];                /* crypt is limited to 8 characters */
441    int i;
442
443    memset(key, 0, 9);
444#define CRYPT_HAPPY(c) ((c % 0x60) + 0x20)
445
446    for(i = 0; i < 2 && i < len1; i++)
447        key[i] = CRYPT_HAPPY(c1[i]);
448    for(i = 0; i < 4 && i < len1; i++)
449        key[2 + i] = CRYPT_HAPPY(c2[i]);
450    for(i = 0; i < 2 && i < len1; i++)
451        key[6 + i] = CRYPT_HAPPY(c3[i]);
452    strncpy(hash_return, crypt(key, "jc"), hash_size);
453}
454#endif
455
456int
457dht_random_bytes(void *buf, size_t size)
458{
459    int fd, rc, save;
460
461    fd = open("/dev/urandom", O_RDONLY);
462    if(fd < 0)
463        return -1;
464
465    rc = read(fd, buf, size);
466
467    save = errno;
468    close(fd);
469    errno = save;
470
471    return rc;
472}
Note: See TracBrowser for help on using the repository browser.