-
-/* 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;
-}