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

Last change on this file since 9323 was 9323, checked in by livings124, 13 years ago

#2514 update to dht-0.10

File size: 8.5 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_in 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
91int
92main(int argc, char **argv)
93{
94    int i, rc, fd;
95    int s, port;
96    int have_id = 0;
97    unsigned char myid[20];
98    time_t tosleep = 0;
99
100    /* Ids need to be distributed evenly, so you cannot just use your
101       bittorrent id.  Either generate it randomly, or take the SHA-1 of
102       something. */
103    fd = open("dht-example.id", O_RDONLY);
104    if(fd >= 0) {
105        rc = read(fd, myid, 20);
106        if(rc == 20)
107            have_id = 1;
108        close(fd);
109    }
110   
111    if(!have_id) {
112        fd = open("/dev/urandom", O_RDONLY);
113        if(fd < 0) {
114            perror("open(random)");
115            exit(1);
116        }
117        rc = read(fd, myid, 20);
118        if(rc < 0) {
119            perror("read(random)");
120            exit(1);
121        }
122        have_id = 1;
123        close(fd);
124
125        fd = open("dht-example.id", O_WRONLY | O_CREAT | O_TRUNC, 0666);
126        if(fd >= 0) {
127            rc = write(fd, myid, 20);
128            if(rc < 20)
129                unlink("dht-example.id");
130            close(fd);
131        }
132    }
133
134    if(argc < 2)
135        goto usage;
136
137    i = 1;
138
139    if(argc < i + 1)
140        goto usage;
141
142    port = atoi(argv[i++]);
143    if(port <= 0 || port >= 0x10000)
144        goto usage;
145
146    while(i < argc) {
147        struct addrinfo hints, *info, *infop;
148        memset(&hints, 0, sizeof(hints));
149        hints.ai_family = AF_INET;
150        hints.ai_socktype = SOCK_DGRAM;
151        rc = getaddrinfo(argv[i], NULL, &hints, &info);
152        if(rc != 0) {
153            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc));
154            exit(1);
155        }
156
157        i++;
158        if(i >= argc)
159            goto usage;
160
161        infop = info;
162        while(infop) {
163            if(infop->ai_addr->sa_family == AF_INET) {
164                struct sockaddr_in sin;
165                memcpy(&sin, infop->ai_addr, infop->ai_addrlen);
166                sin.sin_port = htons(atoi(argv[i]));
167                bootstrap_nodes[num_bootstrap_nodes] = sin;
168                num_bootstrap_nodes++;
169            }
170            infop = infop->ai_next;
171        }
172        freeaddrinfo(info);
173
174        i++;
175    }
176
177    /* If you set dht_debug to a stream, every action taken by the DHT will
178       be logged. */
179    dht_debug = stdout;
180
181    /* We need an IPv4 socket, bound to a stable port.  Rumour has it that
182       uTorrent works better when it is the same as your Bittorrent port. */
183    s = socket(PF_INET, SOCK_DGRAM, 0);
184    if(s < 0) {
185        perror("socket");
186        exit(1);
187    }
188
189    {
190        struct sockaddr_in sin;
191        memset(&sin, 0, sizeof(sin));
192        sin.sin_family = AF_INET;
193        sin.sin_port = htons(port);
194        rc = bind(s, (struct sockaddr*)&sin, sizeof(sin));
195        if(rc < 0) {
196            perror("bind");
197            exit(1);
198        }
199    }
200
201    /* Init the dht.  This sets the socket into non-blocking mode. */
202    rc = dht_init(s, myid, NULL);
203    if(rc < 0) {
204        perror("dht_init");
205        exit(1);
206    }
207
208    init_signals();
209
210    /* For bootstrapping, we need an initial list of nodes.  This could be
211       hard-wired, but can also be obtained from the nodes key of a torrent
212       file, or from the PORT bittorrent message.
213
214       Dht_ping_node is the brutal way of bootstrapping -- it actually
215       sends a message to the peer.  If you're going to bootstrap from
216       a massive number of nodes (for example because you're restoring from
217       a dump) and you already know their ids, it's better to use
218       dht_insert_node.  If the ids are incorrect, the DHT will recover. */
219    for(i = 0; i < num_bootstrap_nodes; i++) {
220        dht_ping_node(s, &bootstrap_nodes[i]);
221        usleep(random() % 100000);
222    }
223
224    while(1) {
225        struct timeval tv;
226        fd_set readfds;
227        tv.tv_sec = tosleep;
228        tv.tv_usec = random() % 1000000;
229
230        FD_ZERO(&readfds);
231        FD_SET(s, &readfds);
232        rc = select(s + 1, &readfds, NULL, NULL, &tv);
233        if(rc < 0) {
234            if(errno != EINTR) {
235                perror("select");
236                sleep(1);
237            }
238        }
239       
240        if(exiting)
241            break;
242
243        rc = dht_periodic(s, rc > 0, &tosleep, callback, NULL);
244        if(rc < 0) {
245            if(errno == EINTR) {
246                continue;
247            } else {
248                perror("dht_periodic");
249                if(rc == EINVAL || rc == EFAULT)
250                    abort();
251                tosleep = 1;
252            }
253        }
254
255        /* This is how you trigger a search for a torrent hash.  If port
256           (the third argument) is non-zero, it also performs an announce.
257           Since peers expire announced data after 30 minutes, it's a good
258           idea to reannounce every 28 minutes or so. */
259        if(searching) {
260            dht_search(s, hash, 0, callback, NULL);
261            searching = 0;
262        }
263
264        /* For debugging, or idle curiosity. */
265        if(dumping) {
266            dht_dump_tables(stdout);
267            dumping = 0;
268        }
269    }
270
271    {
272        struct sockaddr_in sins[500];
273        int i;
274        i = dht_get_nodes(sins, 500);
275        printf("Found %d good nodes.\n", i);
276    }
277
278    dht_uninit(s, 1);
279    return 0;
280   
281 usage:
282    fprintf(stderr, "Foo!\n");
283    exit(1);
284}
285
286/* We need to provide a reasonably strong cryptographic hashing function.
287   Here's how we'd do it if we had RSA's MD5 code. */
288#if 0
289void
290dht_hash(void *hash_return, int hash_size,
291         const void *v1, int len1,
292         const void *v2, int len2,
293         const void *v3, int len3)
294{
295    static MD5_CTX ctx;
296    MD5Init(&ctx);
297    MD5Update(&ctx, v1, len1);
298    MD5Update(&ctx, v2, len2);
299    MD5Update(&ctx, v3, len3);
300    MD5Final(&ctx);
301    if(hash_size > 16)
302        memset((char*)hash_return + 16, 0, hash_size - 16);
303    memcpy(hash_return, ctx.digest, hash_size > 16 ? 16 : hash_size);
304}
305#else
306/* But for this example, we might as well use something weaker. */
307void
308dht_hash(void *hash_return, int hash_size,
309         const void *v1, int len1,
310         const void *v2, int len2,
311         const void *v3, int len3)
312{
313    const char *c1 = v1, *c2 = v2, *c3 = v3;
314    char key[9];                /* crypt is limited to 8 characters */
315    int i;
316
317    memset(key, 0, 9);
318#define CRYPT_HAPPY(c) ((c % 0x60) + 0x20)
319
320    for(i = 0; i < 2 && i < len1; i++)
321        key[i] = CRYPT_HAPPY(c1[i]);
322    for(i = 0; i < 4 && i < len1; i++)
323        key[2 + i] = CRYPT_HAPPY(c2[i]);
324    for(i = 0; i < 2 && i < len1; i++)
325        key[6 + i] = CRYPT_HAPPY(c3[i]);
326    strncpy(hash_return, crypt(key, "jc"), hash_size);
327}
328#endif
329
330int
331dht_random_bytes(void *buf, size_t size)
332{
333    int fd, rc, save;
334
335    fd = open("/dev/urandom", O_RDONLY);
336    if(fd < 0)
337        return -1;
338
339    rc = read(fd, buf, size);
340
341    save = errno;
342    close(fd);
343    errno = save;
344
345    return rc;
346}
Note: See TracBrowser for help on using the repository browser.