X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=dht-bootstrap.c;h=2627d828903ac696096c47a093eecafcba0fdb8f;hb=fe34c1da24280d233983a9b85fb05a64b56fccc8;hp=1c5d37513b877902db006519df86b5a858b21b44;hpb=0c2b6f0d5e25c81c4ead1eeaa8b4af320958c927;p=dht-bootstrap.git diff --git a/dht-bootstrap.c b/dht-bootstrap.c index 1c5d375..2627d82 100644 --- a/dht-bootstrap.c +++ b/dht-bootstrap.c @@ -20,76 +20,22 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* For memmem. */ -#define _GNU_SOURCE - -#include -#include +#include #include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include #include -#include +#include +#include +#include +#include +#include #include -#include +#include +#include -#ifndef HAVE_MEMMEM -#ifdef __GLIBC__ -#define HAVE_MEMMEM -#endif -#endif - -#ifndef MSG_CONFIRM -#define MSG_CONFIRM 0 -#endif - -/* We set sin_family to 0 to mark unused slots. */ -#if AF_INET == 0 || AF_INET6 == 0 -#error You lose -#endif - -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -/* nothing */ -#elif defined(__GNUC__) -#define inline __inline -#if (__GNUC__ >= 3) -#define restrict __restrict -#else -#define restrict /**/ -#endif -#else -#define inline /**/ -#define restrict /**/ -#endif - -#define MAX(x, y) ((x) >= (y) ? (x) : (y)) #define MIN(x, y) ((x) <= (y) ? (x) : (y)) -static int send_ping(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len); -static int send_pong(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len); -static int send_find_node(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len, - const unsigned char *target, int want, int confirm); -static int send_nodes(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len, - const unsigned char *nodes, int nodes_len, - const unsigned char *nodes6, int nodes6_len); -static int send_random_nodes(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len, - const unsigned char *id, int want); -static int send_error(struct sockaddr *sa, int salen, - unsigned char *tid, int tid_len, - int code, const char *message); - #define ERROR 0 #define REPLY 1 #define PING 2 @@ -100,31 +46,11 @@ static int send_error(struct sockaddr *sa, int salen, #define WANT4 1 #define WANT6 2 -static int parse_message(const unsigned char *buf, int buflen, - unsigned char *tid_return, int *tid_len, - unsigned char *id_return, - unsigned char *info_hash_return, - unsigned char *target_return, - unsigned short *port_return, - unsigned char *token_return, int *token_len, - unsigned char *nodes_return, int *nodes_len, - unsigned char *nodes6_return, int *nodes6_len, - unsigned char *values_return, int *values_len, - unsigned char *values6_return, int *values6_len, - int *want_return); - static const unsigned char zeroes[20] = {0}; -static const unsigned char ones[20] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF -}; -static const unsigned char v4prefix[16] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 -}; +static const unsigned char v4prefix[16] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0}; static unsigned char myid[20]; -static int have_v = 0; static unsigned char my_v[9]; static int dht_socket = -1; @@ -135,7 +61,7 @@ struct node { struct sockaddr_storage ss; socklen_t sslen; }; - + #define CIRCULAR_LIST_SIZE 256 struct circular_list { @@ -144,49 +70,315 @@ struct circular_list { struct node nodes[CIRCULAR_LIST_SIZE]; }; -struct circular_list v4_new, v6_new, v4_confirmed, v6_confirmed; +static struct circular_list v4_new, v6_new, v4_confirmed, v6_confirmed; #define MAX_TOKEN_BUCKET_TOKENS 40 static time_t token_bucket_time; static int token_bucket_tokens; -FILE *dht_debug = NULL; +static int verbose = 1; -#ifdef __GNUC__ - __attribute__ ((format (printf, 1, 2))) -#endif -static void -debugf(const char *format, ...) +// ------------------------ >8 ------------------------ + +static const char * +getAddr(const struct sockaddr *sa, const size_t saLen) +{ + static char host[NI_MAXHOST]; + static char port[NI_MAXSERV]; + static char addr[64]; + int rc = getnameinfo( + sa, + saLen, + host, + sizeof host, + port, + sizeof port, + NI_NUMERICHOST | NI_NUMERICSERV); + if (rc != 0) { + fprintf(stderr, "getnameinfo: %s\n", gai_strerror(rc)); + return NULL; + } + snprintf(addr, sizeof addr, "[%s]:%s", host, port); + return addr; +} + +static int +parse_message( + const unsigned char *buf, + int buflen, + unsigned char *tid_return, + int *tid_len, + unsigned char *id_return, + unsigned char *info_hash_return, + unsigned char *target_return, + unsigned short *port_return, + unsigned char *token_return, + int *token_len, + unsigned char *nodes_return, + int *nodes_len, + unsigned char *nodes6_return, + int *nodes6_len, + unsigned char *values_return, + int *values_len, + unsigned char *values6_return, + int *values6_len, + int *want_return, + const struct sockaddr *sa, + const size_t saLen) { - va_list args; - va_start(args, format); - if(dht_debug) - vfprintf(dht_debug, format, args); - va_end(args); - fflush(dht_debug); + const unsigned char *p; + + /* This code will happily crash if the buffer is not NUL-terminated. */ + if (buf[buflen] != '\0') { + if (verbose) + printf("%s : parse_message with unterminated buffer\n", getAddr(sa, saLen)); + return -1; + } + +#define CHECK(ptr, len) \ + if (((unsigned char *)ptr) + (len) > (buf) + (buflen)) \ + goto overflow; + + if (tid_return) { + p = memmem(buf, buflen, "1:t", 3); + if (p) { + long l; + char *q; + l = strtol((char *)p + 3, &q, 10); + if (q && *q == ':' && l > 0 && l < *tid_len) { + CHECK(q + 1, l); + memcpy(tid_return, q + 1, l); + *tid_len = l; + } else + *tid_len = 0; + } + } + if (id_return) { + p = memmem(buf, buflen, "2:id20:", 7); + if (p) { + CHECK(p + 7, 20); + memcpy(id_return, p + 7, 20); + } else { + memset(id_return, 0, 20); + } + } + if (info_hash_return) { + p = memmem(buf, buflen, "9:info_hash20:", 14); + if (p) { + CHECK(p + 14, 20); + memcpy(info_hash_return, p + 14, 20); + } else { + memset(info_hash_return, 0, 20); + } + } + if (port_return) { + p = memmem(buf, buflen, "porti", 5); + if (p) { + long l; + char *q; + l = strtol((char *)p + 5, &q, 10); + if (q && *q == 'e' && l > 0 && l < 0x10000) + *port_return = l; + else + *port_return = 0; + } else + *port_return = 0; + } + if (target_return) { + p = memmem(buf, buflen, "6:target20:", 11); + if (p) { + CHECK(p + 11, 20); + memcpy(target_return, p + 11, 20); + } else { + memset(target_return, 0, 20); + } + } + if (token_return) { + p = memmem(buf, buflen, "5:token", 7); + if (p) { + long l; + char *q; + l = strtol((char *)p + 7, &q, 10); + if (q && *q == ':' && l > 0 && l < *token_len) { + CHECK(q + 1, l); + memcpy(token_return, q + 1, l); + *token_len = l; + } else + *token_len = 0; + } else + *token_len = 0; + } + + if (nodes_len) { + p = memmem(buf, buflen, "5:nodes", 7); + if (p) { + long l; + char *q; + l = strtol((char *)p + 7, &q, 10); + if (q && *q == ':' && l > 0 && l < *nodes_len) { + CHECK(q + 1, l); + memcpy(nodes_return, q + 1, l); + *nodes_len = l; + } else + *nodes_len = 0; + } else + *nodes_len = 0; + } + + if (nodes6_len) { + p = memmem(buf, buflen, "6:nodes6", 8); + if (p) { + long l; + char *q; + l = strtol((char *)p + 8, &q, 10); + if (q && *q == ':' && l > 0 && l < *nodes6_len) { + CHECK(q + 1, l); + memcpy(nodes6_return, q + 1, l); + *nodes6_len = l; + } else + *nodes6_len = 0; + } else + *nodes6_len = 0; + } + + if (values_len || values6_len) { + p = memmem(buf, buflen, "6:valuesl", 9); + if (p) { + int i = p - buf + 9; + int j = 0, j6 = 0; + while (1) { + long l; + char *q; + l = strtol((char *)buf + i, &q, 10); + if (q && *q == ':' && l > 0) { + CHECK(q + 1, l); + if (l == 6) { + if (j + l > *values_len) + continue; + i = q + 1 + l - (char *)buf; + memcpy((char *)values_return + j, q + 1, l); + j += l; + } else if (l == 18) { + if (j6 + l > *values6_len) + continue; + i = q + 1 + l - (char *)buf; + memcpy((char *)values6_return + j6, q + 1, l); + j6 += l; + } else { + if (verbose) + printf( + "%s : received weird value: %d bytes\n", + getAddr(sa, saLen), + (int)l); + i = q + 1 + l - (char *)buf; + } + } else { + break; + } + } + if (i >= buflen || buf[i] != 'e') + if (verbose) + printf("%s : unexpected end for values\n", getAddr(sa, saLen)); + if (values_len) + *values_len = j; + if (values6_len) + *values6_len = j6; + } else { + *values_len = 0; + *values6_len = 0; + } + } + + if (want_return) { + p = memmem(buf, buflen, "4:wantl", 7); + if (p) { + int i = p - buf + 7; + *want_return = 0; + while (buf[i] > '0' && buf[i] <= '9' && buf[i + 1] == ':' && + i + 2 + buf[i] - '0' < buflen) { + CHECK(buf + i + 2, buf[i] - '0'); + if (buf[i] == '2' && memcmp(buf + i + 2, "n4", 2) == 0) + *want_return |= WANT4; + else if (buf[i] == '2' && memcmp(buf + i + 2, "n6", 2) == 0) + *want_return |= WANT6; + else if (verbose) + printf( + "%s : unexpected want flag: %c\n", getAddr(sa, saLen), buf[i]); + i += 2 + buf[i] - '0'; + } + if (i >= buflen || buf[i] != 'e') + if (verbose) + printf("%s : unexpected end for want\n", getAddr(sa, saLen)); + } else { + *want_return = -1; + } + } + +#undef CHECK + + if (memmem(buf, buflen, "1:y1:r", 6)) + return REPLY; + if (memmem(buf, buflen, "1:y1:e", 6)) + return ERROR; + if (!memmem(buf, buflen, "1:y1:q", 6)) + return -1; + if (memmem(buf, buflen, "1:q4:ping", 9)) + return PING; + if (memmem(buf, buflen, "1:q9:find_node", 14)) + return FIND_NODE; + if (memmem(buf, buflen, "1:q9:get_peers", 14)) + return GET_PEERS; + if (memmem(buf, buflen, "1:q13:announce_peer", 19)) + return ANNOUNCE_PEER; + return -1; + +overflow: + if (verbose) + printf(": truncated message\n"); + return -1; } +#undef CHECK +#undef INC +#undef COPY +#undef ADD_V + +/* We could use a proper bencoding printer and parser, but the format of + DHT messages is fairly stylised, so this seemed simpler. */ + +#define CHECK(offset, delta, size) \ + if (delta < 0 || offset + delta > size) \ + goto fail + +#define INC(offset, delta, size) \ + CHECK(offset, delta, size); \ + offset += delta + +#define COPY(buf, offset, src, delta, size) \ + CHECK(offset, delta, size); \ + memcpy(buf + offset, src, delta); \ + offset += delta; + +#define ADD_V(buf, offset, size) COPY(buf, offset, my_v, (sizeof my_v), size) + static int is_martian(struct sockaddr *sa) { - switch(sa->sa_family) { + switch (sa->sa_family) { case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in*)sa; - const unsigned char *address = (const unsigned char*)&sin->sin_addr; - return sin->sin_port == 0 || - (address[0] == 0) || - (address[0] == 127) || - ((address[0] & 0xE0) == 0xE0); + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + const unsigned char *address = (const unsigned char *)&sin->sin_addr; + return sin->sin_port == 0 || (address[0] == 0) || (address[0] == 127) || + ((address[0] & 0xE0) == 0xE0); } case AF_INET6: { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; - const unsigned char *address = (const unsigned char*)&sin6->sin6_addr; - return sin6->sin6_port == 0 || - (address[0] == 0xFF) || - (address[0] == 0xFE && (address[1] & 0xC0) == 0x80) || - (memcmp(address, zeroes, 15) == 0 && - (address[15] == 0 || address[15] == 1)) || - (memcmp(address, v4prefix, 12) == 0); + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + const unsigned char *address = (const unsigned char *)&sin6->sin6_addr; + return sin6->sin6_port == 0 || (address[0] == 0xFF) || + (address[0] == 0xFE && (address[1] & 0xC0) == 0x80) || + (memcmp(address, zeroes, 15) == 0 && + (address[15] == 0 || address[15] == 1)) || + (memcmp(address, v4prefix, 12) == 0); } default: @@ -217,11 +409,10 @@ make_tid(unsigned char *tid_return, const char *prefix, unsigned short seqno) } static int -tid_match(const unsigned char *tid, const char *prefix, - unsigned short *seqno_return) +tid_match(const unsigned char *tid, const char *prefix, unsigned short *seqno_return) { - if(tid[0] == (prefix[0] & 0xFF) && tid[1] == (prefix[1] & 0xFF)) { - if(seqno_return) + if (tid[0] == (prefix[0] & 0xFF) && tid[1] == (prefix[1] & 0xFF)) { + if (seqno_return) memcpy(seqno_return, tid + 2, 2); return 1; } else @@ -232,7 +423,7 @@ static inline int circular(int from, int to) { int x = to - from; - if(x < 0) + if (x < 0) return x + CIRCULAR_LIST_SIZE; return x; } @@ -256,10 +447,9 @@ list_free(struct circular_list *list) } static int -list_pop(struct circular_list *list, - struct sockaddr_storage *ss, socklen_t *sslen) +list_pop(struct circular_list *list, struct sockaddr_storage *ss, socklen_t *sslen) { - if(list->head == list->tail) + if (list->head == list->tail) return 0; memcpy(ss, &list->nodes[list->head].ss, list->nodes[list->head].sslen); @@ -269,17 +459,19 @@ list_pop(struct circular_list *list, } static int -list_random(struct circular_list *list, unsigned char *id, - struct sockaddr_storage *ss, socklen_t *sslen) +list_random( + struct circular_list *list, + unsigned char *id, + struct sockaddr_storage *ss, + socklen_t *sslen) { - int n; - if(list->head == list->tail) + if (list->head == list->tail) return 0; - n = random() % (list->tail - list->head); + int n = random() % (list->tail - list->head); n = (list->head + n) % CIRCULAR_LIST_SIZE; - if(id) + if (id) memcpy(id, &list->nodes[n].id, 20); memcpy(ss, &list->nodes[n].ss, list->nodes[n].sslen); *sslen = list->nodes[n].sslen; @@ -293,11 +485,9 @@ static int new_node(unsigned char *id, struct sockaddr *sa, socklen_t salen, int confirm) { struct circular_list *list; - int i; - - if(sa->sa_family == AF_INET) + if (sa->sa_family == AF_INET) list = confirm >= 2 ? &v4_confirmed : &v4_new; - else if(sa->sa_family == AF_INET6) + else if (sa->sa_family == AF_INET6) list = confirm >= 2 ? &v6_confirmed : &v6_new; else abort(); @@ -306,12 +496,12 @@ new_node(unsigned char *id, struct sockaddr *sa, socklen_t salen, int confirm) We want to avoid getting our tables full of very young nodes -- only include such a node if we have plenty of space. */ - if(confirm == 1 && list_free(list) < 32) + if (confirm == 1 && list_free(list) < 32) return 0; - for(i = list->head; i != list->tail; i = (i + 1) % CIRCULAR_LIST_SIZE) { + for (int i = list->head; i != list->tail; i = (i + 1) % CIRCULAR_LIST_SIZE) { struct node *n = &list->nodes[i]; - if(n->sslen == salen && memcmp(&n->ss, sa, salen) == 0) + if (n->sslen == salen && memcmp(&n->ss, sa, salen) == 0) return 0; } @@ -319,8 +509,10 @@ new_node(unsigned char *id, struct sockaddr *sa, socklen_t salen, int confirm) memcpy(&list->nodes[list->tail].ss, sa, salen); list->nodes[list->tail].sslen = salen; list->tail = (list->tail + 1) % CIRCULAR_LIST_SIZE; - if(list->head == list->tail) + if (list->head == list->tail) list->head = (list->head + 1) % CIRCULAR_LIST_SIZE; + if (verbose) + printf("%s : new node\n", getAddr(sa, salen)); return 1; } @@ -329,223 +521,396 @@ static int token_bucket(void) { time_t now = time(NULL); - if(token_bucket_tokens == 0) { - token_bucket_tokens = MIN(MAX_TOKEN_BUCKET_TOKENS, - 4 * (now - token_bucket_time)); + if (token_bucket_tokens == 0) { + token_bucket_tokens = + MIN(MAX_TOKEN_BUCKET_TOKENS, 4 * (now - token_bucket_time)); token_bucket_time = now; } - - if(token_bucket_tokens == 0) + if (token_bucket_tokens == 0) return 0; - token_bucket_tokens--; return 1; } +static int +dht_send(const void *buf, size_t len, int flags, const struct sockaddr *sa, int salen) +{ + if (salen == 0) + abort(); + int s; + if (sa->sa_family == AF_INET) + s = dht_socket; + else if (sa->sa_family == AF_INET6) + s = dht_socket6; + else + s = -1; + if (s < 0) { + errno = EAFNOSUPPORT; + return -1; + } + return sendto(s, buf, len, flags, sa, salen); +} + +static int +send_ping(struct sockaddr *sa, int salen, const unsigned char *tid, int tid_len) +{ + char buf[512]; + int i = 0, rc; + rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); + INC(i, rc, 512); + COPY(buf, i, myid, 20, 512); + rc = snprintf(buf + i, 512 - i, "e1:q4:ping1:t%d:", tid_len); + INC(i, rc, 512); + COPY(buf, i, tid, tid_len, 512); + ADD_V(buf, i, 512); + rc = snprintf(buf + i, 512 - i, "1:y1:qe"); + INC(i, rc, 512); + if (verbose) + printf("%s <- PING\n", getAddr(sa, salen)); + return dht_send(buf, i, 0, sa, salen); + +fail: + errno = ENOSPC; + return -1; +} + +static int +send_pong(struct sockaddr *sa, int salen, const unsigned char *tid, int tid_len) +{ + char buf[512]; + int i = 0; + int rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); + INC(i, rc, 512); + COPY(buf, i, myid, 20, 512); + rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid_len); + INC(i, rc, 512); + COPY(buf, i, tid, tid_len, 512); + ADD_V(buf, i, 512); + rc = snprintf(buf + i, 512 - i, "1:y1:re"); + INC(i, rc, 512); + if (verbose) + printf("%s <- PONG\n", getAddr(sa, salen)); + return dht_send(buf, i, 0, sa, salen); + +fail: + errno = ENOSPC; + return -1; +} + +static int +send_find_node( + struct sockaddr *sa, + int salen, + const unsigned char *tid, + int tid_len, + const unsigned char *target, + int want) +{ + char buf[512]; + int i = 0; + int rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); + INC(i, rc, 512); + COPY(buf, i, myid, 20, 512); + rc = snprintf(buf + i, 512 - i, "6:target20:"); + INC(i, rc, 512); + COPY(buf, i, target, 20, 512); + if (want > 0) { + rc = snprintf( + buf + i, + 512 - i, + "4:wantl%s%se", + (want & WANT4) ? "2:n4" : "", + (want & WANT6) ? "2:n6" : ""); + INC(i, rc, 512); + } + rc = snprintf(buf + i, 512 - i, "e1:q9:find_node1:t%d:", tid_len); + INC(i, rc, 512); + COPY(buf, i, tid, tid_len, 512); + ADD_V(buf, i, 512); + rc = snprintf(buf + i, 512 - i, "1:y1:qe"); + INC(i, rc, 512); + if (verbose) + printf("%s <- FIND_NODE\n", getAddr(sa, salen)); + return dht_send(buf, i, 0, sa, salen); + +fail: + errno = ENOSPC; + return -1; +} + static int send_request(struct circular_list *list, int dopop, int doping, int want) { - unsigned char ttid[4]; + if (list_empty(list)) + return 0; + struct sockaddr_storage ss; socklen_t sslen; int rc; - - if(list_empty(list)) - return 0; - - if(dopop) { + if (dopop) { rc = list_pop(list, &ss, &sslen); - if(rc == 0) + if (rc == 0) return 0; } else { rc = list_random(list, NULL, &ss, &sslen); - if(rc == 0) + if (rc == 0) return 0; } - if(doping) { + unsigned char ttid[4]; + if (doping) { make_tid(ttid, "pn", 0); - debugf("Sending ping.\n"); - return send_ping((struct sockaddr*)&ss, sslen, ttid, 4); + return send_ping((struct sockaddr *)&ss, sslen, ttid, 4); } else { unsigned char id[20]; - int i; - for(i = 0; i < 20; i++) - id[i] = random() & 0xFF; + arc4random_buf(id, sizeof id); make_tid(ttid, "fn", 0); - debugf("Sending find_node.\n"); - return send_find_node((struct sockaddr*)&ss, sslen, ttid, 4, - id, want, 0); + return send_find_node((struct sockaddr *)&ss, sslen, ttid, 4, id, want); } } -int -main(int argc, char **argv) +static int +newSock(const char *host, const char *port) { - int port = 6881, quiet = 0, ipv4 = 1, ipv6 = 1; - int opt, rc, i, send4; - unsigned char ttid[4]; - - while(1) { - opt = getopt(argc, argv, "q46"); - if(opt < 0) - break; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + struct addrinfo *res = NULL; + int rc = getaddrinfo(host, port, &hints, &res); + if (rc != 0) + err(EXIT_FAILURE, "getaddrinfo: %s\n", gai_strerror(rc)); + int sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock == -1) + err(EXIT_FAILURE, "socket()"); + if (bind(sock, res->ai_addr, res->ai_addrlen) != 0) + err(EXIT_FAILURE, "bind()"); + rc = fcntl(sock, F_GETFL, 0); + if (rc < 0) + err(EXIT_FAILURE, "F_GETFL"); + rc = fcntl(sock, F_SETFL, (rc | O_NONBLOCK)); + if (rc < 0) + err(EXIT_FAILURE, "F_SETFL"); + freeaddrinfo(res); + return sock; +} - switch(opt) { - case 'q': - quiet = 1; - break; - case '4': - ipv6 = 0; - break; - case '6': - ipv4 = 0; - break; - default: - goto usage; - } +static void +rlimited(int res) +{ + struct rlimit r; + r.rlim_cur = 0; + r.rlim_max = 0; + if (setrlimit(res, &r) == -1) { + err(EXIT_FAILURE, "can not setrlimit()"); } +} - i = optind; - - if(argc < i + 1) - goto usage; - - port = atoi(argv[i++]); - if(port <= 0 || port >= 0x10000) - goto usage; - - if(ipv4) { - dht_socket = socket(PF_INET, SOCK_DGRAM, 0); - if(dht_socket < 0) - perror("socket(IPv4)"); +static int +send_nodes( + struct sockaddr *sa, + int salen, + const unsigned char *tid, + int tid_len, + const unsigned char *nodes, + int nodes_len, + const unsigned char *nodes6, + int nodes6_len) +{ + char buf[2048]; + int i = 0; + int rc = snprintf(buf + i, 2048 - i, "d1:rd2:id20:"); + INC(i, rc, 2048); + COPY(buf, i, myid, 20, 2048); + if (nodes_len > 0) { + rc = snprintf(buf + i, 2048 - i, "5:nodes%d:", nodes_len); + INC(i, rc, 2048); + COPY(buf, i, nodes, nodes_len, 2048); } - - if(ipv6) { - dht_socket6 = socket(PF_INET6, SOCK_DGRAM, 0); - if(dht_socket6 < 0) - perror("socket(IPv6)"); + if (nodes6_len > 0) { + rc = snprintf(buf + i, 2048 - i, "6:nodes6%d:", nodes6_len); + INC(i, rc, 2048); + COPY(buf, i, nodes6, nodes6_len, 2048); } + rc = snprintf(buf + i, 2048 - i, "e1:t%d:", tid_len); + INC(i, rc, 2048); + COPY(buf, i, tid, tid_len, 2048); + ADD_V(buf, i, 2048); + rc = snprintf(buf + i, 2048 - i, "1:y1:re"); + INC(i, rc, 2048); + return dht_send(buf, i, 0, sa, salen); - if(dht_socket < 0 && dht_socket6 < 0) { - fprintf(stderr, "Eek!\n"); - exit(1); +fail: + errno = ENOSPC; + return -1; +} + +static int +buffer_random_nodes(int af, unsigned char *nodes) +{ + struct circular_list *list; + switch (af) { + case AF_INET: + list = &v4_confirmed; + break; + case AF_INET6: + list = &v6_confirmed; + break; + default: + abort(); } - if(dht_socket >= 0) { - struct sockaddr_in sin; - int rc; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - rc = bind(dht_socket, (struct sockaddr*)&sin, sizeof(sin)); - if(rc < 0) { - perror("bind(IPv4)"); - exit(1); + struct sockaddr_storage ss; + socklen_t sslen; + unsigned char id[20]; + int rc; + int n = 0; + while (n < 8) { + rc = list_random(list, id, &ss, &sslen); + if (rc < 1) + break; + switch (af) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)&ss; + memcpy(nodes + n * 26, id, 20); + memcpy(nodes + n * 26 + 20, &sin->sin_addr, 4); + memcpy(nodes + n * 26 + 24, &sin->sin_port, 2); + n++; + break; } - - rc = fcntl(dht_socket, F_GETFL, 0); - if(rc < 0) { - perror("F_GETFL"); - exit(1); + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; + memcpy(nodes + n * 38, id, 20); + memcpy(nodes + n * 38 + 20, &sin6->sin6_addr, 16); + memcpy(nodes + n * 38 + 36, &sin6->sin6_port, 2); + n++; + break; } - - rc = fcntl(dht_socket, F_SETFL, (rc | O_NONBLOCK)); - if(rc < 0) { - perror("F_SETFL"); - exit(1); + default: + abort(); } } + return n; +} - if(dht_socket6 >= 0) { - struct sockaddr_in6 sin6; - int rc; - int val = 1; - - rc = setsockopt(dht_socket6, IPPROTO_IPV6, IPV6_V6ONLY, - (char *)&val, sizeof(val)); - if(rc < 0) { - perror("setsockopt(IPV6_V6ONLY)"); - exit(1); - } +static int +send_random_nodes( + struct sockaddr *sa, + int salen, + const unsigned char *tid, + int tid_len, + int want) +{ + unsigned char nodes[8 * 26]; + unsigned char nodes6[8 * 38]; + int numnodes = 0; + int numnodes6 = 0; + if (want < 0) + want = sa->sa_family == AF_INET ? WANT4 : WANT6; + if ((want & WANT4)) + numnodes = buffer_random_nodes(AF_INET, nodes); + if ((want & WANT6)) + numnodes6 = buffer_random_nodes(AF_INET6, nodes6); + if (verbose) + printf("%s <- NODES (%d+%d)\n", getAddr(sa, salen), numnodes, numnodes6); + return send_nodes( + sa, salen, tid, tid_len, nodes, numnodes * 26, nodes6, numnodes6 * 38); +} - /* BEP-32 mandates that we should bind this socket to one of our - global IPv6 addresses. In this program, this only happens if - the user used the -b flag. */ - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(port); - rc = bind(dht_socket6, (struct sockaddr*)&sin6, sizeof(sin6)); - if(rc < 0) { - perror("bind(IPv6)"); - exit(1); - } +static int +send_error( + struct sockaddr *sa, + int salen, + unsigned char *tid, + int tid_len, + int code, + const char *message) +{ + char buf[512]; + int i = 0; + int rc = snprintf(buf + i, 512 - i, "d1:eli%de%d:", code, (int)strlen(message)); + INC(i, rc, 512); + COPY(buf, i, message, (int)strlen(message), 512); + rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid_len); + INC(i, rc, 512); + COPY(buf, i, tid, tid_len, 512); + ADD_V(buf, i, 512); + rc = snprintf(buf + i, 512 - i, "1:y1:ee"); + INC(i, rc, 512); + return dht_send(buf, i, 0, sa, salen); - rc = fcntl(dht_socket6, F_GETFL, 0); - if(rc < 0) { - perror("F_GETFL"); - exit(1); - } - - rc = fcntl(dht_socket6, F_SETFL, (rc | O_NONBLOCK)); - if(rc < 0) { - perror("F_SETFL"); - exit(1); - } - } +fail: + errno = ENOSPC; + return -1; +} - { - int fd; - unsigned int seed; +int +main(int argc, char **argv) +{ + errno = 0; + char *ipv4addr = NULL; + char *ipv6addr = NULL; + int opt = 0; + + while (1) { + opt = getopt(argc, argv, "q4:6:"); + if (opt < 0) + break; - fd = open("/dev/urandom", O_RDONLY); - if(fd < 0) { - perror("open(random)"); - exit(1); + switch (opt) { + case 'q': + verbose = 0; + break; + case '4': + ipv4addr = strdup(optarg); + break; + case '6': + ipv6addr = strdup(optarg); + break; + default: + goto usage; } + } - rc = read(fd, myid, 20); - if(rc < 20) { - perror("open(random)"); - exit(1); - } + int i = optind; - rc = read(fd, &seed, sizeof(seed)); - srandom(seed); + if (argc < i + 1) + goto usage; - close(fd); + const char *ourPort = strdup(argv[i++]); + if (ipv4addr != NULL) { + dht_socket = newSock(ipv4addr, ourPort); + } + if (ipv6addr != NULL) { + dht_socket6 = newSock(ipv6addr, ourPort); } + arc4random_buf(myid, sizeof myid); memcpy(my_v, "1:v4:JB\0\0", 9); - have_v = 1; - if(!quiet) - dht_debug = stdout; - - while(i < argc) { + int rc = 0; + unsigned char ttid[4]; + + while (i < argc) { struct addrinfo hints, *info, *infop; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; hints.ai_family = 0; - if(dht_socket < 0) + if (dht_socket < 0) hints.ai_family = AF_INET6; - else if(dht_socket6 < 0) + else if (dht_socket6 < 0) hints.ai_family |= AF_INET; rc = getaddrinfo(argv[i], argv[i + 1], &hints, &info); - if(rc != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc)); - exit(1); - } + if (rc != 0) + err(EXIT_FAILURE, "getaddrinfo: %s\n", gai_strerror(rc)); i++; - if(i >= argc) + if (i >= argc) goto usage; infop = info; - while(infop) { + while (infop) { make_tid(ttid, "pn", 0); - debugf("Sending ping.\n"); send_ping(infop->ai_addr, infop->ai_addrlen, ttid, 4); infop = infop->ai_next; } @@ -556,42 +921,48 @@ main(int argc, char **argv) token_bucket_time = time(NULL); token_bucket_tokens = MAX_TOKEN_BUCKET_TOKENS; - - while(1) { - struct timeval tv; - fd_set readfds; - int rc; - - if((dht_socket >= 0 && list_elements(&v4_confirmed) <= 16) || - (dht_socket6 >= 0 && list_elements(&v6_confirmed) <= 16)) - tv.tv_sec = 0; + int send4 = 0; + struct pollfd fds[2]; + fds[0].fd = dht_socket; + fds[0].events = POLLIN; + fds[1].fd = dht_socket6; + fds[1].events = POLLIN; + + close(STDIN_FILENO); + if (!verbose) + close(STDOUT_FILENO); + rlimited(RLIMIT_NPROC); + rlimited(RLIMIT_FSIZE); +#if __FreeBSD__ + rlimited(RLIMIT_NOFILE); +#endif // __FreeBSD__ + + while (1) { + int tv_sec = 0; + if ((dht_socket >= 0 && list_elements(&v4_confirmed) <= 16) || + (dht_socket6 >= 0 && list_elements(&v6_confirmed) <= 16)) + tv_sec = 0; else - tv.tv_sec = random() % 30; - tv.tv_usec = random() % 1000000; - - FD_ZERO(&readfds); - if(dht_socket >= 0) - FD_SET(dht_socket, &readfds); - if(dht_socket6 >= 0) - FD_SET(dht_socket6, &readfds); - - if(dht_debug) - debugf("%d+%d %d+%d\n", - list_elements(&v4_confirmed), list_elements(&v6_confirmed), - list_elements(&v4_new), list_elements(&v6_new)); - - rc = select(MAX(dht_socket, dht_socket6) + 1, &readfds, NULL, NULL, - &tv); - - if(rc < 0) { - if(errno != EINTR) { - perror("select"); - sleep(1); - } + tv_sec = random() % 30; + int tv_msec = random() % 1000; + +#ifndef __linux__ + setproctitle_fast( + "%d+%d %d+%d", + list_elements(&v4_confirmed), + list_elements(&v6_confirmed), + list_elements(&v4_new), + list_elements(&v6_new)); +#endif // __linux__ + + rc = poll(fds, 2, tv_sec * 1000 + tv_msec); + if (rc < 0) { + perror("poll"); + sleep(1); } - if(rc > 0) { - int rc, message; + if (rc > 0) { + int message; unsigned char tid[16], id[20], info_hash[20], target[20]; unsigned char buf[1536], nodes[256], nodes6[1024], token[128]; int tid_len = 16, token_len = 128; @@ -599,127 +970,170 @@ main(int argc, char **argv) unsigned short port; unsigned char values[2048], values6[2048]; int values_len = 2048, values6_len = 2048; - int want, want4, want6; + int want; struct sockaddr_storage source_storage; - struct sockaddr *source = (struct sockaddr*)&source_storage; + struct sockaddr *source = (struct sockaddr *)&source_storage; socklen_t sourcelen = sizeof(source_storage); - if(dht_socket >= 0 && FD_ISSET(dht_socket, &readfds)) { - rc = recvfrom(dht_socket, buf, 1536, 0, source, &sourcelen); - } else if(dht_socket6 >= 0 && FD_ISSET(dht_socket6, &readfds)) { - rc = recvfrom(dht_socket6, buf, 1536, 0, source, &sourcelen); + if (fds[0].revents != 0) { + if ((fds[0].revents & (POLLERR | POLLNVAL)) > 0) { + fprintf(stderr, "error in fds[0]"); + rc = -1; + } else { + rc = recvfrom(dht_socket, buf, 1536, 0, source, &sourcelen); + } + } else if (fds[1].revents != 0) { + if ((fds[1].revents & (POLLERR | POLLNVAL)) > 0) { + fprintf(stderr, "error in fds[1]"); + rc = -1; + } else { + rc = recvfrom(dht_socket6, buf, 1536, 0, source, &sourcelen); + } } - if(rc < 0 || sourcelen > sizeof(struct sockaddr_storage)) + if (rc < 0 || sourcelen > sizeof(struct sockaddr_storage)) goto dontread; - if(is_martian(source)) + if (is_martian(source)) goto dontread; /* There's a bug in parse_message -- it will happily overflow the buffer if it's not NUL-terminated. For now, put a NUL at the end of buffers. */ - if(rc < 1536) { + if (rc < 1536) { buf[rc] = '\0'; } else { - debugf("Overlong message.\n"); + if (verbose) + printf("%s : overlong message\n", getAddr(source, sourcelen)); goto dontread; } - message = parse_message(buf, rc, tid, &tid_len, id, info_hash, - target, &port, token, &token_len, - nodes, &nodes_len, nodes6, &nodes6_len, - values, &values_len, values6, &values6_len, - &want); - - if(id_cmp(id, myid) == 0) { - debugf("Received message from self.\n"); + message = parse_message( + buf, + rc, + tid, + &tid_len, + id, + info_hash, + target, + &port, + token, + &token_len, + nodes, + &nodes_len, + nodes6, + &nodes6_len, + values, + &values_len, + values6, + &values6_len, + &want, + source, + sourcelen); + + if (id_cmp(id, myid) == 0) { + if (verbose) + printf( + "%s : received message from self\n", + getAddr(source, sourcelen)); goto dontread; } - if(message > REPLY) { + if (message > REPLY) { /* Rate limit requests. */ - if(!token_bucket()) { - debugf("Dropping request due to rate limiting.\n"); + if (!token_bucket()) { + if (verbose) + printf( + "%s : dropping request due to rate limiting.\n", + getAddr(source, sourcelen)); goto dontread; } } - if(want > 0) { - want4 = (want & WANT4); - want6 = (want & WANT6); - } else { - want4 = source->sa_family == AF_INET; - want6 = source->sa_family == AF_INET6; - } - - switch(message) { + switch (message) { case REPLY: - if(tid_len != 4) { - debugf("Broken node truncates transaction ids.\n"); + if (tid_len != 4) { + if (verbose) + printf( + "%s : broken node truncates transaction ids\n", + getAddr(source, sourcelen)); goto dontread; } - if(tid_match(tid, "pn", NULL)) { - debugf("Pong!\n"); + if (tid_match(tid, "pn", NULL)) { + if (verbose) + printf("%s -> PONG\n", getAddr(source, sourcelen)); new_node(id, source, sourcelen, 2); - } else if(tid_match(tid, "fn", NULL)) { - debugf("Nodes found!\n"); - if(nodes_len % 26 != 0 || nodes6_len % 38 != 0) { - debugf("Unexpected length for node info!\n"); + } else if (tid_match(tid, "fn", NULL)) { + if (verbose) + printf( + "%s -> NODES (%d+%d)\n", + getAddr(source, sourcelen), + nodes_len / 26, + nodes6_len / 38); + if (nodes_len % 26 != 0 || nodes6_len % 38 != 0) { + if (verbose) + printf( + "%s : unexpected length for node info\n", + getAddr(source, sourcelen)); } else { new_node(id, source, sourcelen, 2); - for(i = 0; i < nodes_len / 26; i++) { + for (i = 0; i < nodes_len / 26; i++) { unsigned char *ni = nodes + i * 26; struct sockaddr_in sin; - if(id_cmp(ni, myid) == 0) + if (id_cmp(ni, myid) == 0) continue; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, ni + 20, 4); memcpy(&sin.sin_port, ni + 24, 2); - new_node(ni, (struct sockaddr*)&sin, sizeof(sin), - 0); + new_node(ni, (struct sockaddr *)&sin, sizeof(sin), 0); } - for(i = 0; i < nodes6_len / 38; i++) { + for (i = 0; i < nodes6_len / 38; i++) { unsigned char *ni = nodes6 + i * 38; struct sockaddr_in6 sin6; - if(id_cmp(ni, myid) == 0) + if (id_cmp(ni, myid) == 0) continue; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; memcpy(&sin6.sin6_addr, ni + 20, 16); memcpy(&sin6.sin6_port, ni + 36, 2); - new_node(ni, (struct sockaddr*)&sin6, sizeof(sin6), - 0); + new_node(ni, (struct sockaddr *)&sin6, sizeof(sin6), 0); } } } else { - debugf("Unexpected reply!\n"); + if (verbose) + printf("%s : unexpected reply\n", getAddr(source, sourcelen)); goto dontread; } break; case PING: - debugf("Ping (%d)!\n", tid_len); + if (verbose) + printf("%s -> PING (%d)\n", getAddr(source, sourcelen), tid_len); new_node(id, source, sourcelen, 1); - debugf("Sending pong.\n"); send_pong(source, sourcelen, tid, tid_len); break; case FIND_NODE: case GET_PEERS: - if(message == FIND_NODE) - debugf("Find node!\n"); - else - debugf("Get peers!\n"); + if (verbose) { + if (message == FIND_NODE) + printf("%s -> FIND_NODE\n", getAddr(source, sourcelen)); + else + printf("%s -> GET_PEERS\n", getAddr(source, sourcelen)); + } new_node(id, source, sourcelen, 1); - debugf("Sending nodes (%d).\n", want); - send_random_nodes(source, sourcelen, - tid, tid_len, target, want); + send_random_nodes(source, sourcelen, tid, tid_len, want); break; case ANNOUNCE_PEER: - debugf("Announce peer!\n"); - send_error(source, sourcelen, tid, tid_len, - 203, "This node doesn't accept announces"); + if (verbose) + printf("%s -> ANNOUNCE_PEER\n", getAddr(source, sourcelen)); + send_error( + source, + sourcelen, + tid, + tid_len, + 203, + "This node doesn't accept announces"); break; } } @@ -730,512 +1144,29 @@ main(int argc, char **argv) sure we send at most one packet each time through the select loop. */ - if(dht_socket6 < 0) + if (dht_socket6 < 0) send4 = 1; - else if(dht_socket < 0) + else if (dht_socket < 0) send4 = 0; else send4 = random() % 2; - if(send4) { - int want = - dht_socket6 >= 0 && list_free(&v6_new) > 8 ? - (WANT4 | WANT6) : 0; - if(!list_empty(&v4_new)) + if (send4) { + int want = dht_socket6 >= 0 && list_free(&v6_new) > 8 ? (WANT4 | WANT6) : 0; + if (!list_empty(&v4_new)) send_request(&v4_new, 1, list_free(&v4_new) < 8, want); - else if(!list_empty(&v4_confirmed)) + else if (!list_empty(&v4_confirmed)) send_request(&v4_confirmed, 0, 0, want); } else { - int want = - dht_socket >= 0 && list_free(&v4_new) > 8 ? - (WANT4 | WANT6) : 0; - if(!list_empty(&v6_new)) + int want = dht_socket >= 0 && list_free(&v4_new) > 8 ? (WANT4 | WANT6) : 0; + if (!list_empty(&v6_new)) send_request(&v6_new, 1, list_free(&v6_new) < 8, want); - else if(!list_empty(&v6_confirmed)) + else if (!list_empty(&v6_confirmed)) send_request(&v6_confirmed, 0, 0, want); } } - return 0; - - usage: - fprintf(stderr, "dht-bootstrap [-q] [-4] [-6] port [node port...]\n"); - exit(1); -} - -/* We could use a proper bencoding printer and parser, but the format of - DHT messages is fairly stylised, so this seemed simpler. */ - -#define CHECK(offset, delta, size) \ - if(delta < 0 || offset + delta > size) goto fail - -#define INC(offset, delta, size) \ - CHECK(offset, delta, size); \ - offset += delta - -#define COPY(buf, offset, src, delta, size) \ - CHECK(offset, delta, size); \ - memcpy(buf + offset, src, delta); \ - offset += delta; - -#define ADD_V(buf, offset, size) \ - if(have_v) { \ - COPY(buf, offset, my_v, sizeof(my_v), size); \ - } - -static int -dht_send(const void *buf, size_t len, int flags, - const struct sockaddr *sa, int salen) -{ - int s; - - if(salen == 0) - abort(); - - if(sa->sa_family == AF_INET) - s = dht_socket; - else if(sa->sa_family == AF_INET6) - s = dht_socket6; - else - s = -1; - - if(s < 0) { - errno = EAFNOSUPPORT; - return -1; - } - - return sendto(s, buf, len, flags, sa, salen); -} - -int -send_ping(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len) -{ - char buf[512]; - int i = 0, rc; - rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); INC(i, rc, 512); - COPY(buf, i, myid, 20, 512); - rc = snprintf(buf + i, 512 - i, "e1:q4:ping1:t%d:", tid_len); - INC(i, rc, 512); - COPY(buf, i, tid, tid_len, 512); - ADD_V(buf, i, 512); - rc = snprintf(buf + i, 512 - i, "1:y1:qe"); INC(i, rc, 512); - return dht_send(buf, i, 0, sa, salen); - - fail: - errno = ENOSPC; - return -1; -} - -int -send_pong(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len) -{ - char buf[512]; - int i = 0, rc; - rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512); - COPY(buf, i, myid, 20, 512); - rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid_len); INC(i, rc, 512); - COPY(buf, i, tid, tid_len, 512); - ADD_V(buf, i, 512); - rc = snprintf(buf + i, 512 - i, "1:y1:re"); INC(i, rc, 512); - return dht_send(buf, i, 0, sa, salen); - - fail: - errno = ENOSPC; - return -1; -} - -int -send_find_node(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len, - const unsigned char *target, int want, int confirm) -{ - char buf[512]; - int i = 0, rc; - rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); INC(i, rc, 512); - COPY(buf, i, myid, 20, 512); - rc = snprintf(buf + i, 512 - i, "6:target20:"); INC(i, rc, 512); - COPY(buf, i, target, 20, 512); - if(want > 0) { - rc = snprintf(buf + i, 512 - i, "4:wantl%s%se", - (want & WANT4) ? "2:n4" : "", - (want & WANT6) ? "2:n6" : ""); - INC(i, rc, 512); - } - rc = snprintf(buf + i, 512 - i, "e1:q9:find_node1:t%d:", tid_len); - INC(i, rc, 512); - COPY(buf, i, tid, tid_len, 512); - ADD_V(buf, i, 512); - rc = snprintf(buf + i, 512 - i, "1:y1:qe"); INC(i, rc, 512); - return dht_send(buf, i, confirm ? MSG_CONFIRM : 0, sa, salen); - - fail: - errno = ENOSPC; - return -1; -} - -int -send_nodes(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len, - const unsigned char *nodes, int nodes_len, - const unsigned char *nodes6, int nodes6_len) -{ - char buf[2048]; - int i = 0, rc; - - rc = snprintf(buf + i, 2048 - i, "d1:rd2:id20:"); INC(i, rc, 2048); - COPY(buf, i, myid, 20, 2048); - if(nodes_len > 0) { - rc = snprintf(buf + i, 2048 - i, "5:nodes%d:", nodes_len); - INC(i, rc, 2048); - COPY(buf, i, nodes, nodes_len, 2048); - } - if(nodes6_len > 0) { - rc = snprintf(buf + i, 2048 - i, "6:nodes6%d:", nodes6_len); - INC(i, rc, 2048); - COPY(buf, i, nodes6, nodes6_len, 2048); - } - - rc = snprintf(buf + i, 2048 - i, "e1:t%d:", tid_len); INC(i, rc, 2048); - COPY(buf, i, tid, tid_len, 2048); - ADD_V(buf, i, 2048); - rc = snprintf(buf + i, 2048 - i, "1:y1:re"); INC(i, rc, 2048); - - return dht_send(buf, i, 0, sa, salen); - - fail: - errno = ENOSPC; - return -1; -} - -static int -buffer_random_nodes(int af, unsigned char *nodes) -{ - struct circular_list *list; - struct sockaddr_storage ss; - socklen_t sslen; - unsigned char id[20]; - int n; - int rc; - - switch(af) { - case AF_INET: list = &v4_confirmed; break; - case AF_INET6: list = &v6_confirmed; break; - default: abort(); - } - - n = 0; - while(n < 8) { - rc = list_random(list, id, &ss, &sslen); - if(rc < 1) - break; - switch(af) { - case AF_INET: { - struct sockaddr_in *sin = (struct sockaddr_in*)&ss; - memcpy(nodes + n * 26, id, 20); - memcpy(nodes + n * 26 + 20, &sin->sin_addr, 4); - memcpy(nodes + n * 26 + 24, &sin->sin_port, 2); - n++; - break; - } - case AF_INET6: { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&ss; - memcpy(nodes + n * 38, id, 20); - memcpy(nodes + n * 38 + 20, &sin6->sin6_addr, 16); - memcpy(nodes + n * 38 + 36, &sin6->sin6_port, 2); - n++; - break; - } - default: - abort(); - } - } - return n; -} - -int -send_random_nodes(struct sockaddr *sa, int salen, - const unsigned char *tid, int tid_len, - const unsigned char *id, int want) -{ - unsigned char nodes[8 * 26]; - unsigned char nodes6[8 * 38]; - int numnodes = 0, numnodes6 = 0; - - if(want < 0) - want = sa->sa_family == AF_INET ? WANT4 : WANT6; - - if((want & WANT4)) - numnodes = buffer_random_nodes(AF_INET, nodes); - - if((want & WANT6)) - numnodes6 = buffer_random_nodes(AF_INET6, nodes6); - - return send_nodes(sa, salen, tid, tid_len, - nodes, numnodes * 26, - nodes6, numnodes6 * 38); -} - -static int -send_error(struct sockaddr *sa, int salen, - unsigned char *tid, int tid_len, - int code, const char *message) -{ - char buf[512]; - int i = 0, rc; - - rc = snprintf(buf + i, 512 - i, "d1:eli%de%d:", - code, (int)strlen(message)); - INC(i, rc, 512); - COPY(buf, i, message, (int)strlen(message), 512); - rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid_len); INC(i, rc, 512); - COPY(buf, i, tid, tid_len, 512); - ADD_V(buf, i, 512); - rc = snprintf(buf + i, 512 - i, "1:y1:ee"); INC(i, rc, 512); - return dht_send(buf, i, 0, sa, salen); - - fail: - errno = ENOSPC; - return -1; -} - -#undef CHECK -#undef INC -#undef COPY -#undef ADD_V - -#ifndef HAVE_MEMMEM -static void * -memmem(const void *haystack, size_t haystacklen, - const void *needle, size_t needlelen) -{ - const char *h = haystack; - const char *n = needle; - size_t i; - - /* size_t is unsigned */ - if(needlelen > haystacklen) - return NULL; - - for(i = 0; i <= haystacklen - needlelen; i++) { - if(memcmp(h + i, n, needlelen) == 0) - return (void*)(h + i); - } - return NULL; -} -#endif - -static int -parse_message(const unsigned char *buf, int buflen, - unsigned char *tid_return, int *tid_len, - unsigned char *id_return, unsigned char *info_hash_return, - unsigned char *target_return, unsigned short *port_return, - unsigned char *token_return, int *token_len, - unsigned char *nodes_return, int *nodes_len, - unsigned char *nodes6_return, int *nodes6_len, - unsigned char *values_return, int *values_len, - unsigned char *values6_return, int *values6_len, - int *want_return) -{ - const unsigned char *p; - - /* This code will happily crash if the buffer is not NUL-terminated. */ - if(buf[buflen] != '\0') { - debugf("Eek! parse_message with unterminated buffer.\n"); - return -1; - } - -#define CHECK(ptr, len) \ - if(((unsigned char*)ptr) + (len) > (buf) + (buflen)) goto overflow; - - if(tid_return) { - p = memmem(buf, buflen, "1:t", 3); - if(p) { - long l; - char *q; - l = strtol((char*)p + 3, &q, 10); - if(q && *q == ':' && l > 0 && l < *tid_len) { - CHECK(q + 1, l); - memcpy(tid_return, q + 1, l); - *tid_len = l; - } else - *tid_len = 0; - } - } - if(id_return) { - p = memmem(buf, buflen, "2:id20:", 7); - if(p) { - CHECK(p + 7, 20); - memcpy(id_return, p + 7, 20); - } else { - memset(id_return, 0, 20); - } - } - if(info_hash_return) { - p = memmem(buf, buflen, "9:info_hash20:", 14); - if(p) { - CHECK(p + 14, 20); - memcpy(info_hash_return, p + 14, 20); - } else { - memset(info_hash_return, 0, 20); - } - } - if(port_return) { - p = memmem(buf, buflen, "porti", 5); - if(p) { - long l; - char *q; - l = strtol((char*)p + 5, &q, 10); - if(q && *q == 'e' && l > 0 && l < 0x10000) - *port_return = l; - else - *port_return = 0; - } else - *port_return = 0; - } - if(target_return) { - p = memmem(buf, buflen, "6:target20:", 11); - if(p) { - CHECK(p + 11, 20); - memcpy(target_return, p + 11, 20); - } else { - memset(target_return, 0, 20); - } - } - if(token_return) { - p = memmem(buf, buflen, "5:token", 7); - if(p) { - long l; - char *q; - l = strtol((char*)p + 7, &q, 10); - if(q && *q == ':' && l > 0 && l < *token_len) { - CHECK(q + 1, l); - memcpy(token_return, q + 1, l); - *token_len = l; - } else - *token_len = 0; - } else - *token_len = 0; - } - - if(nodes_len) { - p = memmem(buf, buflen, "5:nodes", 7); - if(p) { - long l; - char *q; - l = strtol((char*)p + 7, &q, 10); - if(q && *q == ':' && l > 0 && l < *nodes_len) { - CHECK(q + 1, l); - memcpy(nodes_return, q + 1, l); - *nodes_len = l; - } else - *nodes_len = 0; - } else - *nodes_len = 0; - } - - if(nodes6_len) { - p = memmem(buf, buflen, "6:nodes6", 8); - if(p) { - long l; - char *q; - l = strtol((char*)p + 8, &q, 10); - if(q && *q == ':' && l > 0 && l < *nodes6_len) { - CHECK(q + 1, l); - memcpy(nodes6_return, q + 1, l); - *nodes6_len = l; - } else - *nodes6_len = 0; - } else - *nodes6_len = 0; - } - - if(values_len || values6_len) { - p = memmem(buf, buflen, "6:valuesl", 9); - if(p) { - int i = p - buf + 9; - int j = 0, j6 = 0; - while(1) { - long l; - char *q; - l = strtol((char*)buf + i, &q, 10); - if(q && *q == ':' && l > 0) { - CHECK(q + 1, l); - if(l == 6) { - if(j + l > *values_len) - continue; - i = q + 1 + l - (char*)buf; - memcpy((char*)values_return + j, q + 1, l); - j += l; - } else if(l == 18) { - if(j6 + l > *values6_len) - continue; - i = q + 1 + l - (char*)buf; - memcpy((char*)values6_return + j6, q + 1, l); - j6 += l; - } else { - debugf("Received weird value -- %d bytes.\n", (int)l); - i = q + 1 + l - (char*)buf; - } - } else { - break; - } - } - if(i >= buflen || buf[i] != 'e') - debugf("eek... unexpected end for values.\n"); - if(values_len) - *values_len = j; - if(values6_len) - *values6_len = j6; - } else { - *values_len = 0; - *values6_len = 0; - } - } - - if(want_return) { - p = memmem(buf, buflen, "4:wantl", 7); - if(p) { - int i = p - buf + 7; - *want_return = 0; - while(buf[i] > '0' && buf[i] <= '9' && buf[i + 1] == ':' && - i + 2 + buf[i] - '0' < buflen) { - CHECK(buf + i + 2, buf[i] - '0'); - if(buf[i] == '2' && memcmp(buf + i + 2, "n4", 2) == 0) - *want_return |= WANT4; - else if(buf[i] == '2' && memcmp(buf + i + 2, "n6", 2) == 0) - *want_return |= WANT6; - else - debugf("eek... unexpected want flag (%c)\n", buf[i]); - i += 2 + buf[i] - '0'; - } - if(i >= buflen || buf[i] != 'e') - debugf("eek... unexpected end for want.\n"); - } else { - *want_return = -1; - } - } - -#undef CHECK - - if(memmem(buf, buflen, "1:y1:r", 6)) - return REPLY; - if(memmem(buf, buflen, "1:y1:e", 6)) - return ERROR; - if(!memmem(buf, buflen, "1:y1:q", 6)) - return -1; - if(memmem(buf, buflen, "1:q4:ping", 9)) - return PING; - if(memmem(buf, buflen, "1:q9:find_node", 14)) - return FIND_NODE; - if(memmem(buf, buflen, "1:q9:get_peers", 14)) - return GET_PEERS; - if(memmem(buf, buflen, "1:q13:announce_peer", 19)) - return ANNOUNCE_PEER; - return -1; - - overflow: - debugf("Truncated message.\n"); - return -1; +usage: + fprintf(stderr, "dht-bootstrap [-q] [-4 ADDR4] [-6 ADDR6] port [node port...]\n"); + exit(EXIT_FAILURE); }