THE SOFTWARE.
*/
+#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
-#include <poll.h>
-#include <stdarg.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
-#define MIN(x, y) ((x) <= (y) ? (x) : (y))
+#include <sys/caprights.h>
+#include <sys/capsicum.h>
+#include <sys/event.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
-#ifdef __linux__
-// clang-format off
-void setproctitle_fast(const char *_fmt, ...) {}
-// clang-format on
-#endif
+#undef MIN
+#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);
-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 MAX_PKT_SIZE 1536
#define ERROR 0
#define REPLY 1
#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 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;
unsigned char id[160];
struct sockaddr_storage ss;
socklen_t sslen;
+ char _pad[4];
};
#define CIRCULAR_LIST_SIZE 256
static time_t token_bucket_time;
static int token_bucket_tokens;
-static FILE *dht_debug = NULL;
+static bool verbose = true;
-static void
-debugf(const char *format, ...)
+struct Pkt {
+ unsigned char buf[MAX_PKT_SIZE];
+ struct sockaddr_storage ss;
+ size_t len;
+ socklen_t salen;
+ char _pad[4];
+};
+
+static struct Pkt pkt;
+static int sockW = -1;
+
+// ------------------------ >8 ------------------------
+
+static const char *
+getAddr(const struct sockaddr *sa, const socklen_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 bool
+stricttol(const char *p, unsigned long *l, char **q)
{
- if (dht_debug == NULL)
- return;
- va_list args;
- va_start(args, format);
- vfprintf(dht_debug, format, args);
- va_end(args);
- fflush(dht_debug);
+ if ((isdigit((int)(p[0])) == 0) || (p[0] == '0'))
+ return false;
+ errno = 0;
+ *l = strtoul(p, q, 10);
+ if (errno != 0) {
+ perror("strtoul()");
+ return false;
+ }
+ return true;
}
static int
-is_martian(struct sockaddr *sa)
+parse_message(
+ const unsigned char *buf,
+ const size_t buflen,
+ unsigned char *tid_return,
+ size_t *tid_len,
+ unsigned char *id_return,
+ unsigned char *info_hash_return,
+ unsigned char *target_return,
+ unsigned short *port_return,
+ unsigned char *token_return,
+ size_t *token_len,
+ unsigned char *nodes_return,
+ size_t *nodes_len,
+ unsigned char *nodes6_return,
+ size_t *nodes6_len,
+ unsigned char *values_return,
+ size_t *values_len,
+ unsigned char *values6_return,
+ size_t *values6_len,
+ int *want_return,
+ const struct sockaddr *sa,
+ const socklen_t saLen)
{
- switch (sa->sa_family) {
+ /* 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 (((const unsigned char *)ptr) + (len) > (buf) + (buflen)) \
+ goto overflow;
+
+ unsigned char *p;
+ unsigned long l = 0;
+ char *q;
+ if (tid_return) {
+ p = memmem(buf, buflen, "1:t", 3);
+ if (p) {
+ if (stricttol((const char *)p + 3, &l, &q) && (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) {
+ if (stricttol((const char *)p + 5, &l, &q) && (q && *q == 'e') &&
+ (l > 0 && l < 0x10000))
+ *port_return = (unsigned short)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) {
+ if (stricttol((const char *)p + 7, &l, &q) && (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) {
+ if (stricttol((const char *)p + 7, &l, &q) && (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) {
+ if (stricttol((const char *)p + 8, &l, &q) && (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) {
+ size_t i = p - buf + 9;
+ unsigned long j = 0, j6 = 0;
+ for (;;) {
+ if (stricttol((const char *)buf + i, &l, &q) && (q && *q == ':') &&
+ (l > 0)) {
+ CHECK(q + 1, l);
+ if (l == 6) {
+ if (j + l > *values_len)
+ continue;
+ i = q + 1 + l - (const 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 - (const 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 - (const 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) {
+ size_t 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 bool
+is_martian(const struct sockaddr_storage *ss)
+{
+ switch (((const struct sockaddr *)ss)->sa_family) {
case AF_INET: {
- struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)ss;
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 struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)ss;
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) ||
(address[15] == 0 || address[15] == 1)) ||
(memcmp(address, v4prefix, 12) == 0);
}
-
- default:
- return 0;
}
+ return false;
}
/* Forget about the ``XOR-metric''. An id is just a path from the
host order. */
static void
-make_tid(unsigned char *tid_return, const char *prefix, unsigned short seqno)
+make_tid(unsigned char *tid_return, const char *prefix, const unsigned short seqno)
{
tid_return[0] = prefix[0] & 0xFF;
tid_return[1] = prefix[1] & 0xFF;
return circular(list->head, list->tail);
}
-static int
+static bool
list_empty(struct circular_list *list)
{
return list_elements(list) == 0;
struct sockaddr_storage *ss,
socklen_t *sslen)
{
- int n;
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)
/* We just learnt about a node, not necessarily a new one. Confirm is 1 if
the node sent a message, 2 if it sent us a reply. */
-static int
-new_node(unsigned char *id, struct sockaddr *sa, socklen_t salen, int confirm)
+static bool
+new_node(
+ const unsigned char *id,
+ const struct sockaddr *sa,
+ const socklen_t salen,
+ const int confirm)
{
struct circular_list *list;
- int i;
-
if (sa->sa_family == AF_INET)
list = confirm >= 2 ? &v4_confirmed : &v4_new;
else if (sa->sa_family == AF_INET6)
include such a node if we have plenty of space. */
if (confirm == 1 && list_free(list) < 32)
- return 0;
+ return false;
- 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)
- return 0;
+ return false;
}
memcpy(&list->nodes[list->tail].id, id, 160);
list->tail = (list->tail + 1) % CIRCULAR_LIST_SIZE;
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;
+ return true;
}
-static int
+static bool
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));
+ MIN(MAX_TOKEN_BUCKET_TOKENS, 4 * (int)(now - token_bucket_time));
token_bucket_time = now;
}
-
if (token_bucket_tokens == 0)
- return 0;
-
+ return false;
token_bucket_tokens--;
- return 1;
+ return true;
}
-static int
-send_request(struct circular_list *list, int dopop, int doping, int want)
+static ssize_t
+pktSend(const struct Pkt *p)
{
- unsigned char ttid[4];
- struct sockaddr_storage ss;
- socklen_t sslen;
- int rc;
+ int s = -1;
+ const struct sockaddr *sa = (const struct sockaddr *)&(p->ss);
+ switch (sa->sa_family) {
+ case AF_INET:
+ s = dht_socket;
+ break;
+ case AF_INET6:
+ s = dht_socket6;
+ break;
+ default:
+ abort();
+ }
+ return sendto(s, p->buf, p->len, 0, sa, p->salen);
+}
+
+static ssize_t
+dht_send(
+ const void *buf,
+ const size_t len,
+ const struct sockaddr *sa,
+ const socklen_t salen)
+{
+ if (salen == 0)
+ abort();
+ memcpy(&(pkt.ss), sa, sizeof(struct sockaddr_storage));
+ memcpy(pkt.buf, buf, len);
+ pkt.len = len;
+ pkt.salen = salen;
+ if (sockW == -1)
+ return pktSend(&pkt);
+ return write(sockW, &pkt, sizeof(struct Pkt));
+}
+
+static ssize_t
+send_ping(
+ const struct sockaddr *sa,
+ const socklen_t salen,
+ const unsigned char *tid,
+ const size_t tid_len)
+{
+ 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, "e1:q4:ping1:t%zu:", 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, (size_t)i, sa, salen);
+
+fail:
+ errno = ENOSPC;
+ return -1;
+}
+
+static ssize_t
+send_pong(
+ const struct sockaddr *sa,
+ const socklen_t salen,
+ const unsigned char *tid,
+ const size_t 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%zu:", 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, (size_t)i, sa, salen);
+
+fail:
+ errno = ENOSPC;
+ return -1;
+}
+
+static ssize_t
+send_find_node(
+ const struct sockaddr *sa,
+ const socklen_t salen,
+ const unsigned char *tid,
+ const size_t tid_len,
+ const unsigned char *target,
+ const 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%zu:", 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, (size_t)i, sa, salen);
+
+fail:
+ errno = ENOSPC;
+ return -1;
+}
+static ssize_t
+send_request(
+ struct circular_list *list,
+ const bool dopop,
+ const bool doping,
+ const int want)
+{
if (list_empty(list))
return 0;
+ struct sockaddr_storage ss;
+ socklen_t sslen;
if (dopop) {
- rc = list_pop(list, &ss, &sslen);
- if (rc == 0)
+ if (list_pop(list, &ss, &sslen) == 0)
return 0;
} else {
- rc = list_random(list, NULL, &ss, &sslen);
- if (rc == 0)
+ if (list_random(list, NULL, &ss, &sslen) == 0)
return 0;
}
+ unsigned char ttid[4];
if (doping) {
make_tid(ttid, "pn", 0);
- debugf("Sending ping.\n");
return send_ping((struct sockaddr *)&ss, sslen, ttid, 4);
} else {
unsigned char id[20];
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);
}
}
r.rlim_cur = 0;
r.rlim_max = 0;
if (setrlimit(res, &r) == -1) {
- err(EXIT_FAILURE, "can not setrlimit()");
+ err(EXIT_FAILURE, "setrlimit()");
+ }
+}
+
+static ssize_t
+send_nodes(
+ const struct sockaddr *sa,
+ const socklen_t salen,
+ const unsigned char *tid,
+ const size_t tid_len,
+ const unsigned char *nodes,
+ const size_t nodes_len,
+ const unsigned char *nodes6,
+ const size_t 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%zu:", 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%zu:", nodes6_len);
+ INC(i, rc, 2048);
+ COPY(buf, i, nodes6, nodes6_len, 2048);
+ }
+ rc = snprintf(buf + i, 2048 - i, "e1:t%zu:", 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, (size_t)i, sa, salen);
+
+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();
+ }
+
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ unsigned char id[20];
+ int n = 0;
+ while (n < 8) {
+ if (list_random(list, id, &ss, &sslen) < 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;
+}
+
+static ssize_t
+send_random_nodes(
+ const struct sockaddr *sa,
+ const socklen_t salen,
+ const unsigned char *tid,
+ const size_t 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);
+}
+
+static ssize_t
+send_error(
+ const struct sockaddr *sa,
+ const socklen_t salen,
+ const unsigned char *tid,
+ const size_t tid_len,
+ const 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%zu:", 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, (size_t)i, sa, salen);
+
+fail:
+ errno = ENOSPC;
+ return -1;
}
int
main(int argc, char **argv)
{
errno = 0;
- int quiet = 0;
char *ipv4addr = NULL;
char *ipv6addr = NULL;
int opt = 0;
- while (1) {
+ for (;;) {
opt = getopt(argc, argv, "q4:6:");
if (opt < 0)
break;
switch (opt) {
case 'q':
- quiet = 1;
+ verbose = 0;
break;
case '4':
ipv4addr = strdup(optarg);
}
arc4random_buf(myid, sizeof myid);
-
memcpy(my_v, "1:v4:JB\0\0", 9);
- have_v = 1;
-
- if (!quiet)
- dht_debug = stdout;
+ memset(&(pkt.ss), 0, sizeof(struct sockaddr_storage));
int rc = 0;
unsigned char ttid[4];
infop = info;
while (infop) {
make_tid(ttid, "pn", 0);
- debugf("Sending ping.\n");
- send_ping(infop->ai_addr, infop->ai_addrlen, ttid, 4);
+ if (send_ping(infop->ai_addr, infop->ai_addrlen, ttid, 4) == -1)
+ err(EXIT_FAILURE, "sendto()");
infop = infop->ai_next;
}
freeaddrinfo(info);
-
i++;
}
+ close(STDIN_FILENO);
+ if (!verbose)
+ close(STDOUT_FILENO);
+
+ int sockets[2];
+ if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, sockets) != 0)
+ err(EXIT_FAILURE, "socketpair()");
+ int sockR = sockets[0];
+ sockW = sockets[1];
+ pid_t pid = fork();
+ if (pid == -1)
+ err(EXIT_FAILURE, "fork()");
+ if (pid == 0) {
+ close(sockW);
+
+ rlimited(RLIMIT_NPROC);
+ rlimited(RLIMIT_FSIZE);
+ rlimited(RLIMIT_NOFILE);
+ ssize_t n = 0;
+ unsigned char buf[sizeof(struct Pkt)];
+ for (;;) {
+ n = read(sockR, buf, sizeof buf);
+ if (n == -1) {
+ perror("read()");
+ continue;
+ }
+ if (pktSend((struct Pkt *)buf) == -1)
+ perror("sendto()");
+ }
+ }
+ close(sockR);
+
+ cap_rights_t caprights;
+ cap_rights_init(&caprights, CAP_WRITE);
+ if (cap_rights_limit(STDERR_FILENO, &caprights) != 0)
+ err(EXIT_FAILURE, "cap_rights_limit(stderr)");
+ if (!verbose)
+ if (cap_rights_limit(STDOUT_FILENO, &caprights) != 0)
+ err(EXIT_FAILURE, "cap_rights_limit(stdout)");
+ if (cap_rights_limit(sockW, &caprights) != 0)
+ err(EXIT_FAILURE, "cap_rights_limit(sockW)");
+ if (cap_enter() != 0)
+ err(EXIT_FAILURE, "cap_enter()");
+
token_bucket_time = time(NULL);
token_bucket_tokens = MAX_TOKEN_BUCKET_TOKENS;
- 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 (quiet)
- close(STDOUT_FILENO);
+ int kq = kqueue();
+ if (kq == -1)
+ err(EXIT_FAILURE, "kqueue()");
+ struct kevent chs[2];
+ struct kevent ev;
+ int chsLen = 0;
+ if (dht_socket != -1) {
+ EV_SET(&(chs[chsLen]), dht_socket, EVFILT_READ, EV_ADD, 0, 0, NULL);
+ chsLen++;
+ }
+ if (dht_socket6 != -1) {
+ EV_SET(&(chs[chsLen]), dht_socket6, EVFILT_READ, EV_ADD, 0, 0, NULL);
+ chsLen++;
+ }
+
rlimited(RLIMIT_NPROC);
rlimited(RLIMIT_FSIZE);
-#if __FreeBSD__
rlimited(RLIMIT_NOFILE);
-#endif // __FreeBSD__
- while (1) {
- int tv_sec = 0;
+ struct timespec tm;
+ bool send4;
+ for (;;) {
if ((dht_socket >= 0 && list_elements(&v4_confirmed) <= 16) ||
(dht_socket6 >= 0 && list_elements(&v6_confirmed) <= 16))
- tv_sec = 0;
+ tm.tv_sec = 0;
else
- tv_sec = random() % 30;
- int tv_msec = random() % 1000;
+ tm.tv_sec = random() % 30;
+ tm.tv_nsec = 1000000 * (random() % 1000);
setproctitle_fast(
"%d+%d %d+%d",
list_elements(&v4_new),
list_elements(&v6_new));
- rc = poll(fds, 2, tv_sec * 1000 + tv_msec);
- if (rc < 0) {
- perror("poll");
- sleep(1);
- }
+ rc = kevent(kq, chs, chsLen, &ev, 1, &tm);
+ if (rc < 0)
+ err(EXIT_FAILURE, "kevent()");
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;
- int nodes_len = 256, nodes6_len = 1024;
- unsigned short port;
- unsigned char values[2048], values6[2048];
- int values_len = 2048, values6_len = 2048;
- int want;
+ ssize_t got = -1;
+ unsigned char buf[MAX_PKT_SIZE];
struct sockaddr_storage source_storage;
struct sockaddr *source = (struct sockaddr *)&source_storage;
socklen_t sourcelen = sizeof(source_storage);
- 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 (ev.flags & EV_ERROR) {
+ fprintf(stderr, "EV_ERROR: %s\n", strerror((int)(ev.data)));
+ } else {
+ got = recvfrom((int)ev.ident, buf, MAX_PKT_SIZE, 0, source, &sourcelen);
}
-
- if (rc < 0 || sourcelen > sizeof(struct sockaddr_storage))
+ if (got < 0 || sourcelen > sizeof(struct sockaddr_storage))
goto dontread;
-
- if (is_martian(source))
+ if (is_martian(&source_storage))
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) {
- buf[rc] = '\0';
+ if (got < MAX_PKT_SIZE) {
+ buf[got] = '\0';
} else {
- debugf("Overlong message.\n");
+ if (verbose)
+ printf("%s : overlong message\n", getAddr(source, sourcelen));
goto dontread;
}
+ int message;
+ unsigned char tid[16];
+ unsigned char id[20];
+ unsigned char info_hash[20];
+ unsigned char target[20];
+ unsigned char nodes[256];
+ unsigned char nodes6[1024];
+ unsigned char token[128];
+ size_t tid_len = sizeof tid;
+ size_t token_len = sizeof token;
+ size_t nodes_len = sizeof nodes;
+ size_t nodes6_len = sizeof nodes6;
+ unsigned short port;
+ unsigned char values[2048];
+ unsigned char values6[2048];
+ size_t values_len = sizeof values;
+ size_t values6_len = sizeof values6;
+ int want;
message = parse_message(
buf,
- rc,
+ (size_t)got,
tid,
&tid_len,
id,
&values_len,
values6,
&values6_len,
- &want);
+ &want,
+ source,
+ sourcelen);
if (id_cmp(id, myid) == 0) {
- debugf("Received message from self.\n");
+ if (verbose)
+ printf(
+ "%s : received message from self\n",
+ getAddr(source, sourcelen));
goto dontread;
}
if (message > REPLY) {
/* Rate limit requests. */
if (!token_bucket()) {
- debugf("Dropping request due to rate limiting.\n");
+ if (verbose)
+ printf(
+ "%s : dropping request due to rate limiting\n",
+ getAddr(source, sourcelen));
goto dontread;
}
}
switch (message) {
case REPLY:
if (tid_len != 4) {
- debugf("Broken node truncates transaction ids.\n");
+ 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 (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 (verbose)
+ printf(
+ "%s -> NODES (%zu+%zu)\n",
+ getAddr(source, sourcelen),
+ nodes_len / 26,
+ nodes6_len / 38);
if (nodes_len % 26 != 0 || nodes6_len % 38 != 0) {
- debugf("Unexpected length for node info!\n");
+ 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++) {
- unsigned char *ni = nodes + i * 26;
+ size_t n;
+ for (n = 0; n < nodes_len / 26; n++) {
+ unsigned char *ni = nodes + n * 26;
struct sockaddr_in sin;
if (id_cmp(ni, myid) == 0)
continue;
memcpy(&sin.sin_port, ni + 24, 2);
new_node(ni, (struct sockaddr *)&sin, sizeof(sin), 0);
}
- for (i = 0; i < nodes6_len / 38; i++) {
- unsigned char *ni = nodes6 + i * 38;
+ for (n = 0; n < nodes6_len / 38; n++) {
+ unsigned char *ni = nodes6 + n * 38;
struct sockaddr_in6 sin6;
if (id_cmp(ni, myid) == 0)
continue;
}
}
} 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 (%zu)\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");
+ if (verbose)
+ printf("%s -> ANNOUNCE_PEER\n", getAddr(source, sourcelen));
send_error(
source,
sourcelen,
loop. */
if (dht_socket6 < 0)
- send4 = 1;
+ send4 = true;
else if (dht_socket < 0)
- send4 = 0;
+ send4 = false;
else
- send4 = random() % 2;
+ send4 = (random() % 2) == 1;
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);
+ send_request(&v4_new, true, list_free(&v4_new) < 8, want);
else if (!list_empty(&v4_confirmed))
- send_request(&v4_confirmed, 0, 0, want);
+ send_request(&v4_confirmed, false, false, want);
} else {
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);
+ send_request(&v6_new, true, list_free(&v6_new) < 8, want);
else if (!list_empty(&v6_confirmed))
- send_request(&v6_confirmed, 0, 0, want);
+ send_request(&v6_confirmed, false, false, want);
}
}
fprintf(stderr, "dht-bootstrap [-q] [-4 ADDR4] [-6 ADDR6] port [node port...]\n");
exit(EXIT_FAILURE);
}
-
-/* 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)
-{
- 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, 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
-
-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;
-}