diff --git a/libs/libblade/src/blade_peer.c b/libs/libblade/src/blade_peer.c index e67e733b6b..8bb96ae676 100644 --- a/libs/libblade/src/blade_peer.c +++ b/libs/libblade/src/blade_peer.c @@ -33,14 +33,22 @@ #include "blade.h" +#define KS_DHT_TPOOL_MIN 2 +#define KS_DHT_TPOOL_MAX 8 +#define KS_DHT_TPOOL_STACK (1024 * 256) +#define KS_DHT_TPOOL_IDLE 10 + typedef enum { BP_NONE = 0, - BP_MYPOOL = (1 << 0) + BP_MYPOOL = (1 << 0), + BP_MYTPOOL = (1 << 1) } bppvt_flag_t; struct blade_peer_s { bppvt_flag_t flags; ks_pool_t *pool; + ks_thread_pool_t *tpool; + ks_dht_t *dht; }; @@ -60,33 +68,75 @@ KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP) flags = bp->flags; pool = bp->pool; + if (bp->dht) ks_dht_destroy(&bp->dht); + if (bp->tpool && (flags & BP_MYTPOOL)) ks_thread_pool_destroy(&bp->tpool); + ks_pool_free(bp->pool, &bp); - if (pool && (flags & BP_MYPOOL)) { - ks_pool_close(&pool); - } + if (pool && (flags & BP_MYPOOL)) ks_pool_close(&pool); return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool) +KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, ks_thread_pool_t *tpool, ks_dht_nodeid_t *nodeid) { bppvt_flag_t newflags = BP_NONE; blade_peer_t *bp = NULL; + ks_dht_t *dht = NULL; if (!pool) { newflags |= BP_MYPOOL; ks_pool_open(&pool); + ks_assert(pool); } + if (!tpool) { + newflags |= BP_MYTPOOL; + ks_thread_pool_create(&tpool, BLADE_PEER_TPOOL_MIN, BLADE_PEER_TPOOL_MAX, BLADE_PEER_TPOOL_STACK, KS_PRI_NORMAL, BLADE_PEER_TPOOL_IDLE); + ks_assert(tpool); + } + ks_dht_create(&dht, pool, tpool, nodeid); + ks_assert(dht); bp = ks_pool_alloc(pool, sizeof(*bp)); - bp->pool = pool; bp->flags = newflags; + bp->pool = pool; + bp->tpool = tpool; + bp->dht = dht; *bpP = bp; return KS_STATUS_SUCCESS; } +KS_DECLARE(ks_dht_nodeid_t *) blade_peer_myid(blade_peer_t *bp) +{ + ks_assert(bp); + ks_assert(bp->dht); + + return &bp->dht->nodeid; +} + +KS_DECLARE(void) blade_peer_autoroute(blade_peer_t *bp, ks_bool_t autoroute, ks_port_t port) +{ + ks_assert(bp); + + ks_dht_autoroute(bp->dht, autoroute, port); +} + +KS_DECLARE(ks_status_t) blade_peer_bind(blade_peer_t *bp, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint) +{ + ks_assert(bp); + ks_assert(addr); + + return ks_dht_bind(bp->dht, addr, endpoint); +} + +KS_DECLARE(void) blade_peer_pulse(blade_peer_t *bp, int32_t timeout) +{ + ks_assert(bp); + ks_assert(timeout >= 0); + + ks_dht_pulse(bp->dht, timeout); +} /* For Emacs: * Local Variables: diff --git a/libs/libblade/src/blade_stack.c b/libs/libblade/src/blade_stack.c index 9dc4ba2f36..7e4bcf01d9 100644 --- a/libs/libblade/src/blade_stack.c +++ b/libs/libblade/src/blade_stack.c @@ -35,12 +35,14 @@ typedef enum { BH_NONE = 0, - BH_MYPOOL = (1 << 0) + BH_MYPOOL = (1 << 0), + BH_MYTPOOL = (1 << 1) } bhpvt_flag_t; struct blade_handle_s { - ks_pool_t *pool; bhpvt_flag_t flags; + ks_pool_t *pool; + ks_thread_pool_t *tpool; blade_peer_t *peer; }; @@ -62,6 +64,7 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) pool = bh->pool; blade_peer_destroy(&bh->peer); + if (bh->tpool && (flags & BH_MYTPOOL)) ks_thread_pool_destroy(&bh->tpool); ks_pool_free(bh->pool, &bh); @@ -72,26 +75,78 @@ KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP) return KS_STATUS_SUCCESS; } -KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool) +KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool, ks_thread_pool_t *tpool, const char *nodeid) { bhpvt_flag_t newflags = BH_NONE; blade_handle_t *bh = NULL; + ks_dht_nodeid_t nid; + + ks_assert(nodeid); + ks_assert(strlen(nodeid) == (KS_DHT_NODEID_SIZE * 2)); if (!pool) { newflags |= BH_MYPOOL; ks_pool_open(&pool); } - + if (!tpool) { + newflags |= BH_MYTPOOL; + ks_thread_pool_create(&tpool, BLADE_HANDLE_TPOOL_MIN, BLADE_HANDLE_TPOOL_MAX, BLADE_HANDLE_TPOOL_STACK, KS_PRI_NORMAL, BLADE_HANDLE_TPOOL_IDLE); + ks_assert(tpool); + } + bh = ks_pool_alloc(pool, sizeof(*bh)); - bh->pool = pool; bh->flags = newflags; - blade_peer_create(&bh->peer, bh->pool); + bh->pool = pool; + bh->tpool = tpool; + ks_dht_dehex(nid.id, nodeid, KS_DHT_NODEID_SIZE); + blade_peer_create(&bh->peer, bh->pool, bh->tpool, &nid); *bhP = bh; return KS_STATUS_SUCCESS; } +KS_DECLARE(void) blade_handle_myid(blade_handle_t *bh, char *buffer) +{ + ks_dht_nodeid_t *nodeid = NULL; + + ks_assert(bh); + ks_assert(bh->peer); + + nodeid = blade_peer_myid(bh->peer); + ks_dht_hex(nodeid->id, buffer, KS_DHT_NODEID_SIZE); +} + +KS_DECLARE(void) blade_handle_autoroute(blade_handle_t *bh, ks_bool_t autoroute, ks_port_t port) +{ + ks_assert(bh); + ks_assert(bh->peer); + + blade_peer_autoroute(bh->peer, autoroute, port); +} + +KS_DECLARE(ks_status_t) blade_handle_bind(blade_handle_t *bh, const char *ip, ks_port_t port, ks_dht_endpoint_t **endpoint) +{ + ks_sockaddr_t addr; + int family = AF_INET; + + ks_assert(bh); + ks_assert(ip); + ks_assert(port); + + if (ip[1] != '.' && ip[2] != '.' && ip[3] != '.') family = AF_INET6; + + ks_addr_set(&addr, ip, port, family); + return blade_peer_bind(bh->peer, &addr, endpoint); +} + +KS_DECLARE(void) blade_handle_pulse(blade_handle_t *bh, int32_t timeout) +{ + ks_assert(bh); + ks_assert(timeout >= 0); + + blade_peer_pulse(bh->peer, timeout); +} /* For Emacs: diff --git a/libs/libblade/src/include/blade.h b/libs/libblade/src/include/blade.h index 4fe1dac783..8df818f7b9 100644 --- a/libs/libblade/src/include/blade.h +++ b/libs/libblade/src/include/blade.h @@ -34,6 +34,7 @@ #ifndef _BLADE_H_ #define _BLADE_H_ #include +#include #include #include "blade_types.h" #include "blade_stack.h" diff --git a/libs/libblade/src/include/blade_peer.h b/libs/libblade/src/include/blade_peer.h index e7db60230b..3c0bb5925d 100644 --- a/libs/libblade/src/include/blade_peer.h +++ b/libs/libblade/src/include/blade_peer.h @@ -35,9 +35,18 @@ #define _BPCP_H_ #include +#define BLADE_PEER_TPOOL_MIN 2 +#define BLADE_PEER_TPOOL_MAX 8 +#define BLADE_PEER_TPOOL_STACK (1024 * 256) +#define BLADE_PEER_TPOOL_IDLE 10 + KS_BEGIN_EXTERN_C -KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool); +KS_DECLARE(ks_status_t) blade_peer_create(blade_peer_t **bpP, ks_pool_t *pool, ks_thread_pool_t *tpool, ks_dht_nodeid_t *nodeid); KS_DECLARE(ks_status_t) blade_peer_destroy(blade_peer_t **bpP); +KS_DECLARE(ks_dht_nodeid_t *) blade_peer_myid(blade_peer_t *bp); +KS_DECLARE(void) blade_peer_autoroute(blade_peer_t *bp, ks_bool_t autoroute, ks_port_t port); +KS_DECLARE(ks_status_t) blade_peer_bind(blade_peer_t *bp, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint); +KS_DECLARE(void) blade_peer_pulse(blade_peer_t *bp, int32_t timeout); KS_END_EXTERN_C #endif diff --git a/libs/libblade/src/include/blade_stack.h b/libs/libblade/src/include/blade_stack.h index 8fdec5f0c2..a5744e459f 100644 --- a/libs/libblade/src/include/blade_stack.h +++ b/libs/libblade/src/include/blade_stack.h @@ -35,9 +35,18 @@ #define _BLADE_STACK_H_ #include +#define BLADE_HANDLE_TPOOL_MIN 2 +#define BLADE_HANDLE_TPOOL_MAX 8 +#define BLADE_HANDLE_TPOOL_STACK (1024 * 256) +#define BLADE_HANDLE_TPOOL_IDLE 10 + KS_BEGIN_EXTERN_C KS_DECLARE(ks_status_t) blade_handle_destroy(blade_handle_t **bhP); -KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool); +KS_DECLARE(ks_status_t) blade_handle_create(blade_handle_t **bhP, ks_pool_t *pool, ks_thread_pool_t *tpool, const char *nodeid); +KS_DECLARE(void) blade_handle_myid(blade_handle_t *bh, char *buffer); +KS_DECLARE(void) blade_handle_autoroute(blade_handle_t *bh, ks_bool_t autoroute, ks_port_t port); +KS_DECLARE(ks_status_t) blade_handle_bind(blade_handle_t *bh, const char *ip, ks_port_t port, ks_dht_endpoint_t **endpoint); +KS_DECLARE(void) blade_handle_pulse(blade_handle_t *bh, int32_t timeout); KS_END_EXTERN_C #endif diff --git a/libs/libblade/test/Makefile.am b/libs/libblade/test/Makefile.am index 37e67e0460..d577b95507 100644 --- a/libs/libblade/test/Makefile.am +++ b/libs/libblade/test/Makefile.am @@ -7,6 +7,11 @@ testbuild_SOURCES = testbuild.c tap.c testbuild_CFLAGS = $(AM_CFLAGS) testbuild_LDADD = $(TEST_LDADD) +check_PROGRAMS += bladec +bladec_SOURCES = bladec.c tap.c +bladec_CFLAGS = $(AM_CFLAGS) +bladec_LDADD = $(TEST_LDADD) + TESTS=$(check_PROGRAMS) diff --git a/libs/libblade/test/bladec.c b/libs/libblade/test/bladec.c new file mode 100644 index 0000000000..f03fe52de4 --- /dev/null +++ b/libs/libblade/test/bladec.c @@ -0,0 +1,204 @@ +#include "blade.h" +#include "tap.h" + +#ifdef _WIN32 +#define STDIO_FD(_fs) _fileno(_fs) +#define READ(_fd, _buffer, _count) _read(_fd, _buffer, _count) +#else +#define STDIO_FD(_fs) fileno(_fs) +#define READ(_fd, _buffer, _count) read(_fd, _buffer, _count) +#endif + +#define CONSOLE_INPUT_MAX 512 + +ks_bool_t g_shutdown = KS_FALSE; +char g_console_input[CONSOLE_INPUT_MAX]; +size_t g_console_input_length = 0; +size_t g_console_input_eol = 0; + +void loop(blade_handle_t *bh); +void process_console_input(blade_handle_t *bh, char *line); + +typedef void (*command_callback)(blade_handle_t *bh, char *args); + +struct command_def_s { + const char *cmd; + command_callback callback; +}; + +void command_test(blade_handle_t *bh, char *args); +void command_quit(blade_handle_t *bh, char *args); +void command_myid(blade_handle_t *bh, char *args); +void command_bind(blade_handle_t *bh, char *args); + +static const struct command_def_s command_defs[] = { + { "test", command_test }, + { "quit", command_quit }, + { "myid", command_myid }, + { "bind", command_bind }, + + { NULL, NULL } +}; + + +int main(int argc, char **argv) +{ + blade_handle_t *bh = NULL; + const char *nodeid; + + ks_assert(argc >= 2); + + nodeid = argv[1]; + + ks_global_set_default_logger(KS_LOG_LEVEL_DEBUG); + + blade_init(); + + blade_handle_create(&bh, NULL, NULL, nodeid); + + blade_handle_autoroute(bh, KS_TRUE, KS_DHT_DEFAULT_PORT); + + loop(bh); + + blade_handle_destroy(&bh); + + blade_shutdown(); + + return 0; +} + +void buffer_console_input(void) +{ + ssize_t bytes = 0; + struct pollfd poll[1]; + poll[0].fd = STDIO_FD(stdin); + poll[0].events = POLLIN | POLLERR; + + if (ks_poll(poll, 1, 1) > 0) { + if (poll[0].revents & POLLIN) { + if ((bytes = READ(poll[0].fd, g_console_input + g_console_input_length, CONSOLE_INPUT_MAX - g_console_input_length)) <= 0) { + // @todo error + return; + } + g_console_input_length += bytes; + } + } +} + +void loop(blade_handle_t *bh) +{ + while (!g_shutdown) { + ks_bool_t eol = KS_FALSE; + buffer_console_input(); + + for (; g_console_input_eol < g_console_input_length; ++g_console_input_eol) { + char c = g_console_input[g_console_input_eol]; + if (c == '\r' || c == '\n') { + eol = KS_TRUE; + break; + } + } + if (eol) { + g_console_input[g_console_input_eol] = '\0'; + process_console_input(bh, g_console_input); + g_console_input_eol++; + for (; g_console_input_eol < g_console_input_length; ++g_console_input_eol) { + char c = g_console_input[g_console_input_eol]; + if (c != '\r' && c != '\n') break; + } + if (g_console_input_eol == g_console_input_length) g_console_input_eol = g_console_input_length = 0; + else { + memcpy(g_console_input, g_console_input + g_console_input_eol, g_console_input_length - g_console_input_eol); + g_console_input_length -= g_console_input_eol; + g_console_input_eol = 0; + } + } + if (g_console_input_length == CONSOLE_INPUT_MAX) { + // @todo lines must not exceed 512 bytes, treat as error and ignore buffer until next new line? + ks_assert(0); + } + blade_handle_pulse(bh, 1); + } +} + +void parse_argument(char **input, char **arg, char terminator) +{ + char *tmp; + + ks_assert(input); + ks_assert(*input); + ks_assert(arg); + + tmp = *input; + *arg = tmp; + + while (*tmp && *tmp != terminator) ++tmp; + if (*tmp == terminator) { + *tmp = '\0'; + ++tmp; + } + *input = tmp; +} + +void process_console_input(blade_handle_t *bh, char *line) +{ + char *args = line; + char *cmd = NULL; + ks_bool_t found = KS_FALSE; + + ks_log(KS_LOG_DEBUG, "Output: %s\n", line); + + parse_argument(&args, &cmd, ' '); + + ks_log(KS_LOG_DEBUG, "Command: %s, Args: %s\n", cmd, args); + + for (int32_t index = 0; command_defs[index].cmd; ++index) { + if (!strcmp(command_defs[index].cmd, cmd)) { + found = KS_TRUE; + command_defs[index].callback(bh, args); + } + } + if (!found) ks_log(KS_LOG_INFO, "Command '%s' unknown.\n", cmd); +} + +void command_test(blade_handle_t *bh, char *args) +{ + ks_log(KS_LOG_DEBUG, "Hello World!\n"); +} + +void command_quit(blade_handle_t *bh, char *args) +{ + ks_assert(bh); + ks_assert(args); + + ks_log(KS_LOG_DEBUG, "Shutting down\n"); + g_shutdown = KS_TRUE; +} + +void command_myid(blade_handle_t *bh, char *args) +{ + char buf[KS_DHT_NODEID_SIZE * 2 + 1]; + + ks_assert(bh); + ks_assert(args); + + blade_handle_myid(bh, buf); + + ks_log(KS_LOG_INFO, "%s\n", buf); +} + +void command_bind(blade_handle_t *bh, char *args) +{ + char *ip = NULL; + char *port = NULL; + ks_port_t p; + + ks_assert(args); + + parse_argument(&args, &ip, ' '); + parse_argument(&args, &port, ' '); + + p = atoi(port); // @todo use strtol for error handling + + blade_handle_bind(bh, ip, p, NULL); +} diff --git a/libs/libblade/test/testbuild.c b/libs/libblade/test/testbuild.c index 4af08b1982..29998fa1a7 100644 --- a/libs/libblade/test/testbuild.c +++ b/libs/libblade/test/testbuild.c @@ -11,7 +11,7 @@ int main(void) plan(1); - status = blade_handle_create(&bh, NULL); + status = blade_handle_create(&bh, NULL, NULL); status = blade_handle_destroy(&bh); ok(status == KS_STATUS_SUCCESS); diff --git a/libs/libks/src/dht/ks_dht.c b/libs/libks/src/dht/ks_dht.c index 4cffb7cef7..a70adef12b 100644 --- a/libs/libks/src/dht/ks_dht.c +++ b/libs/libks/src/dht/ks_dht.c @@ -8,13 +8,14 @@ void ks_dht_transaction_destructor(void *ptr) { ks_dht_transaction_destroy((ks_d void ks_dht_storageitem_destructor(void *ptr) { ks_dht_storageitem_destroy((ks_dht_storageitem_t **)&ptr); } -KS_DECLARE(ks_status_t) ks_dht_create(ks_dht_t **dht, ks_pool_t *pool, ks_thread_pool_t *tpool) +KS_DECLARE(ks_status_t) ks_dht_create(ks_dht_t **dht, ks_pool_t *pool, ks_thread_pool_t *tpool, ks_dht_nodeid_t *nodeid) { ks_bool_t pool_alloc = !pool; ks_dht_t *d = NULL; ks_status_t ret = KS_STATUS_SUCCESS; ks_assert(dht); + ks_assert(nodeid); *dht = NULL; @@ -49,6 +50,8 @@ KS_DECLARE(ks_status_t) ks_dht_create(ks_dht_t **dht, ks_pool_t *pool, ks_thread ks_assert(d->tpool); } + d->nodeid = *nodeid; + /** * Default autorouting to disabled. */ @@ -366,7 +369,7 @@ KS_DECLARE(ks_status_t) ks_dht_autoroute_check(ks_dht_t *dht, const ks_sockaddr_ if (!ep && dht->autoroute) { ks_sockaddr_t addr; if ((ret = ks_addr_set(&addr, ip, dht->autoroute_port, raddr->family)) != KS_STATUS_SUCCESS) return ret; - if ((ret = ks_dht_bind(dht, NULL, &addr, &ep)) != KS_STATUS_SUCCESS) return ret; + if ((ret = ks_dht_bind(dht, &addr, &ep)) != KS_STATUS_SUCCESS) return ret; } /** @@ -419,7 +422,7 @@ KS_DECLARE(void) ks_dht_register_error(ks_dht_t *dht, const char *value, ks_dht_ } -KS_DECLARE(ks_status_t) ks_dht_bind(ks_dht_t *dht, const ks_dht_nodeid_t *nodeid, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint) +KS_DECLARE(ks_status_t) ks_dht_bind(ks_dht_t *dht, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint) { ks_socket_t sock = KS_SOCK_INVALID; ks_dht_endpoint_t *ep = NULL; @@ -466,7 +469,7 @@ KS_DECLARE(ks_status_t) ks_dht_bind(ks_dht_t *dht, const ks_dht_nodeid_t *nodeid /** * Allocate the endpoint to track the local socket. */ - ks_dht_endpoint_create(&ep, dht->pool, nodeid, addr, sock); + ks_dht_endpoint_create(&ep, dht->pool, addr, sock); ks_assert(ep); /** @@ -506,24 +509,24 @@ KS_DECLARE(ks_status_t) ks_dht_bind(ks_dht_t *dht, const ks_dht_nodeid_t *nodeid if (ep->addr.family == AF_INET) { if (!dht->rt_ipv4 && (ret = ks_dhtrt_initroute(&dht->rt_ipv4, dht, dht->pool)) != KS_STATUS_SUCCESS) goto done; if ((ret = ks_dhtrt_create_node(dht->rt_ipv4, - ep->nodeid, + dht->nodeid, KS_DHT_LOCAL, ep->addr.host, ep->addr.port, KS_DHTRT_CREATE_DEFAULT, - &ep->node)) != KS_STATUS_SUCCESS) goto done; + &dht->node)) != KS_STATUS_SUCCESS) goto done; } else { if (!dht->rt_ipv6 && (ret = ks_dhtrt_initroute(&dht->rt_ipv6, dht, dht->pool)) != KS_STATUS_SUCCESS) goto done; if ((ret = ks_dhtrt_create_node(dht->rt_ipv6, - ep->nodeid, + dht->nodeid, KS_DHT_LOCAL, ep->addr.host, ep->addr.port, KS_DHTRT_CREATE_DEFAULT, - &ep->node)) != KS_STATUS_SUCCESS) goto done; + &dht->node)) != KS_STATUS_SUCCESS) goto done; } /** - * Do not release the ep->node, keep it alive until cleanup + * Do not release the dht->node, keep it alive until cleanup */ /** @@ -788,6 +791,19 @@ KS_DECLARE(char *) ks_dht_hex(const uint8_t *data, char *buffer, ks_size_t len) return buffer; } +KS_DECLARE(uint8_t *) ks_dht_dehex(uint8_t *data, const char *buffer, ks_size_t len) +{ + const char *t = buffer; + + ks_assert(data); + ks_assert(buffer); + ks_assert(!(len & 1)); + + for (int i = 0; i < len; ++i, t += 2) sscanf(t, "%2hhx", &data[i]); + + return data; +} + KS_DECLARE(void) ks_dht_utility_nodeid_xor(ks_dht_nodeid_t *dest, ks_dht_nodeid_t *src1, ks_dht_nodeid_t *src2) { ks_assert(dest); @@ -1361,7 +1377,7 @@ KS_DECLARE(ks_status_t) ks_dht_query_setup(ks_dht_t *dht, if (args) *args = a; - ben_dict_set(a, ben_blob("id", 2), ben_blob(ep->nodeid.id, KS_DHT_NODEID_SIZE)); + ben_dict_set(a, ben_blob("id", 2), ben_blob(dht->nodeid.id, KS_DHT_NODEID_SIZE)); *message = msg; @@ -1414,7 +1430,7 @@ KS_DECLARE(ks_status_t) ks_dht_response_setup(ks_dht_t *dht, if (args) *args = r; - ben_dict_set(r, ben_blob("id", 2), ben_blob(ep->nodeid.id, KS_DHT_NODEID_SIZE)); + ben_dict_set(r, ben_blob("id", 2), ben_blob(dht->nodeid.id, KS_DHT_NODEID_SIZE)); *message = msg; @@ -1546,7 +1562,7 @@ KS_DECLARE(ks_status_t) ks_dht_process_query(ks_dht_t *dht, ks_dht_message_t *me message->args_id = *id; ks_log(KS_LOG_DEBUG, "Creating node %s\n", ks_dht_hex(id->id, id_buf, KS_DHT_NODEID_SIZE)); - if ((ret = ks_dhtrt_create_node(message->endpoint->node->table, + if ((ret = ks_dhtrt_create_node(message->endpoint->addr.family == AF_INET ? dht->rt_ipv4 : dht->rt_ipv6, *id, KS_DHT_REMOTE, message->raddr.host, @@ -1620,7 +1636,7 @@ KS_DECLARE(ks_status_t) ks_dht_process_response(ks_dht_t *dht, ks_dht_message_t message->args_id = *id; ks_log(KS_LOG_DEBUG, "Creating node %s\n", ks_dht_hex(id->id, id_buf, KS_DHT_NODEID_SIZE)); - if ((ret = ks_dhtrt_create_node(message->endpoint->node->table, + if ((ret = ks_dhtrt_create_node(message->endpoint->addr.family == AF_INET ? dht->rt_ipv4 : dht->rt_ipv6, *id, KS_DHT_REMOTE, message->raddr.host, @@ -1629,7 +1645,7 @@ KS_DECLARE(ks_status_t) ks_dht_process_response(ks_dht_t *dht, ks_dht_message_t &node)) != KS_STATUS_SUCCESS) goto done; ks_log(KS_LOG_DEBUG, "Touching node %s\n", ks_dht_hex(id->id, id_buf, KS_DHT_NODEID_SIZE)); - if ((ret = ks_dhtrt_touch_node(message->endpoint->node->table, *id)) != KS_STATUS_SUCCESS) goto done; + if ((ret = ks_dhtrt_touch_node(message->endpoint->addr.family == AF_INET ? dht->rt_ipv4 : dht->rt_ipv6, *id)) != KS_STATUS_SUCCESS) goto done; tid = (uint32_t *)message->transactionid; @@ -2292,7 +2308,7 @@ KS_DECLARE(ks_status_t) ks_dht_query_findnode(ks_dht_t *dht, ks_dht_job_t *job) ben_dict_set(a, ben_blob("target", 6), ben_blob(job->query_target.id, KS_DHT_NODEID_SIZE)); // Only request both v4 and v6 if we have both interfaces bound and are looking for our own node id, aka bootstrapping - if (dht->rt_ipv4 && dht->rt_ipv6 && !memcmp(message->endpoint->nodeid.id, job->query_target.id, KS_DHT_NODEID_SIZE)) { + if (dht->rt_ipv4 && dht->rt_ipv6 && !memcmp(dht->nodeid.id, job->query_target.id, KS_DHT_NODEID_SIZE)) { struct bencode *want = ben_list(); ben_list_append_str(want, "n4"); ben_list_append_str(want, "n6"); diff --git a/libs/libks/src/dht/ks_dht.h b/libs/libks/src/dht/ks_dht.h deleted file mode 100644 index 3652c47a39..0000000000 --- a/libs/libks/src/dht/ks_dht.h +++ /dev/null @@ -1,593 +0,0 @@ -#ifndef KS_DHT_H -#define KS_DHT_H - -#include "ks.h" -#include "ks_bencode.h" -#include "sodium.h" - -KS_BEGIN_EXTERN_C - - -#define KS_DHT_DEFAULT_PORT 5309 - -#define KS_DHT_TPOOL_MIN 2 -#define KS_DHT_TPOOL_MAX 8 -#define KS_DHT_TPOOL_STACK (1024 * 256) -#define KS_DHT_TPOOL_IDLE 10 - -#define KS_DHT_DATAGRAM_BUFFER_SIZE 1000 - -//#define KS_DHT_RECV_BUFFER_SIZE 0xFFFF - -#define KS_DHT_NODEID_SIZE 20 - -#define KS_DHT_RESPONSE_NODES_MAX_SIZE 8 - -#define KS_DHT_MESSAGE_TRANSACTIONID_MAX_SIZE 20 -#define KS_DHT_MESSAGE_TYPE_MAX_SIZE 20 -#define KS_DHT_MESSAGE_QUERY_MAX_SIZE 20 -#define KS_DHT_MESSAGE_ERROR_MAX_SIZE 256 - -#define KS_DHT_TRANSACTION_EXPIRATION 10 -#define KS_DHT_TRANSACTIONS_PULSE 1 - -#define KS_DHT_SEARCH_RESULTS_MAX_SIZE 8 // @todo replace with KS_DHTRT_BUCKET_SIZE - -#define KS_DHT_STORAGEITEM_PKEY_SIZE crypto_sign_PUBLICKEYBYTES -#define KS_DHT_STORAGEITEM_SKEY_SIZE crypto_sign_SECRETKEYBYTES -#define KS_DHT_STORAGEITEM_SALT_MAX_SIZE 64 -#define KS_DHT_STORAGEITEM_SIGNATURE_SIZE crypto_sign_BYTES -#define KS_DHT_STORAGEITEM_EXPIRATION 7200 -#define KS_DHT_STORAGEITEM_KEEPALIVE 300 -#define KS_DHT_STORAGEITEMS_PULSE 10 - -#define KS_DHT_TOKEN_SIZE SHA_DIGEST_LENGTH -#define KS_DHT_TOKEN_EXPIRATION 300 -#define KS_DHT_TOKENS_PULSE 1 - -#define KS_DHTRT_MAXQUERYSIZE 20 - -typedef struct ks_dht_s ks_dht_t; -typedef struct ks_dht_datagram_s ks_dht_datagram_t; -typedef struct ks_dht_job_s ks_dht_job_t; -typedef struct ks_dht_nodeid_s ks_dht_nodeid_t; -typedef struct ks_dht_token_s ks_dht_token_t; -typedef struct ks_dht_storageitem_pkey_s ks_dht_storageitem_pkey_t; -typedef struct ks_dht_storageitem_skey_s ks_dht_storageitem_skey_t; -typedef struct ks_dht_storageitem_signature_s ks_dht_storageitem_signature_t; -typedef struct ks_dht_message_s ks_dht_message_t; -typedef struct ks_dht_endpoint_s ks_dht_endpoint_t; -typedef struct ks_dht_transaction_s ks_dht_transaction_t; -typedef struct ks_dht_search_s ks_dht_search_t; -typedef struct ks_dht_publish_s ks_dht_publish_t; -typedef struct ks_dht_distribute_s ks_dht_distribute_t; -typedef struct ks_dht_node_s ks_dht_node_t; -typedef struct ks_dhtrt_routetable_s ks_dhtrt_routetable_t; -typedef struct ks_dhtrt_querynodes_s ks_dhtrt_querynodes_t; -typedef struct ks_dht_storageitem_s ks_dht_storageitem_t; - - -typedef ks_status_t (*ks_dht_job_callback_t)(ks_dht_t *dht, ks_dht_job_t *job); -typedef ks_status_t (*ks_dht_message_callback_t)(ks_dht_t *dht, ks_dht_message_t *message); -//typedef ks_status_t (*ks_dht_search_callback_t)(ks_dht_t *dht, ks_dht_search_t *search); -typedef ks_status_t (*ks_dht_storageitem_callback_t)(ks_dht_t *dht, ks_dht_storageitem_t *item); - - -struct ks_dht_datagram_s { - ks_pool_t *pool; - ks_dht_t *dht; - ks_dht_endpoint_t *endpoint; - ks_sockaddr_t raddr; - uint8_t buffer[KS_DHT_DATAGRAM_BUFFER_SIZE]; - ks_size_t buffer_length; -}; - -/** - * Note: This must remain a structure for casting from raw data - */ -struct ks_dht_nodeid_s { - uint8_t id[KS_DHT_NODEID_SIZE]; -}; - -enum ks_afflags_t { ifv4=AF_INET, ifv6=AF_INET6, ifboth=AF_INET+AF_INET6}; -enum ks_dht_nodetype_t { KS_DHT_REMOTE=0x01, - KS_DHT_LOCAL=0x02, - KS_DHT_BOTH=KS_DHT_REMOTE+KS_DHT_LOCAL }; - -enum ks_create_node_flags_t { - KS_DHTRT_CREATE_DEFAULT=0, - KS_DHTRT_CREATE_PING, - KS_DHTRT_CREATE_TOUCH -}; - -struct ks_dht_node_s { - ks_dht_nodeid_t nodeid; - ks_sockaddr_t addr; - enum ks_dht_nodetype_t type; /* local or remote */ - ks_dhtrt_routetable_t* table; - ks_rwl_t *reflock; -}; - -struct ks_dht_token_s { - uint8_t token[KS_DHT_TOKEN_SIZE]; -}; - -enum ks_dht_job_state_t { - KS_DHT_JOB_STATE_QUERYING, - KS_DHT_JOB_STATE_RESPONDING, - KS_DHT_JOB_STATE_EXPIRING, - KS_DHT_JOB_STATE_COMPLETING, -}; - -enum ks_dht_job_result_t { - KS_DHT_JOB_RESULT_SUCCESS = 0, - KS_DHT_JOB_RESULT_EXPIRED, - KS_DHT_JOB_RESULT_ERROR, - KS_DHT_JOB_RESULT_FAILURE, -}; - -struct ks_dht_job_s { - ks_pool_t *pool; - ks_dht_t *dht; - ks_dht_job_t *next; - - enum ks_dht_job_state_t state; - enum ks_dht_job_result_t result; - - ks_sockaddr_t raddr; // will obtain local endpoint node id when creating message using raddr - int32_t attempts; - - //enum ks_dht_job_type_t type; - ks_dht_job_callback_t query_callback; - ks_dht_job_callback_t finish_callback; - - void *data; - ks_dht_message_t *response; - - // job specific query parameters - ks_dht_nodeid_t query_target; - struct bencode *query_salt; - int64_t query_cas; - ks_dht_token_t query_token; - ks_dht_storageitem_t *query_storageitem; - - // error response parameters - int64_t error_code; - struct bencode *error_description; - - // job specific response parameters - ks_dht_node_t *response_id; - ks_dht_node_t *response_nodes[KS_DHT_RESPONSE_NODES_MAX_SIZE]; - ks_size_t response_nodes_count; - ks_dht_node_t *response_nodes6[KS_DHT_RESPONSE_NODES_MAX_SIZE]; - ks_size_t response_nodes6_count; - - ks_dht_token_t response_token; - int64_t response_seq; - ks_bool_t response_hasitem; - ks_dht_storageitem_t *response_storageitem; -}; - -struct ks_dhtrt_routetable_s { - void* internal; - ks_pool_t* pool; - ks_logger_t logger; -}; - -struct ks_dhtrt_querynodes_s { - ks_dht_nodeid_t nodeid; /* in: id to query */ - enum ks_afflags_t family; /* in: AF_INET or AF_INET6 or both */ - enum ks_dht_nodetype_t type; /* remote, local, or both */ - uint8_t max; /* in: maximum to return */ - uint8_t count; /* out: number returned */ - ks_dht_node_t* nodes[ KS_DHTRT_MAXQUERYSIZE ]; /* out: array of peers (ks_dht_node_t* nodes[incount]) */ -}; - -struct ks_dht_storageitem_pkey_s { - uint8_t key[KS_DHT_STORAGEITEM_PKEY_SIZE]; -}; - -struct ks_dht_storageitem_skey_s { - uint8_t key[KS_DHT_STORAGEITEM_SKEY_SIZE]; -}; - -struct ks_dht_storageitem_signature_s { - uint8_t sig[KS_DHT_STORAGEITEM_SIGNATURE_SIZE]; -}; - -struct ks_dht_message_s { - ks_pool_t *pool; - ks_dht_endpoint_t *endpoint; - ks_sockaddr_t raddr; - struct bencode *data; - uint8_t transactionid[KS_DHT_MESSAGE_TRANSACTIONID_MAX_SIZE]; - ks_size_t transactionid_length; - ks_dht_transaction_t *transaction; - char type[KS_DHT_MESSAGE_TYPE_MAX_SIZE]; - struct bencode *args; - ks_dht_nodeid_t args_id; -}; - -struct ks_dht_endpoint_s { - ks_pool_t *pool; - ks_dht_nodeid_t nodeid; - ks_sockaddr_t addr; - ks_socket_t sock; - // @todo make sure this node is unlocked, and never gets destroyed, should also never use local nodes in search results as they can be internal - // network addresses, not what others have contacted through - ks_dht_node_t *node; -}; - -struct ks_dht_transaction_s { - ks_pool_t *pool; - ks_dht_job_t *job; - uint32_t transactionid; - //ks_dht_nodeid_t target; // @todo look at moving this into job now - ks_dht_job_callback_t callback; - ks_time_t expiration; - ks_bool_t finished; -}; - -struct ks_dht_search_s { - ks_pool_t *pool; - ks_dhtrt_routetable_t *table; - ks_dht_nodeid_t target; - ks_dht_job_callback_t callback; - void *data; - ks_mutex_t *mutex; - ks_hash_t *searched; - int32_t searching; - ks_dht_node_t *results[KS_DHT_SEARCH_RESULTS_MAX_SIZE]; - ks_dht_nodeid_t distances[KS_DHT_SEARCH_RESULTS_MAX_SIZE]; - ks_size_t results_length; -}; - -struct ks_dht_publish_s { - ks_pool_t *pool; - ks_dht_job_callback_t callback; - void *data; - int64_t cas; - ks_dht_storageitem_t *item; -}; - -struct ks_dht_distribute_s { - ks_pool_t *pool; - ks_dht_storageitem_callback_t callback; - void *data; - ks_mutex_t *mutex; - int32_t publishing; - int64_t cas; - ks_dht_storageitem_t *item; -}; - -struct ks_dht_storageitem_s { - ks_pool_t *pool; - ks_dht_nodeid_t id; - ks_time_t expiration; - ks_time_t keepalive; - struct bencode *v; - - ks_mutex_t *mutex; - volatile int32_t refc; - ks_dht_storageitem_callback_t callback; - - ks_bool_t mutable; - ks_dht_storageitem_pkey_t pk; - ks_dht_storageitem_skey_t sk; - struct bencode *salt; - int64_t seq; - ks_dht_storageitem_signature_t sig; -}; - -struct ks_dht_s { - ks_pool_t *pool; - ks_bool_t pool_alloc; - - ks_thread_pool_t *tpool; - ks_bool_t tpool_alloc; - - ks_bool_t autoroute; - ks_port_t autoroute_port; - - ks_hash_t *registry_type; - ks_hash_t *registry_query; - ks_hash_t *registry_error; - - ks_dht_endpoint_t **endpoints; - int32_t endpoints_length; - int32_t endpoints_size; - ks_hash_t *endpoints_hash; - struct pollfd *endpoints_poll; - - ks_q_t *send_q; - ks_dht_message_t *send_q_unsent; - uint8_t recv_buffer[KS_DHT_DATAGRAM_BUFFER_SIZE + 1]; // Add 1, if we receive it then overflow error - ks_size_t recv_buffer_length; - - ks_mutex_t *jobs_mutex; - ks_dht_job_t *jobs_first; - ks_dht_job_t *jobs_last; - - ks_time_t transactions_pulse; - ks_mutex_t *transactionid_mutex; - volatile uint32_t transactionid_next; - ks_hash_t *transactions_hash; - - ks_dhtrt_routetable_t *rt_ipv4; - ks_dhtrt_routetable_t *rt_ipv6; - - ks_time_t tokens_pulse; - volatile uint32_t token_secret_current; - volatile uint32_t token_secret_previous; - ks_time_t token_secret_expiration; - - ks_time_t storageitems_pulse; - ks_hash_t *storageitems_hash; -}; - -/** - * Constructor function for ks_dht_t. - * Will allocate and initialize internal state including registration of message handlers. - * @param dht dereferenced out pointer to the allocated dht instance - * @param pool pointer to the memory pool used by the dht instance, may be NULL to create a new memory pool internally - * @param tpool pointer to a thread pool used by the dht instance, may be NULL to create a new thread pool internally - * @return The ks_status_t result: KS_STATUS_SUCCESS, KS_STATUS_NO_MEM - */ -KS_DECLARE(ks_status_t) ks_dht_create(ks_dht_t **dht, ks_pool_t *pool, ks_thread_pool_t *tpool); - -/** - * Destructor function for ks_dht_t. - * Will deinitialize and deallocate internal state. - * @param dht dereferenced in/out pointer to the dht instance, NULL upon return - */ -KS_DECLARE(void) ks_dht_destroy(ks_dht_t **dht); - -/** - * Enable or disable (default) autorouting support. - * When enabled, autorouting will allow sending to remote addresses on interfaces which are not yet bound. - * The address will be bound with the provided autoroute port when this occurs. - * @param dht pointer to the dht instance - * @param autoroute enable or disable autorouting - * @param port when enabling autorouting this port will be used to bind new addresses, may be 0 to use the default DHT port - */ -KS_DECLARE(void) ks_dht_autoroute(ks_dht_t *dht, ks_bool_t autoroute, ks_port_t port); - -/** - * Register a callback for a specific message type. - * Will overwrite any duplicate handlers. - * @param dht pointer to the dht instance - * @param value string of the type text under the 'y' key of a message - * @param callback the callback to be called when a message matches - */ -KS_DECLARE(void) ks_dht_register_type(ks_dht_t *dht, const char *value, ks_dht_message_callback_t callback); - -/** - * Register a callback for a specific message query. - * Will overwrite any duplicate handlers. - * @param dht pointer to the dht instance - * @param value string of the type text under the 'q' key of a message - * @param callback the callback to be called when a message matches - */ -KS_DECLARE(void) ks_dht_register_query(ks_dht_t *dht, const char *value, ks_dht_message_callback_t callback); - -/** - * Register a callback for a specific message error. - * Will overwrite any duplicate handlers. - * @param dht pointer to the dht instance - * @param value string of the errorcode under the first item of the 'e' key of a message - * @param callback the callback to be called when a message matches - */ -KS_DECLARE(void) ks_dht_register_error(ks_dht_t *dht, const char *value, ks_dht_message_callback_t callback); - -/** - * Bind a local address and port for receiving UDP datagrams. - * @param dht pointer to the dht instance - * @param nodeid pointer to a nodeid for this endpoint, may be NULL to generate one randomly - * @param addr pointer to the local address information - * @param endpoint dereferenced out pointer to the allocated endpoint, may be NULL to ignore endpoint output - * @return The ks_status_t result: KS_STATUS_SUCCESS, KS_STATUS_FAIL, ... - * @see ks_socket_option - * @see ks_addr_bind - * @see ks_dht_endpoint_alloc - * @see ks_dht_endpoint_init - * @see ks_hash_insert - * @see ks_dhtrt_initroute - * @see ks_dhtrt_create_node - */ -KS_DECLARE(ks_status_t) ks_dht_bind(ks_dht_t *dht, const ks_dht_nodeid_t *nodeid, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint); - -/** - * Pulse the internals of dht. - * Handles receiving UDP datagrams, dispatching processing, handles expirations, throttled message sending, route table pulsing, etc. - * @param dht pointer to the dht instance - * @param timeout timeout value used when polling sockets for new UDP datagrams - */ -KS_DECLARE(void) ks_dht_pulse(ks_dht_t *dht, int32_t timeout); - - -KS_DECLARE(char *) ks_dht_hex(const uint8_t *data, char *buffer, ks_size_t len); -/** - * - */ -KS_DECLARE(ks_status_t) ks_dht_storageitem_target_immutable(const uint8_t *value, ks_size_t value_length, ks_dht_nodeid_t *target); - -/** - * - */ -KS_DECLARE(ks_status_t) ks_dht_storageitem_target_mutable(ks_dht_storageitem_pkey_t *pk, const uint8_t *salt, ks_size_t salt_length, ks_dht_nodeid_t *target); - -/** - * - */ -KS_DECLARE(ks_status_t) ks_dht_storageitem_signature_generate(ks_dht_storageitem_signature_t *sig, - ks_dht_storageitem_skey_t *sk, - const uint8_t *salt, - ks_size_t salt_length, - int64_t sequence, - const uint8_t *value, - ks_size_t value_length); - -/** - * - */ -KS_DECLARE(void) ks_dht_storageitem_reference(ks_dht_storageitem_t *item); - -/** - * - */ -KS_DECLARE(void) ks_dht_storageitem_dereference(ks_dht_storageitem_t *item); - -/** - * - */ -KS_DECLARE(void) ks_dht_storageitem_callback(ks_dht_storageitem_t *item, ks_dht_storageitem_callback_t callback); - -/** - * - */ -KS_DECLARE(void) ks_dht_storageitems_read_lock(ks_dht_t *dht); - -/** - * - */ -KS_DECLARE(void) ks_dht_storageitems_read_unlock(ks_dht_t *dht); - -/** - * - */ -KS_DECLARE(void) ks_dht_storageitems_write_lock(ks_dht_t *dht); - -/** - * - */ -KS_DECLARE(void) ks_dht_storageitems_write_unlock(ks_dht_t *dht); - -/** - * - */ -KS_DECLARE(ks_dht_storageitem_t *) ks_dht_storageitems_find(ks_dht_t *dht, ks_dht_nodeid_t *target); - -/** - * - */ -KS_DECLARE(ks_status_t) ks_dht_storageitems_insert(ks_dht_t *dht, ks_dht_storageitem_t *item); - -/** - * - */ -KS_DECLARE(void) ks_dht_ping(ks_dht_t *dht, const ks_sockaddr_t *raddr, ks_dht_job_callback_t callback, void *data); - -/** - * - */ -KS_DECLARE(void) ks_dht_findnode(ks_dht_t *dht, - const ks_sockaddr_t *raddr, - ks_dht_job_callback_t callback, - void *data, - ks_dht_nodeid_t *target); - -/** - * - */ -KS_DECLARE(void) ks_dht_get(ks_dht_t *dht, - const ks_sockaddr_t *raddr, - ks_dht_job_callback_t callback, - void *data, - ks_dht_nodeid_t *target, - const uint8_t *salt, - ks_size_t salt_length); - -/** - * - */ -KS_DECLARE(void) ks_dht_put(ks_dht_t *dht, - const ks_sockaddr_t *raddr, - ks_dht_job_callback_t callback, - void *data, - ks_dht_token_t *token, - int64_t cas, - ks_dht_storageitem_t *item); - -/** - * Create a network search of the closest nodes to a target. - * @param dht pointer to the dht instance - * @param family either AF_INET or AF_INET6 for the appropriate network to search - * @param target pointer to the nodeid for the target to be searched - * @param callback an optional callback to add to the search when it is finished - * @param search dereferenced out pointer to the allocated search, may be NULL to ignore search output - * @see ks_dht_search_create - * @see ks_hash_insert - * @see ks_dht_findnode - */ -KS_DECLARE(void) ks_dht_search(ks_dht_t *dht, - ks_dht_job_callback_t callback, - void *data, - ks_dhtrt_routetable_t *table, - ks_dht_nodeid_t *target); - -KS_DECLARE(void) ks_dht_publish(ks_dht_t *dht, - const ks_sockaddr_t *raddr, - ks_dht_job_callback_t callback, - void *data, - int64_t cas, - ks_dht_storageitem_t *item); - -KS_DECLARE(void) ks_dht_distribute(ks_dht_t *dht, - ks_dht_storageitem_callback_t callback, - void *data, - ks_dhtrt_routetable_t *table, - int64_t cas, - ks_dht_storageitem_t *item); - -/** - * route table methods - * - */ -KS_DECLARE(ks_status_t) ks_dhtrt_initroute(ks_dhtrt_routetable_t **tableP, - ks_dht_t *dht, - ks_pool_t *pool); -KS_DECLARE(void) ks_dhtrt_deinitroute(ks_dhtrt_routetable_t **table); - -KS_DECLARE(ks_status_t) ks_dhtrt_create_node(ks_dhtrt_routetable_t* table, - ks_dht_nodeid_t nodeid, - enum ks_dht_nodetype_t type, - char* ip, unsigned short port, - enum ks_create_node_flags_t flags, - ks_dht_node_t** node); - -KS_DECLARE(ks_status_t) ks_dhtrt_delete_node(ks_dhtrt_routetable_t* table, ks_dht_node_t* node); - -KS_DECLARE(ks_status_t) ks_dhtrt_touch_node(ks_dhtrt_routetable_t* table, ks_dht_nodeid_t nodeid); -KS_DECLARE(ks_status_t) ks_dhtrt_expire_node(ks_dhtrt_routetable_t* table, ks_dht_nodeid_t nodeid); - -KS_DECLARE(uint8_t) ks_dhtrt_findclosest_nodes(ks_dhtrt_routetable_t* table, ks_dhtrt_querynodes_t* query); -KS_DECLARE(ks_dht_node_t*) ks_dhtrt_find_node(ks_dhtrt_routetable_t* table, ks_dht_nodeid_t id); - -KS_DECLARE(ks_status_t) ks_dhtrt_sharelock_node(ks_dht_node_t* node); -KS_DECLARE(ks_status_t) ks_dhtrt_release_node(ks_dht_node_t* node); -KS_DECLARE(ks_status_t) ks_dhtrt_release_querynodes(ks_dhtrt_querynodes_t* query); - -KS_DECLARE(void) ks_dhtrt_process_table(ks_dhtrt_routetable_t* table); - -KS_DECLARE(uint32_t) ks_dhtrt_serialize(ks_dhtrt_routetable_t* table, void** ptr); -KS_DECLARE(ks_status_t) ks_dhtrt_deserialize(ks_dhtrt_routetable_t* table, void* ptr); - - -/* debugging aids */ -KS_DECLARE(void) ks_dhtrt_dump(ks_dhtrt_routetable_t* table, int level); - - -KS_END_EXTERN_C - -#endif /* KS_DHT_H */ - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/libs/libks/src/dht/ks_dht_endpoint.c b/libs/libks/src/dht/ks_dht_endpoint.c index d8c2965fc4..3651bea39d 100644 --- a/libs/libks/src/dht/ks_dht_endpoint.c +++ b/libs/libks/src/dht/ks_dht_endpoint.c @@ -7,7 +7,6 @@ */ KS_DECLARE(ks_status_t) ks_dht_endpoint_create(ks_dht_endpoint_t **endpoint, ks_pool_t *pool, - const ks_dht_nodeid_t *nodeid, const ks_sockaddr_t *addr, ks_socket_t sock) { @@ -23,8 +22,6 @@ KS_DECLARE(ks_status_t) ks_dht_endpoint_create(ks_dht_endpoint_t **endpoint, ks_assert(ep); ep->pool = pool; - if (!nodeid) randombytes_buf(ep->nodeid.id, KS_DHT_NODEID_SIZE); - else memcpy(ep->nodeid.id, nodeid->id, KS_DHT_NODEID_SIZE); ep->addr = *addr; ep->sock = sock; diff --git a/libs/libks/src/dht/ks_dht-int.h b/libs/libks/src/include/ks_dht-int.h similarity index 99% rename from libs/libks/src/dht/ks_dht-int.h rename to libs/libks/src/include/ks_dht-int.h index 6d06d4682b..ad5d20bb9c 100644 --- a/libs/libks/src/dht/ks_dht-int.h +++ b/libs/libks/src/include/ks_dht-int.h @@ -319,7 +319,6 @@ KS_DECLARE(void) ks_dht_job_destroy(ks_dht_job_t **job); */ KS_DECLARE(ks_status_t) ks_dht_endpoint_create(ks_dht_endpoint_t **endpoint, ks_pool_t *pool, - const ks_dht_nodeid_t *nodeid, const ks_sockaddr_t *addr, ks_socket_t sock); KS_DECLARE(void) ks_dht_endpoint_destroy(ks_dht_endpoint_t **endpoint); diff --git a/libs/libks/src/include/ks_dht.h b/libs/libks/src/include/ks_dht.h index 2b5b9cb467..2c84921a87 100644 --- a/libs/libks/src/include/ks_dht.h +++ b/libs/libks/src/include/ks_dht.h @@ -1,101 +1,588 @@ -/* -Copyright (c) 2009-2011 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef _KS_DHT_H -#define _KS_DHT_H +#ifndef KS_DHT_H +#define KS_DHT_H #include "ks.h" #include "ks_bencode.h" +#include "sodium.h" KS_BEGIN_EXTERN_C -typedef enum { - KS_DHT_EVENT_NONE = 0, - KS_DHT_EVENT_VALUES = 1, - KS_DHT_EVENT_VALUES6 = 2, - KS_DHT_EVENT_SEARCH_DONE = 3, - KS_DHT_EVENT_SEARCH_DONE6 = 4 -} ks_dht_event_t; + +#define KS_DHT_DEFAULT_PORT 5309 + +#define KS_DHT_TPOOL_MIN 2 +#define KS_DHT_TPOOL_MAX 8 +#define KS_DHT_TPOOL_STACK (1024 * 256) +#define KS_DHT_TPOOL_IDLE 10 + +#define KS_DHT_DATAGRAM_BUFFER_SIZE 1000 + +//#define KS_DHT_RECV_BUFFER_SIZE 0xFFFF + +#define KS_DHT_NODEID_SIZE 20 + +#define KS_DHT_RESPONSE_NODES_MAX_SIZE 8 + +#define KS_DHT_MESSAGE_TRANSACTIONID_MAX_SIZE 20 +#define KS_DHT_MESSAGE_TYPE_MAX_SIZE 20 +#define KS_DHT_MESSAGE_QUERY_MAX_SIZE 20 +#define KS_DHT_MESSAGE_ERROR_MAX_SIZE 256 + +#define KS_DHT_TRANSACTION_EXPIRATION 10 +#define KS_DHT_TRANSACTIONS_PULSE 1 + +#define KS_DHT_SEARCH_RESULTS_MAX_SIZE 8 // @todo replace with KS_DHTRT_BUCKET_SIZE + +#define KS_DHT_STORAGEITEM_PKEY_SIZE crypto_sign_PUBLICKEYBYTES +#define KS_DHT_STORAGEITEM_SKEY_SIZE crypto_sign_SECRETKEYBYTES +#define KS_DHT_STORAGEITEM_SALT_MAX_SIZE 64 +#define KS_DHT_STORAGEITEM_SIGNATURE_SIZE crypto_sign_BYTES +#define KS_DHT_STORAGEITEM_EXPIRATION 7200 +#define KS_DHT_STORAGEITEM_KEEPALIVE 300 +#define KS_DHT_STORAGEITEMS_PULSE 10 + +#define KS_DHT_TOKEN_SIZE SHA_DIGEST_LENGTH +#define KS_DHT_TOKEN_EXPIRATION 300 +#define KS_DHT_TOKENS_PULSE 1 + +#define KS_DHTRT_MAXQUERYSIZE 20 + +typedef struct ks_dht_s ks_dht_t; +typedef struct ks_dht_datagram_s ks_dht_datagram_t; +typedef struct ks_dht_job_s ks_dht_job_t; +typedef struct ks_dht_nodeid_s ks_dht_nodeid_t; +typedef struct ks_dht_token_s ks_dht_token_t; +typedef struct ks_dht_storageitem_pkey_s ks_dht_storageitem_pkey_t; +typedef struct ks_dht_storageitem_skey_s ks_dht_storageitem_skey_t; +typedef struct ks_dht_storageitem_signature_s ks_dht_storageitem_signature_t; +typedef struct ks_dht_message_s ks_dht_message_t; +typedef struct ks_dht_endpoint_s ks_dht_endpoint_t; +typedef struct ks_dht_transaction_s ks_dht_transaction_t; +typedef struct ks_dht_search_s ks_dht_search_t; +typedef struct ks_dht_publish_s ks_dht_publish_t; +typedef struct ks_dht_distribute_s ks_dht_distribute_t; +typedef struct ks_dht_node_s ks_dht_node_t; +typedef struct ks_dhtrt_routetable_s ks_dhtrt_routetable_t; +typedef struct ks_dhtrt_querynodes_s ks_dhtrt_querynodes_t; +typedef struct ks_dht_storageitem_s ks_dht_storageitem_t; -typedef enum { - DHT_PARAM_AUTOROUTE = 1 -} ks_dht_param_t; - -typedef enum { - KS_DHT_AF_INET4 = (1 << 0), - KS_DHT_AF_INET6 = (1 << 1) -} ks_dht_af_flag_t; +typedef ks_status_t (*ks_dht_job_callback_t)(ks_dht_t *dht, ks_dht_job_t *job); +typedef ks_status_t (*ks_dht_message_callback_t)(ks_dht_t *dht, ks_dht_message_t *message); +//typedef ks_status_t (*ks_dht_search_callback_t)(ks_dht_t *dht, ks_dht_search_t *search); +typedef ks_status_t (*ks_dht_storageitem_callback_t)(ks_dht_t *dht, ks_dht_storageitem_t *item); -typedef void (*dht_callback_t)(void *closure, ks_dht_event_t event, const unsigned char *info_hash, const void *data, size_t data_len); +struct ks_dht_datagram_s { + ks_pool_t *pool; + ks_dht_t *dht; + ks_dht_endpoint_t *endpoint; + ks_sockaddr_t raddr; + uint8_t buffer[KS_DHT_DATAGRAM_BUFFER_SIZE]; + ks_size_t buffer_length; +}; -typedef struct dht_handle_s dht_handle_t; +/** + * Note: This must remain a structure for casting from raw data + */ +struct ks_dht_nodeid_s { + uint8_t id[KS_DHT_NODEID_SIZE]; +}; -KS_DECLARE(int) dht_periodic(dht_handle_t *h, const void *buf, size_t buflen, ks_sockaddr_t *from); -KS_DECLARE(ks_status_t) ks_dht_init(dht_handle_t **handle, ks_dht_af_flag_t af_flags, const unsigned char *id, unsigned int port); -KS_DECLARE(void) ks_dht_set_param(dht_handle_t *h, ks_dht_param_t param, ks_bool_t val); -KS_DECLARE(ks_status_t) ks_dht_add_ip(dht_handle_t *h, char *ip, int port); -KS_DECLARE(void) ks_dht_start(dht_handle_t *h); -KS_DECLARE(int) dht_insert_node(dht_handle_t *h, const unsigned char *id, ks_sockaddr_t *sa); -KS_DECLARE(int) dht_ping_node(dht_handle_t *h, ks_sockaddr_t *sa); -KS_DECLARE(int) dht_search(dht_handle_t *h, const unsigned char *id, int port, int af, dht_callback_t callback, void *closure); -KS_DECLARE(int) dht_nodes(dht_handle_t *h, int af, int *good_return, int *dubious_return, int *cached_return, int *incoming_return); -KS_DECLARE(ks_status_t) ks_dht_one_loop(dht_handle_t *h, int timeout); -KS_DECLARE(ks_status_t) ks_dht_get_bind_addrs(dht_handle_t *h, const ks_sockaddr_t ***addrs, ks_size_t *addrlen); -KS_DECLARE(void) ks_dht_set_callback(dht_handle_t *h, dht_callback_t callback, void *closure); -KS_DECLARE(void) ks_dht_set_port(dht_handle_t *h, unsigned int port); -KS_DECLARE(void) dht_dump_tables(dht_handle_t *h, FILE *f); -KS_DECLARE(int) dht_get_nodes(dht_handle_t *h, struct sockaddr_in *sin, int *num, struct sockaddr_in6 *sin6, int *num6); -KS_DECLARE(int) dht_uninit(dht_handle_t **h); -KS_DECLARE(void) ks_dht_set_v(dht_handle_t *h, const unsigned char *v); -KS_DECLARE(int) ks_dht_calculate_mutable_storage_target(unsigned char *pk, unsigned char *salt, int salt_length, unsigned char *target, int target_length); -KS_DECLARE(int) ks_dht_generate_mutable_storage_args(struct bencode *data, int64_t sequence, int cas, - unsigned char *id, int id_len, /* querying nodes id */ - const unsigned char *sk, const unsigned char *pk, - unsigned char *salt, unsigned long long salt_length, - unsigned char *token, unsigned long long token_length, - unsigned char *signature, unsigned long long *signature_length, - struct bencode **arguments); +enum ks_afflags_t { ifv4=AF_INET, ifv6=AF_INET6, ifboth=AF_INET+AF_INET6}; +enum ks_dht_nodetype_t { KS_DHT_REMOTE=0x01, + KS_DHT_LOCAL=0x02, + KS_DHT_BOTH=KS_DHT_REMOTE+KS_DHT_LOCAL }; -/* This must be provided by the user. */ -KS_DECLARE(int) dht_blacklisted(const ks_sockaddr_t *sa); -KS_DECLARE(void) dht_hash(void *hash_return, int hash_size, const void *v1, int len1, const void *v2, int len2, const void *v3, int len3); -KS_DECLARE(int) dht_random_bytes(void *buf, size_t size); +enum ks_create_node_flags_t { + KS_DHTRT_CREATE_DEFAULT=0, + KS_DHTRT_CREATE_PING, + KS_DHTRT_CREATE_TOUCH +}; + +struct ks_dht_node_s { + ks_dht_nodeid_t nodeid; + ks_sockaddr_t addr; + enum ks_dht_nodetype_t type; /* local or remote */ + ks_dhtrt_routetable_t* table; + ks_rwl_t *reflock; +}; -KS_DECLARE(int) ks_dht_send_message_mutable(dht_handle_t *h, unsigned char *sk, unsigned char *pk, char **node_id, - char *message_id, int sequence, char *message, ks_time_t life); +struct ks_dht_token_s { + uint8_t token[KS_DHT_TOKEN_SIZE]; +}; -KS_DECLARE(int) ks_dht_send_message_mutable_cjson(dht_handle_t *h, unsigned char *sk, unsigned char *pk, char **node_id, - char *message_id, int sequence, cJSON *message, ks_time_t life); +enum ks_dht_job_state_t { + KS_DHT_JOB_STATE_QUERYING, + KS_DHT_JOB_STATE_RESPONDING, + KS_DHT_JOB_STATE_EXPIRING, + KS_DHT_JOB_STATE_COMPLETING, +}; -typedef void (ks_dht_store_entry_json_cb)(struct dht_handle_s *h, const cJSON *msg, void *obj); -KS_DECLARE(void) ks_dht_store_entry_json_cb_set(struct dht_handle_s *h, ks_dht_store_entry_json_cb *store_json_cb, void *arg); +enum ks_dht_job_result_t { + KS_DHT_JOB_RESULT_SUCCESS = 0, + KS_DHT_JOB_RESULT_EXPIRED, + KS_DHT_JOB_RESULT_ERROR, + KS_DHT_JOB_RESULT_FAILURE, +}; -KS_DECLARE(int) ks_dht_api_find_node(dht_handle_t *h, char *node_id_hex, char *target_hex, ks_bool_t ipv6); +struct ks_dht_job_s { + ks_pool_t *pool; + ks_dht_t *dht; + ks_dht_job_t *next; + enum ks_dht_job_state_t state; + enum ks_dht_job_result_t result; + + ks_sockaddr_t raddr; // will obtain local endpoint node id when creating message using raddr + int32_t attempts; + + //enum ks_dht_job_type_t type; + ks_dht_job_callback_t query_callback; + ks_dht_job_callback_t finish_callback; + + void *data; + ks_dht_message_t *response; + + // job specific query parameters + ks_dht_nodeid_t query_target; + struct bencode *query_salt; + int64_t query_cas; + ks_dht_token_t query_token; + ks_dht_storageitem_t *query_storageitem; + + // error response parameters + int64_t error_code; + struct bencode *error_description; + + // job specific response parameters + ks_dht_node_t *response_id; + ks_dht_node_t *response_nodes[KS_DHT_RESPONSE_NODES_MAX_SIZE]; + ks_size_t response_nodes_count; + ks_dht_node_t *response_nodes6[KS_DHT_RESPONSE_NODES_MAX_SIZE]; + ks_size_t response_nodes6_count; + + ks_dht_token_t response_token; + int64_t response_seq; + ks_bool_t response_hasitem; + ks_dht_storageitem_t *response_storageitem; +}; + +struct ks_dhtrt_routetable_s { + void* internal; + ks_pool_t* pool; + ks_logger_t logger; +}; + +struct ks_dhtrt_querynodes_s { + ks_dht_nodeid_t nodeid; /* in: id to query */ + enum ks_afflags_t family; /* in: AF_INET or AF_INET6 or both */ + enum ks_dht_nodetype_t type; /* remote, local, or both */ + uint8_t max; /* in: maximum to return */ + uint8_t count; /* out: number returned */ + ks_dht_node_t* nodes[ KS_DHTRT_MAXQUERYSIZE ]; /* out: array of peers (ks_dht_node_t* nodes[incount]) */ +}; + +struct ks_dht_storageitem_pkey_s { + uint8_t key[KS_DHT_STORAGEITEM_PKEY_SIZE]; +}; + +struct ks_dht_storageitem_skey_s { + uint8_t key[KS_DHT_STORAGEITEM_SKEY_SIZE]; +}; + +struct ks_dht_storageitem_signature_s { + uint8_t sig[KS_DHT_STORAGEITEM_SIGNATURE_SIZE]; +}; + +struct ks_dht_message_s { + ks_pool_t *pool; + ks_dht_endpoint_t *endpoint; + ks_sockaddr_t raddr; + struct bencode *data; + uint8_t transactionid[KS_DHT_MESSAGE_TRANSACTIONID_MAX_SIZE]; + ks_size_t transactionid_length; + ks_dht_transaction_t *transaction; + char type[KS_DHT_MESSAGE_TYPE_MAX_SIZE]; + struct bencode *args; + ks_dht_nodeid_t args_id; +}; + +struct ks_dht_endpoint_s { + ks_pool_t *pool; + ks_sockaddr_t addr; + ks_socket_t sock; +}; + +struct ks_dht_transaction_s { + ks_pool_t *pool; + ks_dht_job_t *job; + uint32_t transactionid; + //ks_dht_nodeid_t target; // @todo look at moving this into job now + ks_dht_job_callback_t callback; + ks_time_t expiration; + ks_bool_t finished; +}; + +struct ks_dht_search_s { + ks_pool_t *pool; + ks_dhtrt_routetable_t *table; + ks_dht_nodeid_t target; + ks_dht_job_callback_t callback; + void *data; + ks_mutex_t *mutex; + ks_hash_t *searched; + int32_t searching; + ks_dht_node_t *results[KS_DHT_SEARCH_RESULTS_MAX_SIZE]; + ks_dht_nodeid_t distances[KS_DHT_SEARCH_RESULTS_MAX_SIZE]; + ks_size_t results_length; +}; + +struct ks_dht_publish_s { + ks_pool_t *pool; + ks_dht_job_callback_t callback; + void *data; + int64_t cas; + ks_dht_storageitem_t *item; +}; + +struct ks_dht_distribute_s { + ks_pool_t *pool; + ks_dht_storageitem_callback_t callback; + void *data; + ks_mutex_t *mutex; + int32_t publishing; + int64_t cas; + ks_dht_storageitem_t *item; +}; + +struct ks_dht_storageitem_s { + ks_pool_t *pool; + ks_dht_nodeid_t id; + ks_time_t expiration; + ks_time_t keepalive; + struct bencode *v; + + ks_mutex_t *mutex; + volatile int32_t refc; + ks_dht_storageitem_callback_t callback; + + ks_bool_t mutable; + ks_dht_storageitem_pkey_t pk; + ks_dht_storageitem_skey_t sk; + struct bencode *salt; + int64_t seq; + ks_dht_storageitem_signature_t sig; +}; + +struct ks_dht_s { + ks_pool_t *pool; + ks_bool_t pool_alloc; + + ks_thread_pool_t *tpool; + ks_bool_t tpool_alloc; + + ks_dht_nodeid_t nodeid; + // @todo make sure this node is unlocked, and never gets destroyed, should also never use local nodes in search results as they can be internal + // network addresses, not what others have contacted through + ks_dht_node_t *node; + + ks_bool_t autoroute; + ks_port_t autoroute_port; + + ks_hash_t *registry_type; + ks_hash_t *registry_query; + ks_hash_t *registry_error; + + ks_dht_endpoint_t **endpoints; + int32_t endpoints_length; + int32_t endpoints_size; + ks_hash_t *endpoints_hash; + struct pollfd *endpoints_poll; + + ks_q_t *send_q; + ks_dht_message_t *send_q_unsent; + uint8_t recv_buffer[KS_DHT_DATAGRAM_BUFFER_SIZE + 1]; // Add 1, if we receive it then overflow error + ks_size_t recv_buffer_length; + + ks_mutex_t *jobs_mutex; + ks_dht_job_t *jobs_first; + ks_dht_job_t *jobs_last; + + ks_time_t transactions_pulse; + ks_mutex_t *transactionid_mutex; + volatile uint32_t transactionid_next; + ks_hash_t *transactions_hash; + + ks_dhtrt_routetable_t *rt_ipv4; + ks_dhtrt_routetable_t *rt_ipv6; + + ks_time_t tokens_pulse; + volatile uint32_t token_secret_current; + volatile uint32_t token_secret_previous; + ks_time_t token_secret_expiration; + + ks_time_t storageitems_pulse; + ks_hash_t *storageitems_hash; +}; + +/** + * Constructor function for ks_dht_t. + * Will allocate and initialize internal state including registration of message handlers. + * @param dht dereferenced out pointer to the allocated dht instance + * @param pool pointer to the memory pool used by the dht instance, may be NULL to create a new memory pool internally + * @param tpool pointer to a thread pool used by the dht instance, may be NULL to create a new thread pool internally + * @param nodeid pointer to the nodeid for this dht instance + * @return The ks_status_t result: KS_STATUS_SUCCESS, KS_STATUS_NO_MEM + */ +KS_DECLARE(ks_status_t) ks_dht_create(ks_dht_t **dht, ks_pool_t *pool, ks_thread_pool_t *tpool, ks_dht_nodeid_t *nodeid); + +/** + * Destructor function for ks_dht_t. + * Will deinitialize and deallocate internal state. + * @param dht dereferenced in/out pointer to the dht instance, NULL upon return + */ +KS_DECLARE(void) ks_dht_destroy(ks_dht_t **dht); + +/** + * Enable or disable (default) autorouting support. + * When enabled, autorouting will allow sending to remote addresses on interfaces which are not yet bound. + * The address will be bound with the provided autoroute port when this occurs. + * @param dht pointer to the dht instance + * @param autoroute enable or disable autorouting + * @param port when enabling autorouting this port will be used to bind new addresses, may be 0 to use the default DHT port + */ +KS_DECLARE(void) ks_dht_autoroute(ks_dht_t *dht, ks_bool_t autoroute, ks_port_t port); + +/** + * Register a callback for a specific message type. + * Will overwrite any duplicate handlers. + * @param dht pointer to the dht instance + * @param value string of the type text under the 'y' key of a message + * @param callback the callback to be called when a message matches + */ +KS_DECLARE(void) ks_dht_register_type(ks_dht_t *dht, const char *value, ks_dht_message_callback_t callback); + +/** + * Register a callback for a specific message query. + * Will overwrite any duplicate handlers. + * @param dht pointer to the dht instance + * @param value string of the type text under the 'q' key of a message + * @param callback the callback to be called when a message matches + */ +KS_DECLARE(void) ks_dht_register_query(ks_dht_t *dht, const char *value, ks_dht_message_callback_t callback); + +/** + * Register a callback for a specific message error. + * Will overwrite any duplicate handlers. + * @param dht pointer to the dht instance + * @param value string of the errorcode under the first item of the 'e' key of a message + * @param callback the callback to be called when a message matches + */ +KS_DECLARE(void) ks_dht_register_error(ks_dht_t *dht, const char *value, ks_dht_message_callback_t callback); + +/** + * Bind a local address and port for receiving UDP datagrams. + * @param dht pointer to the dht instance + * @param addr pointer to the local address information + * @param endpoint dereferenced out pointer to the allocated endpoint, may be NULL to ignore endpoint output + * @return The ks_status_t result: KS_STATUS_SUCCESS, KS_STATUS_FAIL, ... + * @see ks_socket_option + * @see ks_addr_bind + * @see ks_dht_endpoint_alloc + * @see ks_dht_endpoint_init + * @see ks_hash_insert + * @see ks_dhtrt_initroute + * @see ks_dhtrt_create_node + */ +KS_DECLARE(ks_status_t) ks_dht_bind(ks_dht_t *dht, const ks_sockaddr_t *addr, ks_dht_endpoint_t **endpoint); + +/** + * Pulse the internals of dht. + * Handles receiving UDP datagrams, dispatching processing, handles expirations, throttled message sending, route table pulsing, etc. + * @param dht pointer to the dht instance + * @param timeout timeout value used when polling sockets for new UDP datagrams + */ +KS_DECLARE(void) ks_dht_pulse(ks_dht_t *dht, int32_t timeout); + + +KS_DECLARE(char *) ks_dht_hex(const uint8_t *data, char *buffer, ks_size_t len); +KS_DECLARE(uint8_t *) ks_dht_dehex(uint8_t *data, const char *buffer, ks_size_t len); + + +/** + * + */ +KS_DECLARE(ks_status_t) ks_dht_storageitem_target_immutable(const uint8_t *value, ks_size_t value_length, ks_dht_nodeid_t *target); + +/** + * + */ +KS_DECLARE(ks_status_t) ks_dht_storageitem_target_mutable(ks_dht_storageitem_pkey_t *pk, const uint8_t *salt, ks_size_t salt_length, ks_dht_nodeid_t *target); + +/** + * + */ +KS_DECLARE(ks_status_t) ks_dht_storageitem_signature_generate(ks_dht_storageitem_signature_t *sig, + ks_dht_storageitem_skey_t *sk, + const uint8_t *salt, + ks_size_t salt_length, + int64_t sequence, + const uint8_t *value, + ks_size_t value_length); + +/** + * + */ +KS_DECLARE(void) ks_dht_storageitem_reference(ks_dht_storageitem_t *item); + +/** + * + */ +KS_DECLARE(void) ks_dht_storageitem_dereference(ks_dht_storageitem_t *item); + +/** + * + */ +KS_DECLARE(void) ks_dht_storageitem_callback(ks_dht_storageitem_t *item, ks_dht_storageitem_callback_t callback); + +/** + * + */ +KS_DECLARE(void) ks_dht_storageitems_read_lock(ks_dht_t *dht); + +/** + * + */ +KS_DECLARE(void) ks_dht_storageitems_read_unlock(ks_dht_t *dht); + +/** + * + */ +KS_DECLARE(void) ks_dht_storageitems_write_lock(ks_dht_t *dht); + +/** + * + */ +KS_DECLARE(void) ks_dht_storageitems_write_unlock(ks_dht_t *dht); + +/** + * + */ +KS_DECLARE(ks_dht_storageitem_t *) ks_dht_storageitems_find(ks_dht_t *dht, ks_dht_nodeid_t *target); + +/** + * + */ +KS_DECLARE(ks_status_t) ks_dht_storageitems_insert(ks_dht_t *dht, ks_dht_storageitem_t *item); + +/** + * + */ +KS_DECLARE(void) ks_dht_ping(ks_dht_t *dht, const ks_sockaddr_t *raddr, ks_dht_job_callback_t callback, void *data); + +/** + * + */ +KS_DECLARE(void) ks_dht_findnode(ks_dht_t *dht, + const ks_sockaddr_t *raddr, + ks_dht_job_callback_t callback, + void *data, + ks_dht_nodeid_t *target); + +/** + * + */ +KS_DECLARE(void) ks_dht_get(ks_dht_t *dht, + const ks_sockaddr_t *raddr, + ks_dht_job_callback_t callback, + void *data, + ks_dht_nodeid_t *target, + const uint8_t *salt, + ks_size_t salt_length); + +/** + * + */ +KS_DECLARE(void) ks_dht_put(ks_dht_t *dht, + const ks_sockaddr_t *raddr, + ks_dht_job_callback_t callback, + void *data, + ks_dht_token_t *token, + int64_t cas, + ks_dht_storageitem_t *item); + +/** + * Create a network search of the closest nodes to a target. + * @param dht pointer to the dht instance + * @param family either AF_INET or AF_INET6 for the appropriate network to search + * @param target pointer to the nodeid for the target to be searched + * @param callback an optional callback to add to the search when it is finished + * @param search dereferenced out pointer to the allocated search, may be NULL to ignore search output + * @see ks_dht_search_create + * @see ks_hash_insert + * @see ks_dht_findnode + */ +KS_DECLARE(void) ks_dht_search(ks_dht_t *dht, + ks_dht_job_callback_t callback, + void *data, + ks_dhtrt_routetable_t *table, + ks_dht_nodeid_t *target); + +KS_DECLARE(void) ks_dht_publish(ks_dht_t *dht, + const ks_sockaddr_t *raddr, + ks_dht_job_callback_t callback, + void *data, + int64_t cas, + ks_dht_storageitem_t *item); + +KS_DECLARE(void) ks_dht_distribute(ks_dht_t *dht, + ks_dht_storageitem_callback_t callback, + void *data, + ks_dhtrt_routetable_t *table, + int64_t cas, + ks_dht_storageitem_t *item); + +/** + * route table methods + * + */ +KS_DECLARE(ks_status_t) ks_dhtrt_initroute(ks_dhtrt_routetable_t **tableP, + ks_dht_t *dht, + ks_pool_t *pool); +KS_DECLARE(void) ks_dhtrt_deinitroute(ks_dhtrt_routetable_t **table); + +KS_DECLARE(ks_status_t) ks_dhtrt_create_node(ks_dhtrt_routetable_t* table, + ks_dht_nodeid_t nodeid, + enum ks_dht_nodetype_t type, + char* ip, unsigned short port, + enum ks_create_node_flags_t flags, + ks_dht_node_t** node); + +KS_DECLARE(ks_status_t) ks_dhtrt_delete_node(ks_dhtrt_routetable_t* table, ks_dht_node_t* node); + +KS_DECLARE(ks_status_t) ks_dhtrt_touch_node(ks_dhtrt_routetable_t* table, ks_dht_nodeid_t nodeid); +KS_DECLARE(ks_status_t) ks_dhtrt_expire_node(ks_dhtrt_routetable_t* table, ks_dht_nodeid_t nodeid); + +KS_DECLARE(uint8_t) ks_dhtrt_findclosest_nodes(ks_dhtrt_routetable_t* table, ks_dhtrt_querynodes_t* query); +KS_DECLARE(ks_dht_node_t*) ks_dhtrt_find_node(ks_dhtrt_routetable_t* table, ks_dht_nodeid_t id); + +KS_DECLARE(ks_status_t) ks_dhtrt_sharelock_node(ks_dht_node_t* node); +KS_DECLARE(ks_status_t) ks_dhtrt_release_node(ks_dht_node_t* node); +KS_DECLARE(ks_status_t) ks_dhtrt_release_querynodes(ks_dhtrt_querynodes_t* query); + +KS_DECLARE(void) ks_dhtrt_process_table(ks_dhtrt_routetable_t* table); + +KS_DECLARE(uint32_t) ks_dhtrt_serialize(ks_dhtrt_routetable_t* table, void** ptr); +KS_DECLARE(ks_status_t) ks_dhtrt_deserialize(ks_dhtrt_routetable_t* table, void* ptr); + +/* debugging aids */ +KS_DECLARE(void) ks_dhtrt_dump(ks_dhtrt_routetable_t* table, int level); + + KS_END_EXTERN_C -#endif /* _KS_DHT_H */ +#endif /* KS_DHT_H */ /* For Emacs: * Local Variables: diff --git a/libs/libks/src/ks_dht.c b/libs/libks/src/ks_dht.c deleted file mode 100644 index 5715a4902c..0000000000 --- a/libs/libks/src/ks_dht.c +++ /dev/null @@ -1,4137 +0,0 @@ -/* -Copyright (c) 2009-2011 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -/* Please, please, please. - - You are welcome to integrate this code in your favourite Bittorrent - client. Please remember, however, that it is meant to be usable by - others, including myself. This means no C++, no relicensing, and no - gratuitious changes to the coding style. And please send back any - improvements to the author. */ - -/* Sorry dude, we hacked up this code pretty good but its not C++ and the license is pure BSD. -Like Meatloaf says, 2 out of 3 ain't bad! But we needed a good base for some additions we needed */ - - -#include "ks.h" -#include "sodium.h" - -#ifndef MSG_CONFIRM -#define MSG_CONFIRM 0 -#endif - -KS_DECLARE(int) dht_blacklisted(const ks_sockaddr_t *sa) -{ - return 0; -} - -KS_DECLARE(void) dht_hash(void *hash_return, int hash_size, const void *v1, int len1, const void *v2, int len2, const void *v3, int len3) -{ - crypto_generichash_state state; - - crypto_generichash_init(&state, NULL, 0, hash_size); - crypto_generichash_update(&state, v1, len1); - crypto_generichash_update(&state, v2, len2); - crypto_generichash_update(&state, v3, len3); - crypto_generichash_final(&state, (unsigned char *)hash_return, hash_size); - - return; -} - -/* -KS_DECLARE(int) dht_random_bytes(void *buf, size_t size) -{ -return 0; -} -*/ - -#ifdef _WIN32 - -#undef EAFNOSUPPORT -#define EAFNOSUPPORT WSAEAFNOSUPPORT - -static int random(void) -{ - return rand(); -} - -/* Windows Vista and later already provide the implementation. */ -#if _WIN32_WINNT < 0x0600 -extern const char *inet_ntop(int, const void *, char *, socklen_t); -#endif - -#else -#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)) - -struct node { - unsigned char id[20]; - ks_sockaddr_t ss; - time_t time; /* time of last message received */ - time_t reply_time; /* time of last correct reply received */ - time_t pinged_time; /* time of last request */ - int pinged; /* how many requests we sent since last reply */ - struct node *next; -}; - -struct bucket { - int af; - unsigned char first[20]; - int count; /* number of nodes */ - time_t time; /* time of last reply in this bucket */ - struct node *nodes; - ks_sockaddr_t cached; /* the address of a likely candidate */ - struct bucket *next; -}; - -struct search_node { - unsigned char id[20]; - ks_sockaddr_t ss; - time_t request_time; /* the time of the last unanswered request */ - time_t reply_time; /* the time of the last reply */ - int pinged; - unsigned char token[40]; - int token_len; - int replied; /* whether we have received a reply */ - int acked; /* whether they acked our announcement */ -}; - -/* When performing a search, we search for up to SEARCH_NODES closest nodes - to the destination, and use the additional ones to backtrack if any of - the target 8 turn out to be dead. */ -#define SEARCH_NODES 14 - -struct search { - unsigned short tid; - int af; - time_t step_time; /* the time of the last search_step */ - unsigned char id[20]; - unsigned short port; /* 0 for pure searches */ - int done; - struct search_node nodes[SEARCH_NODES]; - int numnodes; - struct search *next; -}; - -struct peer { - time_t time; - ks_sockaddr_t addr; -}; - -/* The maximum number of peers we store for a given hash. */ -#ifndef DHT_MAX_PEERS -#define DHT_MAX_PEERS 2048 -#endif - -/* The maximum number of hashes we're willing to track. */ -#ifndef DHT_MAX_HASHES -#define DHT_MAX_HASHES 16384 -#endif - -/* The maximum number of searches we keep data about. */ -#ifndef DHT_MAX_SEARCHES -#define DHT_MAX_SEARCHES 1024 -#endif - -/* The time after which we consider a search to be expirable. */ -#ifndef DHT_SEARCH_EXPIRE_TIME -#define DHT_SEARCH_EXPIRE_TIME (62 * 60) -#endif - -struct storage { - unsigned char id[20]; - int numpeers, maxpeers; - struct peer *peers; - struct storage *next; -}; - -static struct storage * find_storage(dht_handle_t *h, const unsigned char *id); -static void flush_search_node(struct search_node *n, struct search *sr); - -typedef enum { - DHT_MSG_INVALID = 0, - DHT_MSG_ERROR = 1, - DHT_MSG_REPLY = 2, - DHT_MSG_PING = 3, - DHT_MSG_FIND_NODE = 4, - DHT_MSG_GET_PEERS = 5, - DHT_MSG_ANNOUNCE_PEER = 6, - DHT_MSG_STORE_PUT = 7 -} dht_msg_type_t; - -#define WANT4 1 -#define WANT6 2 - -static dht_msg_type_t parse_message(struct bencode *bencode_p, - unsigned char *tid_return, int *tid_len, - unsigned char *id_return); - -static unsigned char *debug_printable(const unsigned char *buf, unsigned char *out, int buflen); -static void print_hex(FILE *f, const unsigned char *buf, int buflen); -static int is_martian(const ks_sockaddr_t *sa); -static int id_cmp(const unsigned char *restrict id1, const unsigned char *restrict id2); -static int lowbit(const unsigned char *id); -static int common_bits(const unsigned char *id1, const unsigned char *id2); -static int xorcmp(const unsigned char *id1, const unsigned char *id2, const unsigned char *ref); -static int in_bucket(const unsigned char *id, struct bucket *b); -static struct bucket *find_bucket(dht_handle_t *h, unsigned const char *id, int af); -static struct bucket *previous_bucket(dht_handle_t *h, struct bucket *b); -static struct node *find_node(dht_handle_t *h, const unsigned char *id, int af); -static struct node *random_node(struct bucket *b); -static int bucket_middle(struct bucket *b, unsigned char *id_return); -static int bucket_random(struct bucket *b, unsigned char *id_return); -static struct node *insert_node(dht_handle_t *h, struct node *node); -static int node_good(dht_handle_t *h, struct node *node); -static void 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); -static int send_cached_ping(dht_handle_t *h, struct bucket *b); -static void pinged(dht_handle_t *h, struct node *n, struct bucket *b); -static void blacklist_node(dht_handle_t *h, const unsigned char *id, const ks_sockaddr_t *sa); -static int node_blacklisted(dht_handle_t *h, const ks_sockaddr_t *sa); -static struct bucket *split_bucket(dht_handle_t *h, struct bucket *b); -static struct node *new_node(dht_handle_t *h, const unsigned char *id, const ks_sockaddr_t *sa, int confirm); -static int expire_buckets(dht_handle_t *h, struct bucket *b); -static struct search *find_search(dht_handle_t *h, unsigned short tid, int af); -static int insert_search_node(dht_handle_t *h, unsigned char *id, - const ks_sockaddr_t *sa, - struct search *sr, int replied, - unsigned char *token, int token_len); -static void flush_search_node(struct search_node *n, struct search *sr); -static void expire_searches(dht_handle_t *h); -static int search_send_get_peers(dht_handle_t *h, struct search *sr, struct search_node *n); -static void search_step(dht_handle_t *h, struct search *sr); -static struct search *new_search(dht_handle_t *h); -static void insert_search_bucket(dht_handle_t *h, struct bucket *b, struct search *sr); -static struct storage *find_storage(dht_handle_t *h, const unsigned char *id); -static int storage_store(dht_handle_t *h, const unsigned char *id, const ks_sockaddr_t *sa, unsigned short port); -static int expire_storage(dht_handle_t *h); -static int rotate_secrets(dht_handle_t *h); -static void make_token(dht_handle_t *h, const ks_sockaddr_t *sa, int old, unsigned char *token_return); -static int token_match(dht_handle_t *h, const unsigned char *token, int token_len, const ks_sockaddr_t *sa); -static void dump_bucket(dht_handle_t *h, FILE *f, struct bucket *b); - -static void reset_poll(dht_handle_t *h); -static void clear_all_ip(dht_handle_t *h); -static int token_bucket(dht_handle_t *h); -static int neighbourhood_maintenance(dht_handle_t *h, int af); -static int bucket_maintenance(dht_handle_t *h, int af); -static int dht_send(dht_handle_t *h, const void *buf, size_t len, int flags, const ks_sockaddr_t *sa); - -static int send_ping(dht_handle_t *h, const ks_sockaddr_t *sa, const unsigned char *tid, int tid_len); -static int send_pong(dht_handle_t *h, const ks_sockaddr_t *sa, const unsigned char *tid, int tid_len); -static int send_find_node(dht_handle_t *h, const ks_sockaddr_t *sa, const unsigned char *tid, int tid_len, const unsigned char *target, int target_len, - int want, int confirm); -static int send_nodes_peers(dht_handle_t *h, const ks_sockaddr_t *sa, const unsigned char *tid, int tid_len, const unsigned char *nodes, int nodes_len, - const unsigned char *nodes6, int nodes6_len, int af, struct storage *st, const unsigned char *token, int token_len); -static int insert_closest_node(unsigned char *nodes, int numnodes, const unsigned char *id, struct node *n); -static int buffer_closest_nodes(dht_handle_t *h, unsigned char *nodes, int numnodes, const unsigned char *id, struct bucket *b); -static int send_closest_nodes(dht_handle_t *h, const ks_sockaddr_t *sa, const unsigned char *tid, int tid_len, const unsigned char *id, int want, - int af, struct storage *st, const unsigned char *token, int token_len); -static int send_get_peers(dht_handle_t *h, const ks_sockaddr_t *sa, unsigned char *tid, int tid_len, unsigned char *infohash, int want, int confirm); -static int send_announce_peer(dht_handle_t *h, const ks_sockaddr_t *sa, unsigned char *tid, int tid_len, unsigned char *infohas, unsigned short port, - unsigned char *token, int token_len, int confirm); -static int send_peer_announced(dht_handle_t *h, const ks_sockaddr_t *sa, unsigned char *tid, int tid_len); -static int send_error(dht_handle_t *h, const ks_sockaddr_t *sa, unsigned char *tid, int tid_len, int code, const char *message); - -static dht_msg_type_t parse_message(struct bencode *bencode_p, unsigned char *tid_return, int *tid_len, unsigned char *id_return); -static int b64encode(unsigned char *in, ks_size_t ilen, unsigned char *out, ks_size_t olen); - - -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 }; - -#define MAX_TOKEN_BUCKET_TOKENS 400 - -/* The maximum number of nodes that we snub. There is probably little - reason to increase this value. */ -#ifndef DHT_MAX_BLACKLISTED -#define DHT_MAX_BLACKLISTED 10 -#endif - -struct ks_dht_store_entry_s { - const char *key; - - ks_time_t received; /* recieved timestamp */ - ks_time_t last_announce; - ks_time_t expiration; /* When should 'my' message be automatically expired. If not set will be expired after 10 minutes */ - - /* Top level struct pointers. Will need to be freed */ - struct bencode *bencode_message_raw; - struct bencode *payload_bencode; - cJSON *body; - - /* Short cut accessor pointers. Do not free these. */ - const char *content_type; - const char *payload_raw; - - unsigned int serial; - ks_bool_t mine; - ks_pool_t *pool; -}; - -struct ks_dht_store_s { - ks_time_t next_expiring; - ks_hash_t *hash; - ks_pool_t *pool; -}; - -typedef struct { - char ip[80]; - ks_sockaddr_t addr; - ks_socket_t sock; -} ks_ip_t; - -struct dht_handle_s { - ks_pool_t *pool; - - struct pollfd *pollsocks; - ks_ip_t **iptsocks; - ks_sockaddr_t **addrsocks; - uint32_t num_pollsocks; - - //int dht_socket; - //int dht_socket6; - - time_t search_time; - time_t confirm_nodes_time; - time_t rotate_secrets_time; - - unsigned char myid[20]; - int have_v; - unsigned char my_v[9]; - unsigned char secret[8]; - unsigned char oldsecret[8]; - unsigned int port; - - struct bucket *buckets; - struct bucket *buckets6; - struct storage *storage; - int numstorage; - - struct search *searches; - int numsearches; - unsigned short search_id; - - ks_sockaddr_t blacklist[DHT_MAX_BLACKLISTED]; - int next_blacklisted; - - ks_time_t now; - time_t mybucket_grow_time, mybucket6_grow_time; - time_t expire_stuff_time; - - time_t token_bucket_time; - int token_bucket_tokens; - - ks_dht_store_entry_json_cb *store_json_cb; - void *store_json_cb_arg; - - ks_hash_t *iphash; - - struct ks_dht_store_s *store; - - dht_callback_t callback; - void *closure; - - uint32_t ip4s; - uint32_t ip6s; - - int af_flags; - - int tosleep; - - int autoroute; - - int started; -}; - -static ks_ip_t *add_ip(dht_handle_t *h, const char *ip, int port, int family); - -static void ks_dht_store_entry_destroy(struct ks_dht_store_entry_s **old_entry); -static int ks_dht_store_entry_create(struct dht_handle_s *h, struct bencode *msg, struct ks_dht_store_entry_s **new_entry, ks_time_t life, ks_bool_t mine); -static struct ks_dht_store_entry_s *ks_dht_store_fetch(struct ks_dht_store_s *store, char *key); -static int ks_dht_store_insert(struct ks_dht_store_s *store, struct ks_dht_store_entry_s *entry, ks_time_t now); -static int ks_dht_store_replace(struct ks_dht_store_s *store, struct ks_dht_store_entry_s *entry); -static void ks_dht_store_prune(struct ks_dht_store_s *store, ks_time_t now); -static int ks_dht_store_create(ks_pool_t *pool, struct ks_dht_store_s **new_store); -static void ks_dht_store_destroy(struct ks_dht_store_s **old_store); - - -KS_DECLARE(void) ks_dht_store_entry_json_cb_set(struct dht_handle_s *h, ks_dht_store_entry_json_cb *store_json_cb, void *arg) -{ - h->store_json_cb = store_json_cb; - h->store_json_cb_arg = arg; -} - -static unsigned char *debug_printable(const unsigned char *buf, unsigned char *out, int buflen) -{ - int i; - for (i = 0; i < buflen; i++) { - out[i] = (buf[i] >= 32 && buf[i] <= 126) ? buf[i] : '.'; - } - return out; -} - -static void print_hex(FILE *f, const unsigned char *buf, int buflen) -{ - int i; - for (i = 0; i < buflen; i++) { - fprintf(f, "%02x", buf[i]); - } -} - -static int is_martian(const ks_sockaddr_t *sa) -{ - switch(sa->family) { - case AF_INET: { - return (sa->port == 0); - } - case AF_INET6: { - return (sa->port == 0); - } - - default: - return 1; - } -} - -/* Forget about the ``XOR-metric''. An id is just a path from the - root of the tree, so bits are numbered from the start. */ - -static int id_cmp(const unsigned char *restrict id1, const unsigned char *restrict id2) -{ - /* Memcmp is guaranteed to perform an unsigned comparison. */ - return memcmp(id1, id2, 20); -} - -/* Find the lowest 1 bit in an id. */ -static int lowbit(const unsigned char *id) -{ - int i, j; - - for (i = 19; i >= 0; i--) { - if (id[i] != 0) { - break; - } - } - - if (i < 0) return -1; - - for (j = 7; j >= 0; j--) { - if ((id[i] & (0x80 >> j)) != 0) { - break; - } - } - - return 8 * i + j; -} - -/* Find how many bits two ids have in common. */ -static int common_bits(const unsigned char *id1, const unsigned char *id2) -{ - int i, j; - unsigned char xor; - for (i = 0; i < 20; i++) { - if (id1[i] != id2[i]) { - break; - } - } - - if (i == 20) { - return 160; - } - - xor = id1[i] ^ id2[i]; - - j = 0; - while ((xor & 0x80) == 0) { - xor <<= 1; - j++; - } - - return 8 * i + j; -} - -/* Determine whether id1 or id2 is closer to ref */ -static int xorcmp(const unsigned char *id1, const unsigned char *id2, const unsigned char *ref) -{ - int i; - for (i = 0; i < 20; i++) { - unsigned char xor1, xor2; - if (id1[i] == id2[i]) { - continue; - } - xor1 = id1[i] ^ ref[i]; - xor2 = id2[i] ^ ref[i]; - if (xor1 < xor2) { - return -1; - } - return 1; - } - return 0; -} - -/* We keep buckets in a sorted linked list. A bucket b ranges from - b->first inclusive up to b->next->first exclusive. */ -static int in_bucket(const unsigned char *id, struct bucket *b) -{ - return id_cmp(b->first, id) <= 0 && (b->next == NULL || id_cmp(id, b->next->first) < 0); -} - -static struct bucket *find_bucket(dht_handle_t *h, unsigned const char *id, int af) -{ - struct bucket *b = af == AF_INET ? h->buckets : h->buckets6; - - if (b == NULL) { - return NULL; - } - - while (1) { - if (b->next == NULL) { - return b; - } - - if (id_cmp(id, b->next->first) < 0) { - return b; - } - - b = b->next; - } -} - -static struct bucket *previous_bucket(dht_handle_t *h, struct bucket *b) -{ - struct bucket *p = b->af == AF_INET ? h->buckets : h->buckets6; - - if (b == p) { - return NULL; - } - - while (1) { - if (p->next == NULL) { - return NULL; - } - - if (p->next == b) { - return p; - } - - p = p->next; - } -} - -/* Every bucket contains an unordered list of nodes. */ -static struct node *find_node(dht_handle_t *h, const unsigned char *id, int af) -{ - struct bucket *b = find_bucket(h, id, af); - struct node *n; - - if (b == NULL) - return NULL; - - n = b->nodes; - while (n) { - if (id_cmp(n->id, id) == 0) { - return n; - } - n = n->next; - } - return NULL; -} - -/* Return a random node in a bucket. */ -static struct node *random_node(struct bucket *b) -{ - struct node *n; - int nn; - - if (b->count == 0) { - return NULL; - } - - nn = random() % b->count; - n = b->nodes; - while (nn > 0 && n) { - n = n->next; - nn--; - } - return n; -} - -/* Return the middle id of a bucket. */ -static int bucket_middle(struct bucket *b, unsigned char *id_return) -{ - int bit1 = lowbit(b->first); - int bit2 = b->next ? lowbit(b->next->first) : -1; - int bit = MAX(bit1, bit2) + 1; - - if (bit >= 160) { - return -1; - } - - memcpy(id_return, b->first, 20); - id_return[bit / 8] |= (0x80 >> (bit % 8)); - return 1; -} - -/* Return a random id within a bucket. */ -static int bucket_random(struct bucket *b, unsigned char *id_return) -{ - int bit1 = lowbit(b->first); - int bit2 = b->next ? lowbit(b->next->first) : -1; - int bit = MAX(bit1, bit2) + 1; - int i; - - if (bit >= 160) { - memcpy(id_return, b->first, 20); - return 1; - } - - memcpy(id_return, b->first, bit / 8); - id_return[bit / 8] = b->first[bit / 8] & (0xFF00 >> (bit % 8)); - id_return[bit / 8] |= random() & 0xFF >> (bit % 8); - for (i = bit / 8 + 1; i < 20; i++) { - id_return[i] = random() & 0xFF; - } - return 1; -} - -/* Insert a new node into a bucket. */ -static struct node *insert_node(dht_handle_t *h, struct node *node) -{ - struct bucket *b = find_bucket(h, node->id, node->ss.family); - - if (b == NULL) { - return NULL; - } - - node->next = b->nodes; - b->nodes = node; - b->count++; - return node; -} - -/* This is our definition of a known-good node. */ -static int node_good(dht_handle_t *h, struct node *node) -{ - return node->pinged <= 2 && node->reply_time >= h->now - 7200 && node->time >= h->now - 900; -} - -/* Our transaction-ids are 4-bytes long, with the first two bytes identi- - fying the kind of request, and the remaining two a sequence number in - host order. */ - -static void make_tid(unsigned char *tid_return, const char *prefix, unsigned short seqno) -{ - tid_return[0] = prefix[0] & 0xFF; - tid_return[1] = prefix[1] & 0xFF; - memcpy(tid_return + 2, &seqno, 2); -} - -static int 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) { - memcpy(seqno_return, tid + 2, 2); - } - return 1; - } - - return 0; -} - -/* Every bucket caches the address of a likely node. Ping it. */ -static int send_cached_ping(dht_handle_t *h, struct bucket *b) -{ - unsigned char tid[4]; - int rc; - /* We set family to 0 when there's no cached node. */ - if (b->cached.family == 0) { - return 0; - } - - ks_log(KS_LOG_DEBUG, "Sending ping to cached node.\n"); - make_tid(tid, "pn", 0); - rc = send_ping(h, &b->cached, tid, 4); - b->cached.family = 0; - return rc; -} - -/* Called whenever we send a request to a node, increases the ping count - and, if that reaches 3, sends a ping to a new candidate. */ -static void pinged(dht_handle_t *h, struct node *n, struct bucket *b) -{ - n->pinged++; - n->pinged_time = h->now; - if (n->pinged >= 3) { - send_cached_ping(h, b ? b : find_bucket(h, n->id, n->ss.family)); - } -} - -/* The internal blacklist is an LRU cache of nodes that have sent - incorrect messages. */ -static void blacklist_node(dht_handle_t *h, const unsigned char *id, const ks_sockaddr_t *sa) -{ - int i; - - ks_log(KS_LOG_DEBUG, "Blacklisting broken node.\n"); - - if (id) { - struct node *n; - struct search *sr; - /* Make the node easy to discard. */ - n = find_node(h, id, sa->family); - if (n) { - n->pinged = 3; - pinged(h, n, NULL); - } - /* Discard it from any searches in progress. */ - sr = h->searches; - while (sr) { - for (i = 0; i < sr->numnodes; i++) { - if (id_cmp(sr->nodes[i].id, id) == 0) { - flush_search_node(&sr->nodes[i], sr); - } - } - sr = sr->next; - } - } - /* And make sure we don't hear from it again. */ - ks_addr_copy(&h->blacklist[h->next_blacklisted], sa); - h->next_blacklisted = (h->next_blacklisted + 1) % DHT_MAX_BLACKLISTED; -} - -static int node_blacklisted(dht_handle_t *h, const ks_sockaddr_t *sa) -{ - int i; - - if (dht_blacklisted(sa)) { - return 1; - } - - for(i = 0; i < DHT_MAX_BLACKLISTED; i++) { - if (ks_addr_cmp(&h->blacklist[i], sa)) { - return 1; - } - } - - return 0; -} - -/* Split a bucket into two equal parts. */ -static struct bucket *split_bucket(dht_handle_t *h, struct bucket *b) -{ - struct bucket *new; - struct node *nodes; - int rc; - unsigned char new_id[20]; - - if ((rc = bucket_middle(b, new_id)) < 0) { - return NULL; - } - - new = ks_pool_alloc(h->pool, sizeof(struct bucket)); - - new->af = b->af; - - send_cached_ping(h, b); - - memcpy(new->first, new_id, 20); - new->time = b->time; - - nodes = b->nodes; - b->nodes = NULL; - b->count = 0; - new->next = b->next; - b->next = new; - while (nodes) { - struct node *n; - n = nodes; - nodes = nodes->next; - insert_node(h, n); - } - return b; -} - -/* 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 struct node *new_node(dht_handle_t *h, const unsigned char *id, const ks_sockaddr_t *sa, int confirm) -{ - struct bucket *b = find_bucket(h, id, sa->family); - struct node *n; - int mybucket, split; - - if (b == NULL) { - return NULL; - } - - if (id_cmp(id, h->myid) == 0) { - return NULL; - } - - if (is_martian(sa) || node_blacklisted(h, sa)) { - return NULL; - } - - mybucket = in_bucket(h->myid, b); - - if (confirm == 2) { - b->time = h->now; - } - - n = b->nodes; - while (n) { - if (id_cmp(n->id, id) == 0) { - if (confirm || n->time < h->now - 15 * 60) { - /* Known node. Update stuff. */ - ks_addr_copy(&n->ss, sa); - - if (confirm) { - n->time = h->now; - } - if (confirm >= 2) { - n->reply_time = h->now; - n->pinged = 0; - n->pinged_time = 0; - } - } - return n; - } - n = n->next; - } - - /* New node. */ - - if (mybucket) { - if (sa->family == AF_INET) { - h->mybucket_grow_time = h->now; - } else { - h->mybucket6_grow_time = h->now; - } - } - - /* First, try to get rid of a known-bad node. */ - n = b->nodes; - while (n) { - if (n->pinged >= 3 && n->pinged_time < h->now - 15) { - memcpy(n->id, id, 20); - ks_addr_copy(&n->ss, sa); - n->time = confirm ? h->now : 0; - n->reply_time = confirm >= 2 ? h->now : 0; - n->pinged_time = 0; - n->pinged = 0; - return n; - } - n = n->next; - } - - if (b->count >= 8) { - /* Bucket full. Ping a dubious node */ - int dubious = 0; - n = b->nodes; - while (n) { - /* Pick the first dubious node that we haven't pinged in the - last 15 seconds. This gives nodes the time to reply, but - tends to concentrate on the same nodes, so that we get rid - of bad nodes fast. */ - if (!node_good(h, n)) { - dubious = 1; - if (n->pinged_time < h->now - 15) { - unsigned char tid[4]; - ks_log(KS_LOG_DEBUG, "Sending ping to dubious node.\n"); - make_tid(tid, "pn", 0); - send_ping(h, &n->ss, tid, 4); - n->pinged++; - n->pinged_time = h->now; - break; - } - } - n = n->next; - } - - split = 0; - if (mybucket) { - if (!dubious) { - split = 1; - } - /* If there's only one bucket, split eagerly. This is - incorrect unless there's more than 8 nodes in the DHT. */ - else if (b->af == AF_INET && h->buckets->next == NULL) { - split = 1; - } else if (b->af == AF_INET6 && h->buckets6->next == NULL) { - split = 1; - } - } - - if (split) { - ks_log(KS_LOG_DEBUG, "Splitting.\n"); - b = split_bucket(h, b); - return new_node(h, id, sa, confirm); - } - - /* No space for this node. Cache it away for later. */ - if (confirm || b->cached.family == 0) { - ks_addr_copy(&b->cached, sa); - } - - return NULL; - } - - /* Create a new node. */ - n = ks_pool_alloc(h->pool, sizeof(struct node)); - - memcpy(n->id, id, 20); - ks_addr_copy(&n->ss, sa); - n->time = confirm ? h->now : 0; - n->reply_time = confirm >= 2 ? h->now : 0; - n->next = b->nodes; - b->nodes = n; - b->count++; - return n; -} - -/* Called periodically to purge known-bad nodes. Note that we're very - conservative here: broken nodes in the table don't do much harm, we'll - recover as soon as we find better ones. */ -static int expire_buckets(dht_handle_t *h, struct bucket *b) -{ - while (b) { - struct node *n, *p; - int changed = 0; - - while (b->nodes && b->nodes->pinged >= 4) { - n = b->nodes; - b->nodes = n->next; - b->count--; - changed = 1; - ks_pool_free(h->pool, &n); - } - - p = b->nodes; - while (p) { - while (p->next && p->next->pinged >= 4) { - n = p->next; - p->next = n->next; - b->count--; - changed = 1; - ks_pool_free(h->pool, &n); - } - p = p->next; - } - - if (changed) { - send_cached_ping(h, b); - } - - b = b->next; - } - h->expire_stuff_time = h->now + 120 + random() % 240; - return 1; -} - -/* While a search is in progress, we don't necessarily keep the nodes being - walked in the main bucket table. A search in progress is identified by - a unique transaction id, a short (and hence small enough to fit in the - transaction id of the protocol packets). */ - -static struct search *find_search(dht_handle_t *h, unsigned short tid, int af) -{ - struct search *sr = h->searches; - while (sr) { - if (sr->tid == tid && sr->af == af) { - return sr; - } - sr = sr->next; - } - return NULL; -} - -/* A search contains a list of nodes, sorted by decreasing distance to the - target. We just got a new candidate, insert it at the right spot or - discard it. */ - -static int insert_search_node(dht_handle_t *h, unsigned char *id, - const ks_sockaddr_t *sa, - struct search *sr, int replied, - unsigned char *token, int token_len) -{ - struct search_node *n; - int i, j; - - if (sa->family != sr->af) { - ks_log(KS_LOG_DEBUG, "Attempted to insert node in the wrong family.\n"); - return 0; - } - - for(i = 0; i < sr->numnodes; i++) { - if (id_cmp(id, sr->nodes[i].id) == 0) { - n = &sr->nodes[i]; - goto found; - } - if (xorcmp(id, sr->nodes[i].id, sr->id) < 0) { - break; - } - } - - if (i == SEARCH_NODES) { - return 0; - } - - if (sr->numnodes < SEARCH_NODES) { - sr->numnodes++; - } - - for (j = sr->numnodes - 1; j > i; j--) { - sr->nodes[j] = sr->nodes[j - 1]; - } - - n = &sr->nodes[i]; - - memset(n, 0, sizeof(struct search_node)); - memcpy(n->id, id, 20); - -found: - - ks_addr_copy(&n->ss, sa); - - if (replied) { - n->replied = 1; - n->reply_time = h->now; - n->request_time = 0; - n->pinged = 0; - } - if (token) { - if (token_len >= 40) { - ks_log(KS_LOG_DEBUG, "Eek! Overlong token.\n"); - assert(0); - } else { - memcpy(n->token, token, token_len); - n->token_len = token_len; - } - } - - return 1; -} - -static void flush_search_node(struct search_node *n, struct search *sr) -{ - int i = n - sr->nodes, j; - for (j = i; j < sr->numnodes - 1; j++) { - sr->nodes[j] = sr->nodes[j + 1]; - } - sr->numnodes--; -} - -static void expire_searches(dht_handle_t *h) -{ - struct search *sr = h->searches, *previous = NULL; - - while (sr) { - struct search *next = sr->next; - if (sr->step_time < h->now - DHT_SEARCH_EXPIRE_TIME) { - if (previous) { - previous->next = next; - } else { - h->searches = next; - } - ks_pool_free(h->pool, &sr); - h->numsearches--; - } else { - previous = sr; - } - sr = next; - } -} - -/* This must always return 0 or 1, never -1, not even on failure (see below). */ -static int search_send_get_peers(dht_handle_t *h, struct search *sr, struct search_node *n) -{ - struct node *node; - unsigned char tid[4]; - - if (n == NULL) { - int i; - for (i = 0; i < sr->numnodes; i++) { - if (sr->nodes[i].pinged < 3 && !sr->nodes[i].replied && sr->nodes[i].request_time < h->now - 15) { - n = &sr->nodes[i]; - } - } - } - - if (!n || n->pinged >= 3 || n->replied || n->request_time >= h->now - 15) { - return 0; - } - - ks_log(KS_LOG_DEBUG, "Sending get_peers.\n"); - make_tid(tid, "gp", sr->tid); - send_get_peers(h, &n->ss, tid, 4, sr->id, -1, n->reply_time >= h->now - 15); - n->pinged++; - n->request_time = h->now; - /* If the node happens to be in our main routing table, mark it as pinged. */ - if ((node = find_node(h, n->id, n->ss.family))) { - pinged(h, node, NULL); - } - return 1; -} - -/* When a search is in progress, we periodically call search_step to send - further requests. */ -static void search_step(dht_handle_t *h, struct search *sr) -{ - int i, j; - int all_done = 1; - - /* Check if the first 8 live nodes have replied. */ - j = 0; - for (i = 0; i < sr->numnodes && j < 8; i++) { - struct search_node *n = &sr->nodes[i]; - if (n->pinged >= 3) { - continue; - } - if (!n->replied) { - all_done = 0; - break; - } - j++; - } - - if (all_done) { - int all_acked = 1; - if (sr->port == 0) { - goto done; - } - - j = 0; - - for (i = 0; i < sr->numnodes && j < 8; i++) { - struct search_node *n = &sr->nodes[i]; - struct node *node; - unsigned char tid[4]; - if (n->pinged >= 3) { - continue; - } - /* A proposed extension to the protocol consists in omitting the token when storage tables are full. While - I don't think this makes a lot of sense -- just sending a positive reply is just as good --, let's deal with it. */ - if (n->token_len == 0) { - n->acked = 1; - } - if (!n->acked) { - all_acked = 0; - ks_log(KS_LOG_DEBUG, "Sending announce_peer.\n"); - make_tid(tid, "ap", sr->tid); - send_announce_peer(h, &n->ss, - tid, 4, sr->id, sr->port, - n->token, n->token_len, - n->reply_time < h->now - 15); - n->pinged++; - n->request_time = h->now; - node = find_node(h, n->id, n->ss.family); - if (node) pinged(h, node, NULL); - } - j++; - } - if (all_acked) { - goto done; - } - - sr->step_time = h->now; - return; - } - - if (sr->step_time + 15 >= h->now) { - return; - } - - j = 0; - for (i = 0; i < sr->numnodes; i++) { - j += search_send_get_peers(h, sr, &sr->nodes[i]); - if (j >= 3) { - break; - } - } - sr->step_time = h->now; - return; - - done: - sr->done = 1; - if (h->callback) { - h->callback(h->closure, sr->af == AF_INET ? KS_DHT_EVENT_SEARCH_DONE : KS_DHT_EVENT_SEARCH_DONE6, sr->id, NULL, 0); - } - sr->step_time = h->now; -} - -static struct search *new_search(dht_handle_t *h) -{ - struct search *sr, *oldest = NULL; - - /* Find the oldest done search */ - sr = h->searches; - while (sr) { - if (sr->done && (oldest == NULL || oldest->step_time > sr->step_time)) { - oldest = sr; - } - sr = sr->next; - } - - /* The oldest slot is expired. */ - if (oldest && oldest->step_time < h->now - DHT_SEARCH_EXPIRE_TIME) { - return oldest; - } - - /* Allocate a new slot. */ - if (h->numsearches < DHT_MAX_SEARCHES) { - sr = ks_pool_alloc(h->pool, sizeof(struct search)); - sr->next = h->searches; - h->searches = sr; - h->numsearches++; - return sr; - } - - /* Oh, well, never mind. Reuse the oldest slot. */ - return oldest; -} - -/* Insert the contents of a bucket into a search structure. */ -static void insert_search_bucket(dht_handle_t *h, struct bucket *b, struct search *sr) -{ - struct node *n; - n = b->nodes; - while (n) { - insert_search_node(h, n->id, &n->ss, sr, 0, NULL, 0); - n = n->next; - } -} - -/* Start a search. If port is non-zero, perform an announce when the - search is complete. */ -KS_DECLARE(int) dht_search(dht_handle_t *h, const unsigned char *id, int port, int af, dht_callback_t callback, void *closure) -{ - struct search *sr; - struct storage *st; - struct bucket *b = find_bucket(h, id, af); - - if (b == NULL) { - errno = EAFNOSUPPORT; - return -1; - } - - if (!callback) callback = h->callback; - if (!closure) closure = h->closure; - - /* Try to answer this search locally. In a fully grown DHT this - is very unlikely, but people are running modified versions of - this code in private DHTs with very few nodes. What's wrong - with flooding? */ - if (callback) { - st = find_storage(h, id); - if (st) { - int i; - - ks_log(KS_LOG_DEBUG, "Found local data (%d peers).\n", st->numpeers); - - for (i = 0; i < st->numpeers; i++) { - (*callback)(closure, st->peers[i].addr.family == AF_INET ? KS_DHT_EVENT_VALUES : KS_DHT_EVENT_VALUES6, id, (void *)&st->peers[i].addr, sizeof(st->peers[i].addr)); - } - } - } - - sr = h->searches; - while (sr) { - if (sr->af == af && id_cmp(sr->id, id) == 0) { - break; - } - sr = sr->next; - } - - if (sr) { - /* We're reusing data from an old search. Reusing the same tid - means that we can merge replies for both searches. */ - int i; - sr->done = 0; - again: - for (i = 0; i < sr->numnodes; i++) { - struct search_node *n; - n = &sr->nodes[i]; - /* Discard any doubtful nodes. */ - if (n->pinged >= 3 || n->reply_time < h->now - 7200) { - flush_search_node(n, sr); - goto again; - } - n->pinged = 0; - n->token_len = 0; - n->replied = 0; - n->acked = 0; - } - } else { - sr = new_search(h); - if (sr == NULL) { - errno = ENOSPC; - return -1; - } - sr->af = af; - sr->tid = h->search_id++; - sr->step_time = 0; - memcpy(sr->id, id, 20); - sr->done = 0; - sr->numnodes = 0; - } - - sr->port = port; - - insert_search_bucket(h, b, sr); - - if (sr->numnodes < SEARCH_NODES) { - struct bucket *p = previous_bucket(h, b); - if (b->next) { - insert_search_bucket(h, b->next, sr); - } - if (p) { - insert_search_bucket(h, p, sr); - } - } - if (sr->numnodes < SEARCH_NODES) { - insert_search_bucket(h, find_bucket(h, h->myid, af), sr); - } - - search_step(h, sr); - h->search_time = h->now; - return 1; -} - -/* A struct storage stores all the stored peer addresses for a given info hash. */ -static struct storage *find_storage(dht_handle_t *h, const unsigned char *id) -{ - struct storage *st = h->storage; - - while(st) { - if (id_cmp(id, st->id) == 0) { - break; - } - st = st->next; - } - return st; -} - -static int storage_store(dht_handle_t *h, const unsigned char *id, const ks_sockaddr_t *sa, unsigned short port) -{ - int i; - struct storage *st; - - st = find_storage(h, id); - - if (st == NULL) { - if (h->numstorage >= DHT_MAX_HASHES) { - return -1; - } - - st = ks_pool_alloc(h->pool, sizeof(struct storage)); - memcpy(st->id, id, 20); - st->next = h->storage; - h->storage = st; - h->numstorage++; - } - - for(i = 0; i < st->numpeers; i++) { - if (ks_addr_cmp(&st->peers[i].addr, sa)) { - break; - } - } - - if (i < st->numpeers) { - /* Already there, only need to refresh */ - st->peers[i].time = h->now; - return 0; - } else { - struct peer *p; - if (i >= st->maxpeers) { - /* Need to expand the array. */ - struct peer *new_peers; - int n; - if (st->maxpeers >= DHT_MAX_PEERS) { - return 0; - } - n = st->maxpeers == 0 ? 2 : 2 * st->maxpeers; - n = MIN(n, DHT_MAX_PEERS); - - if (!(new_peers = realloc(st->peers, n * sizeof(struct peer)))) { - return -1; - } - st->peers = new_peers; - st->maxpeers = n; - } - p = &st->peers[st->numpeers++]; - p->time = h->now; - ks_addr_copy(&p->addr, sa); - return 1; - } -} - -static int expire_storage(dht_handle_t *h) -{ - struct storage *st = h->storage, *previous = NULL; - - while (st) { - int i = 0; - while (i < st->numpeers) { - if (st->peers[i].time < h->now - 32 * 60) { - if (i != st->numpeers - 1) - st->peers[i] = st->peers[st->numpeers - 1]; - st->numpeers--; - } else { - i++; - } - } - - if (st->numpeers == 0) { - free(st->peers); - if (previous) { - previous->next = st->next; - ks_pool_free(h->pool, &st); - st = previous->next; - } else { - h->storage = st->next; - ks_pool_free(h->pool, &st); - st = h->storage; - } - - h->numstorage--; - if (h->numstorage < 0) { - ks_log(KS_LOG_DEBUG, "Eek... numstorage became negative.\n"); - h->numstorage = 0; - } - } else { - previous = st; - st = st->next; - } - } - return 1; -} - -static int rotate_secrets(dht_handle_t *h) -{ - h->rotate_secrets_time = h->now + 900 + random() % 1800; - - memcpy(h->oldsecret, h->secret, sizeof(h->secret)); - randombytes_buf(h->secret, sizeof(h->secret)); - - return 1; -} - -#ifndef TOKEN_SIZE -#define TOKEN_SIZE 8 -#endif - -static void make_token(dht_handle_t *h, const ks_sockaddr_t *sa, int old, unsigned char *token_return) -{ - void *ip; - ks_size_t iplen; - unsigned short port; - - ks_addr_raw_data(sa, &ip, &iplen); - port = htons(sa->port); - - dht_hash(token_return, TOKEN_SIZE, old ? h->oldsecret : h->secret, sizeof(h->secret), ip, iplen, (unsigned char*)&port, 2); -} - -static int token_match(dht_handle_t *h, const unsigned char *token, int token_len, const ks_sockaddr_t *sa) -{ - unsigned char t[TOKEN_SIZE]; - - if (token_len != TOKEN_SIZE) { - return 0; - } - - make_token(h, sa, 0, t); - if (memcmp(t, token, TOKEN_SIZE) == 0) { - return 1; - } - - make_token(h, sa, 1, t); - if (memcmp(t, token, TOKEN_SIZE) == 0) { - return 1; - } - - return 0; -} - -KS_DECLARE(int) dht_nodes(dht_handle_t *h, int af, int *good_return, int *dubious_return, int *cached_return, int *incoming_return) -{ - int good = 0, dubious = 0, cached = 0, incoming = 0; - struct bucket *b = af == AF_INET ? h->buckets : h->buckets6; - - while (b) { - struct node *n = b->nodes; - while (n) { - if (node_good(h, n)) { - good++; - if (n->time > n->reply_time) { - incoming++; - } - } else { - dubious++; - } - n = n->next; - } - if (b->cached.family > 0) { - cached++; - } - b = b->next; - } - - if (good_return) { - *good_return = good; - } - - if (dubious_return) { - *dubious_return = dubious; - } - - if (cached_return) { - *cached_return = cached; - } - - if (incoming_return) { - *incoming_return = incoming; - } - - return good + dubious; -} - -static void dump_bucket(dht_handle_t *h, FILE *f, struct bucket *b) -{ - struct node *n = b->nodes; - int mine = in_bucket(h->myid, b); - int age = (int)(h->now - b->time); - int cached = b->cached.family; - fprintf(f, "Bucket "); - print_hex(f, b->first, 20); - fprintf(f, " count %d age %d%s%s:\n", b->count, age, mine ? " (mine)" : "", cached ? " (cached)" : ""); - - while (n) { - fprintf(f, " Node "); - print_hex(f, n->id, 20); - - if (n->ss.family == AF_INET6) { - fprintf(f, " [%s]:%d ", n->ss.host, n->ss.port); - } else { - fprintf(f, " %s:%d ", n->ss.host, n->ss.port); - } - - if (n->time != n->reply_time) { - fprintf(f, "age %ld, %ld", (long)(h->now - n->time), (long)(h->now - n->reply_time)); - } else { - fprintf(f, "age %ld", (long)(h->now - n->time)); - } - - if (n->pinged) { - fprintf(f, " (%d)", n->pinged); - } - - if (node_good(h, n)) { - fprintf(f, " (good)"); - } - fprintf(f, "\n"); - n = n->next; - } - -} - -KS_DECLARE(void) dht_dump_tables(dht_handle_t *h, FILE *f) -{ - int i; - struct bucket *b; - struct storage *st = h->storage; - struct search *sr = h->searches; - - fprintf(f, "My id "); - print_hex(f, h->myid, 20); - fprintf(f, "\n"); - - b = h->buckets; - while (b) { - dump_bucket(h, f, b); - b = b->next; - } - - fprintf(f, "\n"); - - b = h->buckets6; - while (b) { - dump_bucket(h, f, b); - b = b->next; - } - - while (sr) { - fprintf(f, "\nSearch%s id ", sr->af == AF_INET6 ? " (IPv6)" : ""); - print_hex(f, sr->id, 20); - fprintf(f, " age %d%s\n", (int)(h->now - sr->step_time), sr->done ? " (done)" : ""); - for (i = 0; i < sr->numnodes; i++) { - struct search_node *n = &sr->nodes[i]; - fprintf(f, "Node %d id ", i); - print_hex(f, n->id, 20); - fprintf(f, " bits %d age ", common_bits(sr->id, n->id)); - if (n->request_time) { - fprintf(f, "%d, ", (int)(h->now - n->request_time)); - } - fprintf(f, "%d", (int)(h->now - n->reply_time)); - if (n->pinged) { - fprintf(f, " (%d)", n->pinged); - } - fprintf(f, "%s%s.\n", find_node(h, n->id, AF_INET) ? " (known)" : "", n->replied ? " (replied)" : ""); - } - sr = sr->next; - } - - while (st) { - fprintf(f, "\nStorage "); - print_hex(f, st->id, 20); - fprintf(f, " %d/%d nodes:", st->numpeers, st->maxpeers); - for (i = 0; i < st->numpeers; i++) { - char buf[100]; - if (st->peers[i].addr.family == AF_INET) { - ks_snprintf(buf, sizeof(buf), "%s", st->peers[i].addr.host); - } else if (st->peers[i].addr.family == AF_INET6) { - ks_snprintf(buf, sizeof(buf), "[%s]", st->peers[i].addr.host); - } else { - strcpy(buf, "???"); - } - fprintf(f, " %s:%u (%ld)", buf, st->peers[i].addr.port, (long)(h->now - st->peers[i].time)); - } - st = st->next; - } - - fprintf(f, "\n\n"); - fflush(f); -} - -static void ks_dht_store_entry_destroy(struct ks_dht_store_entry_s **old_entry) -{ - struct ks_dht_store_entry_s *entry = *old_entry; - ks_pool_t *pool = entry->pool; - *old_entry = NULL; - - /* While setting these members to NULL is not required, defaulting to including them for easier debugging */ - entry->key = NULL; - entry->content_type = NULL; - entry->payload_raw = NULL; - entry->pool = NULL; - - if ( entry->bencode_message_raw ) { - ben_free(entry->bencode_message_raw); - entry->bencode_message_raw = NULL; - } - - if ( entry->payload_bencode ) { - ben_free(entry->payload_bencode); - entry->payload_bencode = NULL; - } - - if ( entry->body ) { - cJSON_Delete(entry->body); - entry->body = NULL; - } - - ks_pool_free(pool, &entry); - return; -} - -/* Entries can be created by a remote system 'pushing' a message to us, or the local system creating and sending the message. */ - -static int ks_dht_store_entry_create(struct dht_handle_s *h, struct bencode *msg, struct ks_dht_store_entry_s **new_entry, ks_time_t life, ks_bool_t mine) -{ - struct ks_dht_store_entry_s *entry = NULL; - ks_time_t now = ks_time_now_sec(); - - entry = ks_pool_alloc(h->pool, sizeof(struct ks_dht_store_entry_s)); - entry->pool = h->pool; - entry->received = now; - entry->expiration = now + life; - entry->last_announce = 0; /* TODO: Instead we should announce this one, and set to now */ - entry->serial = 1; - entry->mine = mine; - - entry->bencode_message_raw = msg; - entry->payload_raw = NULL; - - entry->content_type = NULL; - entry->payload_bencode = NULL; - entry->body = NULL; - - if ( msg ) { - struct bencode *key_args = ben_dict_get_by_str(msg, "a"); - struct bencode *key_token = NULL; - struct bencode *key_v = NULL; - struct bencode *key_ct = NULL; - struct bencode *tmp_v = NULL; - - if ( !key_args ) { - ks_log(KS_LOG_ERROR, "dht_store_entry requires an 'a' key in the message\n"); - goto err; - } - - key_token = ben_dict_get_by_str(key_args, "token"); - if ( !key_token ) { - ks_log(KS_LOG_ERROR, "dht_store_entry requires an 'token' key in the message\n"); - goto err; - } - entry->key = ben_str_val(key_token); - ks_log(KS_LOG_INFO, "dht_store_entry now with new key[%s]\n", entry->key); - - key_v = ben_dict_get_by_str(key_args, "v"); - if ( !key_v ) { - ks_log(KS_LOG_ERROR, "dht_store_entry requires an 'v' key in the message\n"); - goto err; - } - - tmp_v = ben_decode(ben_str_val(key_v), ben_str_len(key_v)); - - entry->payload_raw = ben_str_val(tmp_v); - entry->payload_bencode = ben_decode(entry->payload_raw, ben_str_len(tmp_v)); - - if ( !entry->payload_bencode ) { - ks_log(KS_LOG_WARNING, "dht_store_entry payload failed to parse as bencode object\n"); - goto err; - } - - ks_log(KS_LOG_DEBUG, "Payload: %s\n", ben_print(entry->payload_bencode)); - - if ( ! ben_is_dict( entry->payload_bencode ) ) { - ks_log(KS_LOG_DEBUG, "dht_store_entry is not a bencode dict. Legal, just not likely one of ours.\n"); - goto done; - } - - /* - This is a custom key that SWITCHBLADE is adding to give the protocol decoder a hint as to the payload type. - If this key is not set, then we need to assume that the payload is binary buffer of a known length, likely not from SWITCHBLADE. - */ - key_ct = ben_dict_get_by_str(entry->payload_bencode, "ct"); - if ( !key_ct ) { - ks_log(KS_LOG_DEBUG, "dht_store_entry without a 'ct' key to hint at payload content type. Legal, just not likely one of ours.\n"); - goto done; - } - - entry->content_type = ben_str_val(key_ct); - - if ( !ben_cmp_with_str(key_ct, "json") ) { - struct bencode *key_b = ben_dict_get_by_str(entry->payload_bencode, "b"); - int buf_len = ben_str_len(key_b); - char *buf = NULL; - - buf = calloc(1, buf_len); - memcpy(buf, ben_str_val(key_b), buf_len); - - entry->body = cJSON_Parse(buf); - free(buf); - buf = NULL; - - if ( !entry->body ) { - ks_log(KS_LOG_ERROR, "dht_store_entry with json payload failed to json parse. Someone sent and signed an invalid message.\n"); - goto err; - } - - if ( h->store_json_cb ) { - h->store_json_cb(h, entry->body, h->store_json_cb_arg); - } - } - } - - done: - *new_entry = entry; - return 0; - err: - ks_dht_store_entry_destroy(&entry); - return -1; -} - -static struct ks_dht_store_entry_s *ks_dht_store_fetch(struct ks_dht_store_s *store, char *key) -{ - assert(store != NULL); - - return ks_hash_search(store->hash, (void *)key, 0); -} - -static int ks_dht_store_insert(struct ks_dht_store_s *store, struct ks_dht_store_entry_s *entry, ks_time_t now) -{ - return ks_hash_insert(store->hash, (void *)entry->key, entry); -} - -static int ks_dht_store_replace(struct ks_dht_store_s *store, struct ks_dht_store_entry_s *entry) -{ - struct ks_dht_store_entry_s *val = ks_hash_remove(store->hash, (void *) entry->key); - - if ( val ) { - ks_dht_store_entry_destroy(&val); - } - - return ks_hash_insert(store->hash, (void *) entry->key, entry); -} - -static void ks_dht_store_prune(struct ks_dht_store_s *store, ks_time_t now) -{ - (void) store; - (void) now; - return; -} - -/* TODO: Look into using the ks_hash automatic destructor functionality. */ -static int ks_dht_store_create(ks_pool_t *pool, struct ks_dht_store_s **new_store) -{ - struct ks_dht_store_s *store = NULL; - - store = ks_pool_alloc(pool, sizeof(struct ks_dht_store_s)); - store->next_expiring = 0; - store->pool = pool; - - ks_hash_create(&store->hash, KS_HASH_MODE_DEFAULT, KS_HASH_FLAG_RWLOCK, pool); - - *new_store = store; - return 0; -} - -static void ks_dht_store_destroy(struct ks_dht_store_s **old_store) -{ - struct ks_dht_store_s *store = *old_store; - ks_hash_iterator_t *itt = NULL; - ks_pool_t *pool = store->pool; - *old_store = NULL; - - ks_hash_write_lock(store->hash); - for (itt = ks_hash_first(store->hash, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { - const void *key = NULL; - struct ks_dht_store_entry_s *val = NULL; - - ks_hash_this(itt, &key, NULL, (void **) &val); - ks_hash_remove(store->hash, (char *)key); - - ks_dht_store_entry_destroy(&val); - } - ks_hash_write_unlock(store->hash); - - ks_hash_destroy(&store->hash); - - ks_pool_free(pool, &store); - - return; -} - -static void reset_poll(dht_handle_t *h) -{ - int i = 0, socks = h->ip4s + h->ip6s; - ks_hash_iterator_t *itt; - - if (!h->iphash) return; - - if (h->num_pollsocks < socks) { - h->num_pollsocks = socks; - h->pollsocks = (struct pollfd *)ks_pool_resize(h->pool, (void *)h->pollsocks, sizeof(struct pollfd) * h->num_pollsocks); - h->iptsocks = (ks_ip_t **) ks_pool_resize(h->pool, (void *)h->iptsocks, sizeof(ks_ip_t *) * h->num_pollsocks); - h->addrsocks = (ks_sockaddr_t **) ks_pool_resize(h->pool, (void *)h->addrsocks, sizeof(ks_sockaddr_t *) * h->num_pollsocks); - ks_log(KS_LOG_DEBUG, "Resize poll array to %d\n", h->num_pollsocks); - } - - for (itt = ks_hash_first(h->iphash, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { - const void *key; - void *val; - ks_ip_t *ipt; - - ks_hash_this(itt, &key, NULL, &val); - - ipt = (ks_ip_t *) val; - - h->pollsocks[i].fd = ipt->sock; - h->pollsocks[i].events = POLLIN | POLLERR; - h->iptsocks[i] = ipt; - h->addrsocks[i] = &ipt->addr; - i++; - } -} - -KS_DECLARE(ks_status_t) ks_dht_get_bind_addrs(dht_handle_t *h, const ks_sockaddr_t ***addrs, ks_size_t *addrlen) -{ - *addrs = (const ks_sockaddr_t **) h->addrsocks; - *addrlen = h->num_pollsocks; - - return KS_STATUS_SUCCESS; -} - -KS_DECLARE(ks_status_t) ks_dht_one_loop(dht_handle_t *h, int timeout) -{ - ks_status_t status; - int s, i; - unsigned char buf[65536] = {0}; - ks_size_t bytes = sizeof(buf); - - if (!h->started) { - return KS_STATUS_FAIL; - } - - - reset_poll(h); - - if (!timeout) timeout = h->tosleep * 1000; - - - s = ks_poll(h->pollsocks, h->num_pollsocks, timeout); - - if (s < 0) { - return KS_STATUS_FAIL; - } - - if (s == 0) { - dht_periodic(h, buf, 0, NULL); - return KS_STATUS_TIMEOUT; - } - - for (i = 0; i < h->num_pollsocks; i++) { - if ((h->pollsocks[i].revents & POLLIN)) { - ks_sockaddr_t remote_addr = KS_SA_INIT; - - remote_addr.family = h->iptsocks[i]->addr.family; - if ((status = ks_socket_recvfrom(h->pollsocks[i].fd, buf, &bytes, &remote_addr)) == KS_STATUS_SUCCESS) { - // git rid of tosleep and convert it to non-blocking counter so you can still call this in a loop and just return timeout till tosleep expired - // beginning of rabbit hole to change references to addrs to ks_addrs instead and stop passing sockaddr and len all over the place. - dht_periodic(h, buf, bytes, &remote_addr); - } - } - } - - return KS_STATUS_SUCCESS; -} - -static void clear_all_ip(dht_handle_t *h) -{ - ks_hash_iterator_t *itt; - - if (!h->iphash) return; - - ks_hash_write_lock(h->iphash); - for (itt = ks_hash_first(h->iphash, KS_UNLOCKED); itt; itt = ks_hash_next(&itt)) { - const void *key; - void *val; - ks_ip_t *ipt; - - ks_hash_this(itt, &key, NULL, &val); - - ipt = (ks_ip_t *) val; - - ks_socket_close(&ipt->sock); - ks_pool_free(h->pool, &ipt); - - if (ipt->addr.family == AF_INET) { - h->ip4s--; - } else { - h->ip6s--; - } - - ks_hash_remove(h->iphash, (char *)key); - } - ks_hash_write_unlock(h->iphash); - - ks_hash_destroy(&h->iphash); - -} - -static ks_ip_t *add_ip(dht_handle_t *h, const char *ip, int port, int family) -{ - ks_ip_t *ipt; - - ks_assert(h); - ks_assert(ip); - - if (!port) port = h->port; - - if (family == AF_INET) { - h->af_flags |= KS_DHT_AF_INET4; - - if (!h->buckets) { - h->buckets = ks_pool_alloc(h->pool, sizeof(*h->buckets)); - h->buckets->af = AF_INET; - } - - } else if (family == AF_INET6) { - h->af_flags |= KS_DHT_AF_INET6; - - if (!h->buckets6) { - h->buckets6 = ks_pool_alloc(h->pool, sizeof(*h->buckets6)); - h->buckets6->af = AF_INET6; - } - } - - - ks_log(KS_LOG_DEBUG, "Adding bind ip: %s port: %d family:%d\n", ip, port, family); - - ipt = ks_pool_alloc(h->pool, sizeof(*ipt)); - ipt->sock = KS_SOCK_INVALID; - - ks_set_string(ipt->ip, ip); - - ks_addr_set(&ipt->addr, ip, port, family); - - if ((ipt->sock = socket(family, SOCK_DGRAM, IPPROTO_UDP)) == KS_SOCK_INVALID) { - ks_log(KS_LOG_ERROR, "Socket Error\n"); - ks_pool_free(h->pool, &ipt); - return NULL; - } - - if (ks_addr_bind(ipt->sock, &ipt->addr) != KS_STATUS_SUCCESS) { - ks_log(KS_LOG_ERROR, "Error Adding bind ip: %s port: %d sock: %d (%s)\n", ip, port, ipt->sock, strerror(errno)); - ks_socket_close(&ipt->sock); - ks_pool_free(h->pool, &ipt); - return NULL; - } - - ks_socket_option(ipt->sock, SO_REUSEADDR, KS_TRUE); - ks_socket_option(ipt->sock, KS_SO_NONBLOCK, KS_TRUE); - - ks_hash_insert(h->iphash, (void *)ipt->ip, ipt); - - if (family == AF_INET) { - h->ip4s++; - } else { - h->ip6s++; - } - - reset_poll(h); - - return ipt; -} - -KS_DECLARE(void) ks_dht_set_port(dht_handle_t *h, unsigned int port) -{ - h->port = port; -} - -KS_DECLARE(void) ks_dht_set_v(dht_handle_t *h, const unsigned char *v) -{ - if (v) { - memcpy(h->my_v, "1:v4:", 5); - memcpy(h->my_v + 5, v, 4); - h->have_v = 1; - } else { - h->have_v = 0; - } -} - -KS_DECLARE(ks_status_t) ks_dht_add_ip(dht_handle_t *h, char *ip, int port) -{ - int family = AF_INET; - - if (strchr(ip, ':')) { - family = AF_INET6; - } - - return add_ip(h, ip, port, family) ? KS_STATUS_SUCCESS : KS_STATUS_FAIL; -} - - -KS_DECLARE(void) ks_dht_set_callback(dht_handle_t *h, dht_callback_t callback, void *closure) -{ - h->callback = callback; - h->closure = closure; -} - - -KS_DECLARE(void) ks_dht_set_param(dht_handle_t *h, ks_dht_param_t param, ks_bool_t val) -{ - switch(param) { - case DHT_PARAM_AUTOROUTE: - h->autoroute = val; - break; - } -} - -KS_DECLARE(void) ks_dht_start(dht_handle_t *h) -{ - char ip[48] = ""; - int mask = 0; - - if (h->started) return; - - if ((h->af_flags & KS_DHT_AF_INET4) && !h->ip4s) { - ks_find_local_ip(ip, sizeof(ip), &mask, AF_INET, NULL); - add_ip(h, ip, 0, AF_INET); - } - - if ((h->af_flags & KS_DHT_AF_INET6) && !h->ip6s) { - ks_find_local_ip(ip, sizeof(ip), &mask, AF_INET6, NULL); - add_ip(h, ip, 0, AF_INET6); - } - - h->started = 1; - -} - -//KS_DECLARE(int) dht_init(dht_handle_t **handle, int s, int s6, const unsigned char *id, const unsigned char *v, unsigned int port) -KS_DECLARE(ks_status_t) ks_dht_init(dht_handle_t **handle, ks_dht_af_flag_t af_flags, const unsigned char *id, unsigned int port) -{ - int rc; - dht_handle_t *h; - ks_pool_t *pool; - - ks_pool_open(&pool); - *handle = h = ks_pool_alloc(pool, sizeof(dht_handle_t)); - - h->pool = pool; - h->searches = NULL; - h->numsearches = 0; - - if (port) { - h->port = port; - } else { - h->port = 5309; - } - - h->af_flags = af_flags; - - - ks_hash_create(&h->iphash, KS_HASH_MODE_DEFAULT, KS_HASH_FLAG_RWLOCK, h->pool); - - h->store_json_cb = NULL; - h->store_json_cb_arg = NULL; - - h->storage = NULL; - h->numstorage = 0; - - if (!id) { - //ks_random_string((char *)h->myid, 20, NULL); - randombytes_buf(h->myid, 20); - } else { - memcpy(h->myid, id, 20); - } - - h->have_v = 0; - - h->now = ks_time_now_sec(); - - h->mybucket_grow_time = h->now; - h->mybucket6_grow_time = h->now; - h->confirm_nodes_time = h->now + random() % 3; - - h->search_id = random() & 0xFFFF; - h->search_time = 0; - - h->next_blacklisted = 0; - - h->token_bucket_time = h->now; - h->token_bucket_tokens = MAX_TOKEN_BUCKET_TOKENS; - - memset(h->secret, 0, sizeof(h->secret)); - rc = rotate_secrets(h); - if (rc < 0) - goto fail; - - - expire_buckets(h, h->buckets); - expire_buckets(h, h->buckets6); - - ks_dht_store_create(h->pool, &h->store); - - return KS_STATUS_SUCCESS; - - fail: - ks_pool_free(h->pool, &h->buckets); - h->buckets = NULL; - ks_pool_free(h->pool, &h->buckets6); - h->buckets6 = NULL; - return KS_STATUS_FAIL; -} - -KS_DECLARE(int) dht_uninit(dht_handle_t **handle) -{ - dht_handle_t *h; - ks_pool_t *pool; - - ks_assert(handle && *handle); - - h = *handle; - *handle = NULL; - - clear_all_ip(h); - - while (h->buckets) { - struct bucket *b = h->buckets; - h->buckets = b->next; - while (b->nodes) { - struct node *n = b->nodes; - b->nodes = n->next; - ks_pool_free(h->pool, &n); - } - ks_pool_free(h->pool, &b); - } - - while (h->buckets6) { - struct bucket *b = h->buckets6; - h->buckets6 = b->next; - while (b->nodes) { - struct node *n = b->nodes; - b->nodes = n->next; - ks_pool_free(h->pool, &n); - } - ks_pool_free(h->pool, &b); - } - - while (h->storage) { - struct storage *st = h->storage; - h->storage = h->storage->next; - ks_pool_free(h->pool, &st->peers); - ks_pool_free(h->pool, &st); - } - - while (h->searches) { - struct search *sr = h->searches; - h->searches = h->searches->next; - ks_pool_free(h->pool, &sr); - } - - ks_dht_store_destroy(&h->store); - pool = h->pool; - h->pool = NULL; - ks_pool_free(pool, &h); - ks_pool_close(&pool); - - return 1; -} - -/* Rate control for requests we receive. */ - -static int token_bucket(dht_handle_t *h) -{ - if (h->token_bucket_tokens == 0) { - h->token_bucket_tokens = MIN(MAX_TOKEN_BUCKET_TOKENS, 100 * (h->now - h->token_bucket_time)); - h->token_bucket_time = h->now; - } - - if (h->token_bucket_tokens == 0) { - return 0; - } - - h->token_bucket_tokens--; - return 1; -} - -static int neighbourhood_maintenance(dht_handle_t *h, int af) -{ - unsigned char id[20]; - struct bucket *b = find_bucket(h, h->myid, af); - struct bucket *q; - struct node *n; - - if (b == NULL) { - return 0; - } - - memcpy(id, h->myid, 20); - id[19] = random() & 0xFF; - q = b; - - if (q->next && (q->count == 0 || (random() & 7) == 0)) { - q = b->next; - } - - if (q->count == 0 || (random() & 7) == 0) { - struct bucket *r; - r = previous_bucket(h, b); - if (r && r->count > 0) { - q = r; - } - } - - if (q) { - /* Since our node-id is the same in both DHTs, it's probably - profitable to query both families. */ - - n = random_node(q); - if (n) { - unsigned char tid[4]; - - const char *msg; - - - if ((h->af_flags & KS_DHT_AF_INET6) && (h->af_flags & KS_DHT_AF_INET4)) { - msg = "v4 and v6"; - } else if (h->af_flags & KS_DHT_AF_INET6) { - msg = "v6"; - } else { - msg = "v4"; - } - - - ks_log(KS_LOG_DEBUG, "Sending find_node for %s on %s neighborhood maintenance.\n", msg, af == AF_INET6 ? "IPv6" : "IPv4"); - make_tid(tid, "fn", 0); - send_find_node(h, &n->ss, tid, 4, id, sizeof(id), h->af_flags, n->reply_time >= h->now - 15); - pinged(h, n, q); - } - return 1; - } - return 0; -} - -static int bucket_maintenance(dht_handle_t *h, int af) -{ - struct bucket *b; - - b = af == AF_INET ? h->buckets : h->buckets6; - - while (b) { - struct bucket *q; - if (b->time < h->now - 600) { - /* This bucket hasn't seen any positive confirmation for a long - time. Pick a random id in this bucket's range, and send - a request to a random node. */ - unsigned char id[20]; - struct node *n; - int rc; - - rc = bucket_random(b, id); - if (rc < 0) { - memcpy(id, b->first, 20); - } - - q = b; - /* If the bucket is empty, we try to fill it from a neighbour. - We also sometimes do it gratuitiously to recover from - buckets full of broken nodes. */ - if (q->next && (q->count == 0 || (random() & 7) == 0)) { - q = b->next; - } - - if (q->count == 0 || (random() & 7) == 0) { - struct bucket *r; - r = previous_bucket(h, b); - if (r && r->count > 0) { - q = r; - } - } - - if (q) { - n = random_node(q); - if (n) { - unsigned char tid[4]; - int want = -1; - - if ((h->af_flags & KS_DHT_AF_INET4) && (h->af_flags & KS_DHT_AF_INET6)) { - struct bucket *otherbucket; - otherbucket = find_bucket(h, id, af == AF_INET ? AF_INET6 : AF_INET); - if (otherbucket && otherbucket->count < 8) { - /* The corresponding bucket in the other family is emptyish -- querying both is useful. */ - want = WANT4 | WANT6; - } else if (random() % 37 == 0) { - /* Most of the time, this just adds overhead. - However, it might help stitch back one of - the DHTs after a network collapse, so query - both, but only very occasionally. */ - want = WANT4 | WANT6; - } - } - - ks_log(KS_LOG_DEBUG, "Sending find_node for%s bucket maintenance.\n", af == AF_INET6 ? " IPv6" : ""); - make_tid(tid, "fn", 0); - send_find_node(h, &n->ss, tid, 4, id, sizeof(id), want, n->reply_time >= h->now - 15); - pinged(h, n, q); - /* In order to avoid sending queries back-to-back, give up for now and reschedule us soon. */ - return 1; - } - } - } - b = b->next; - } - return 0; -} - - -KS_DECLARE(int) dht_periodic(dht_handle_t *h, const void *buf, size_t buflen, ks_sockaddr_t *from) -//KS_DECLARE(int) dht_periodic(dht_handle_t *h, const void *buf, size_t buflen, const struct sockaddr *from, int fromlen, -// time_t *tosleep, dht_callback *callback, void *closure) -{ - unsigned char *logmsg = NULL; - h->now = ks_time_now_sec(); - - if (buflen > 0) { - dht_msg_type_t message; - unsigned char tid[16], id[20], info_hash[20], target[20]; - unsigned char nodes[26*16], nodes6[38*16], token[128] = {0}; - int tid_len = 16, token_len = 0; - int nodes_len = 26*16, nodes6_len = 38*16; - unsigned short port = 0; - unsigned char values[2048], values6[2048]; - int values_len = 2048, values6_len = 2048; - int want = 0; - unsigned short ttid; - struct bencode *msg_ben = NULL; - struct bencode *key_args = NULL; /* Request args */ - struct bencode *key_info_hash = NULL; - struct bencode *key_want = NULL; - struct bencode *key_token = NULL; - struct bencode *key_port = NULL; - struct bencode *key_target = NULL; - - struct bencode *key_resp = NULL; /* Response values */ - struct bencode *key_values = NULL; - struct bencode *key_values6 = NULL; - struct bencode *key_nodes = NULL; - struct bencode *key_nodes6 = NULL; - - if (is_martian(from)) { - goto dontread; - } - - if (node_blacklisted(h, from)) { - ks_log(KS_LOG_DEBUG, "Received packet from blacklisted node.\n"); - goto dontread; - } - - if (((char*)buf)[buflen] != '\0') { - ks_log(KS_LOG_DEBUG, "Unterminated message.\n"); - errno = EINVAL; - return -1; - } - - msg_ben = ben_decode((const void *) buf, buflen); - if ( !msg_ben ) { - ks_log(KS_LOG_DEBUG, "Received invalid message. Unable to ben_decode it.\n"); - goto dontread; - } - - message = parse_message(msg_ben, tid, &tid_len, id); - ks_log(KS_LOG_DEBUG, "Received bencode message[%d] from [%s] port (%d): \n\n%s\n", message, from->host, from->port, ben_print(msg_ben)); - - if (id_cmp(id, zeroes) == 0) { - message = DHT_MSG_INVALID; - } else if (id_cmp(id, h->myid) == 0) { - ks_log(KS_LOG_DEBUG, "Received message from self.\n"); - goto dontread; - } - - if (message > DHT_MSG_REPLY) { - /* Rate limit requests. */ - if (!token_bucket(h)) { - ks_log(KS_LOG_DEBUG, "Dropping request due to rate limiting.\n"); - goto dontread; - } - } - - key_args = ben_dict_get_by_str(msg_ben, "a"); - if ( key_args ) { - key_info_hash = ben_dict_get_by_str(key_args, "info_hash"); - - if ( key_info_hash ) { - memcpy(info_hash, ben_str_val(key_info_hash), ben_str_len(key_info_hash)); - } - - key_want = ben_dict_get_by_str(key_args, "want"); - - if ( key_want && ben_is_list(key_want)) { - int x = 0; - for( x = 0; x < ben_list_len(key_want); x++ ) { - struct bencode *key_tmp = ben_list_get(key_want, x); - if ( !ben_cmp_with_str(key_tmp, "n4") ) { - want |= WANT4; - } else if ( !ben_cmp_with_str(key_tmp, "n6") ) { - want |= WANT6; - } - } - } else { - want = WANT4; - } - - key_target = ben_dict_get_by_str(key_args, "target"); - - if ( key_target ) { - memcpy(target, ben_str_val(key_target), ben_str_len(key_target)); - } - - - key_token = ben_dict_get_by_str(key_args, "token"); - - if ( key_token ) { - token_len = ben_str_len(key_token); - memcpy(token, ben_str_val(key_token), token_len); - } - - key_port = ben_dict_get_by_str(key_args, "port"); - - if ( key_port ) { - port = ben_int_val(key_port); - } - } - - key_resp = ben_dict_get_by_str(msg_ben, "r"); - if ( key_resp ) { - key_values = ben_dict_get_by_str(key_resp, "values"); - - if ( key_values ) { - values_len = ben_str_len(key_values); - memcpy(values, ben_str_val(key_values), values_len); - } - - key_values6 = ben_dict_get_by_str(key_resp, "values6"); - - if ( key_values6 ) { - values6_len = ben_str_len(key_values6); - memcpy(values6, ben_str_val(key_values6), values6_len); - } - - key_nodes = ben_dict_get_by_str(key_resp, "nodes"); - - if ( key_nodes ) { - nodes_len = ben_str_len(key_nodes); - memcpy(nodes, ben_str_val(key_nodes), nodes_len); - ks_log(KS_LOG_DEBUG, "Parsed nodes from response with length %d\n", nodes_len); - } - - key_nodes6 = ben_dict_get_by_str(key_resp, "nodes6"); - - if ( key_nodes6 ) { - nodes6_len = ben_str_len(key_nodes6); - memcpy(nodes6, ben_str_val(key_nodes6), nodes6_len); - } - } - - logmsg = calloc(1, buflen); - ks_log(KS_LOG_DEBUG, "Message type %d\n", message); - switch(message) { - case DHT_MSG_STORE_PUT: - if ( buf ) { - struct ks_dht_store_entry_s *entry = NULL; - struct bencode *sig = NULL, *salt = NULL; - struct bencode *sig_ben = NULL, *pk_ben = NULL; - unsigned char *data_sig = NULL; - const char *sig_binary = NULL, *pk_binary = NULL; - size_t data_sig_len = 0; - - /* Handle checking callback handler, and response */ - if ( !key_args ) { - ks_log(KS_LOG_DEBUG, "Failed to locate 'a' field in message\n"); - goto dontread; - } else { - ks_log(KS_LOG_DEBUG, "Successfully located 'a' field in message\n"); - } - - ks_log(KS_LOG_DEBUG, "Received bencode store PUT: \n\n%s\n", ben_print(msg_ben)); - - sig_ben = ben_dict_get_by_str(key_args, "sig"); - sig_binary = ben_str_val(sig_ben); - - pk_ben = ben_dict_get_by_str(key_args, "k"); - pk_binary = ben_str_val(pk_ben); - - sig = ben_dict(); - - salt = ben_dict_get_by_str(key_args, "salt"); - if ( salt ) { - ben_dict_set(sig, ben_blob("salt", 4), ben_blob(ben_str_val(salt), ben_str_len(salt))); - } - - /* TODO: fix double reference here. Need to bencode duplicate these values, and then free sig when finished encoding it */ - ben_dict_set(sig, ben_blob("seq", 3), ben_dict_get_by_str(key_args, "seq")); - ben_dict_set(sig, ben_blob("v", 1), ben_dict_get_by_str(key_args, "v")); - - data_sig = (unsigned char *) ben_encode(&data_sig_len, sig); - - if ( !data_sig ) { - ks_log(KS_LOG_DEBUG, "Failed to encode message for signature validation\n"); - goto dontread; - } - - if (crypto_sign_verify_detached((unsigned char *)sig_binary, data_sig, data_sig_len, (unsigned char *) pk_binary) != 0) { - ks_log(KS_LOG_DEBUG, "Signature failed to verify. Corrupted or malicious data suspected!\n"); - goto dontread; - } else { - ks_log(KS_LOG_DEBUG, "Valid message store signature.\n"); - } - - ks_dht_store_entry_create(h, msg_ben, &entry, 600, 0); - ks_dht_store_insert(h->store, entry, h->now); - } - break; - case DHT_MSG_INVALID: - case DHT_MSG_ERROR: - ks_log(KS_LOG_DEBUG, "Unparseable message: %s\n", debug_printable(buf, logmsg, buflen)); - goto dontread; - case DHT_MSG_REPLY: - if (tid_len != 4) { - ks_log(KS_LOG_DEBUG, "Broken node truncates transaction ids: %s\n", debug_printable(buf, logmsg, buflen)); - /* This is really annoying, as it means that we will - time-out all our searches that go through this node. - Kill it. */ - blacklist_node(h, id, from); - goto dontread; - } - if (tid_match(tid, "pn", NULL)) { - ks_log(KS_LOG_DEBUG, "Pong!\n"); - new_node(h, id, from, 2); - } else if (tid_match(tid, "fn", NULL) || tid_match(tid, "gp", NULL)) { - int gp = 0; - struct search *sr = NULL; - if (tid_match(tid, "gp", &ttid)) { - gp = 1; - sr = find_search(h, ttid, from->family); - } - ks_log(KS_LOG_DEBUG, "Nodes found (%d+%d)%s!\n", nodes_len/26, nodes6_len/38, gp ? " for get_peers" : ""); - if (nodes_len % 26 != 0 || nodes6_len % 38 != 0) { - ks_log(KS_LOG_DEBUG, "Unexpected length for node info!\n"); - blacklist_node(h, id, from); - } else if (gp && sr == NULL) { - ks_log(KS_LOG_DEBUG, "Unknown search!\n"); - new_node(h, id, from, 1); - } else { - int i; - new_node(h, id, from, 2); - for (i = 0; i < nodes_len / 26; i++) { - unsigned char *ni = nodes + i * 26; - struct sockaddr_in sin; - ks_sockaddr_t addr = { 0 }; - - if (id_cmp(ni, h->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); - - ks_addr_set_raw(&addr, &sin.sin_addr, sin.sin_port, AF_INET); - new_node(h, ni, &addr, 0); - if (sr && sr->af == AF_INET) { - insert_search_node(h, ni, &addr, sr, 0, NULL, 0); - } - } - for (i = 0; i < nodes6_len / 38; i++) { - unsigned char *ni = nodes6 + i * 38; - struct sockaddr_in6 sin6; - ks_sockaddr_t addr = { 0 }; - - if (id_cmp(ni, h->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); - - ks_addr_set_raw(&addr, &sin6.sin6_addr, sin6.sin6_port, AF_INET6); - - new_node(h, ni, &addr, 0); - if (sr && sr->af == AF_INET6) { - insert_search_node(h, ni, &addr, sr, 0, NULL, 0); - } - } - if (sr) { - /* Since we received a reply, the number of requests in flight has decreased. Let's push another request. */ - search_send_get_peers(h, sr, NULL); - } - } - if (sr) { - if ( token_len ) { - ks_log(KS_LOG_DEBUG, "token %d [%.*s]\n", token_len, token); - } - insert_search_node(h, id, from, sr, 1, token, token_len); - if (values_len > 0 || values6_len > 0) { - ks_log(KS_LOG_DEBUG, "Got values (%d+%d)!\n", values_len / 6, values6_len / 18); - if (h->callback) { - if (values_len > 0) { - h->callback(h->closure, KS_DHT_EVENT_VALUES, sr->id, (void*)values, values_len); - } - if (values6_len > 0) { - h->callback(h->closure, KS_DHT_EVENT_VALUES6, sr->id, (void*)values6, values6_len); - } - } - } - } - } else if (tid_match(tid, "ap", &ttid)) { - struct search *sr; - ks_log(KS_LOG_DEBUG, "Got reply to announce_peer.\n"); - sr = find_search(h, ttid, from->family); - if (!sr) { - ks_log(KS_LOG_DEBUG, "Unknown search!\n"); - new_node(h, id, from, 1); - } else { - int i; - new_node(h, id, from, 2); - for (i = 0; i < sr->numnodes; i++) { - if (id_cmp(sr->nodes[i].id, id) == 0) { - sr->nodes[i].request_time = 0; - sr->nodes[i].reply_time = h->now; - sr->nodes[i].acked = 1; - sr->nodes[i].pinged = 0; - break; - } - } - /* See comment for gp above. */ - search_send_get_peers(h, sr, NULL); - } - } else { - ks_log(KS_LOG_DEBUG, "Unexpected reply: %s\n", debug_printable(buf, logmsg, buflen)); - } - break; - case DHT_MSG_PING: - ks_log(KS_LOG_DEBUG, "Ping (%d)!\n", tid_len); - new_node(h, id, from, 1); - ks_log(KS_LOG_DEBUG, "Sending pong.\n"); - send_pong(h, from, tid, tid_len); - break; - case DHT_MSG_FIND_NODE: - if ( key_args ) { - /* - http://www.bittorrent.org/beps/bep_0005.html - http://www.bittorrent.org/beps/bep_0032.html - - find_node Query = {"t":"aa", "y":"q", "q":"find_node", "a": {"id":"abcdefghij0123456789", "target":"mnopqrstuvwxyz123456"}} - bencoded = d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe - */ - - ks_log(KS_LOG_DEBUG, "Find node!\n"); - /* Needs to fetch the from, and fromlen from the decoded message, as well as the target and want */ - new_node(h, id, from, 1); - ks_log(KS_LOG_DEBUG, "Sending closest nodes (%d).\n", want); - send_closest_nodes(h, from, tid, tid_len, target, want, 0, NULL, NULL, 0); - } else { - goto dontread; - } - break; - case DHT_MSG_GET_PEERS: - /* - http://www.bittorrent.org/beps/bep_0005.html - - get_peers Query = {"t":"aa", "y":"q", "q":"get_peers", "a": {"id":"abcdefghij0123456789", "info_hash":"mnopqrstuvwxyz123456"}} - bencoded = d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:t2:aa1:y1:qe - */ - - ks_log(KS_LOG_DEBUG, "Get_peers!\n"); - new_node(h, id, from, 1); - if (id_cmp(info_hash, zeroes) == 0) { - ks_log(KS_LOG_DEBUG, "Eek! Got get_peers with no info_hash.\n"); - send_error(h, from, tid, tid_len, 203, "Get_peers with no info_hash"); - break; - } else { - struct storage *st = find_storage(h, info_hash); - unsigned char token[TOKEN_SIZE]; - make_token(h, from, 0, token); - if (st && st->numpeers > 0) { - ks_log(KS_LOG_DEBUG, "Sending found%s peers.\n", from->family == AF_INET6 ? " IPv6" : ""); - send_closest_nodes(h, from, tid, tid_len, info_hash, want, from->family, st, token, TOKEN_SIZE); - } else { - ks_log(KS_LOG_DEBUG, "Sending nodes for get_peers.\n"); - send_closest_nodes(h, from, tid, tid_len, info_hash, want, 0, NULL, token, TOKEN_SIZE); - } - } - break; - case DHT_MSG_ANNOUNCE_PEER: - ks_log(KS_LOG_DEBUG, "Announce peer!\n"); - new_node(h, id, from, 1); - if (id_cmp(info_hash, zeroes) == 0) { - ks_log(KS_LOG_DEBUG, "Announce_peer with no info_hash.\n"); - send_error(h, from, tid, tid_len, 203, "Announce_peer with no info_hash"); - break; - } - if (!token_match(h, token, token_len, from)) { - ks_log(KS_LOG_DEBUG, "Incorrect token for announce_peer.\n"); - send_error(h, from, tid, tid_len, 203, "Announce_peer with wrong token"); - break; - } - if (port == 0) { - ks_log(KS_LOG_DEBUG, "Announce_peer with forbidden port %d.\n", port); - send_error(h, from, tid, tid_len, 203, "Announce_peer with forbidden port number"); - break; - } - storage_store(h, info_hash, from, port); - /* Note that if storage_store failed, we lie to the requestor. This is to prevent them from backtracking, and hence polluting the DHT. */ - ks_log(KS_LOG_DEBUG, "Sending peer announced.\n"); - send_peer_announced(h, from, tid, tid_len); - } - } - - dontread: - if (h->now >= h->rotate_secrets_time) { - rotate_secrets(h); - } - - if (h->now >= h->expire_stuff_time) { - expire_buckets(h, h->buckets); - expire_buckets(h, h->buckets6); - expire_storage(h); - expire_searches(h); - } - - if (h->search_time > 0 && h->now >= h->search_time) { - struct search *sr; - sr = h->searches; - while (sr) { - if (!sr->done && sr->step_time + 5 <= h->now) { - search_step(h, sr); - } - sr = sr->next; - } - - h->search_time = 0; - - sr = h->searches; - while (sr) { - if (!sr->done) { - time_t tm = sr->step_time + 15 + random() % 10; - if (h->search_time == 0 || h->search_time > tm) { - h->search_time = tm; - } - } - sr = sr->next; - } - } - - if (h->now >= h->confirm_nodes_time) { - int soon = 0; - - soon |= bucket_maintenance(h, AF_INET); - soon |= bucket_maintenance(h, AF_INET6); - - if (!soon) { - if (h->mybucket_grow_time >= h->now - 150) { - soon |= neighbourhood_maintenance(h, AF_INET); - } - if (h->mybucket6_grow_time >= h->now - 150) { - soon |= neighbourhood_maintenance(h, AF_INET6); - } - } - - /* In order to maintain all buckets' age within 600 seconds, worst case is roughly 27 seconds, assuming the table is 22 bits deep. - We want to keep a margin for neighborhood maintenance, so keep this within 25 seconds. */ - if (soon) { - h->confirm_nodes_time = h->now + 5 + random() % 20; - } else { - h->confirm_nodes_time = h->now + 60 + random() % 120; - } - } - - if (h->confirm_nodes_time > h->now) { - h->tosleep = h->confirm_nodes_time - h->now; - } else { - h->tosleep = 0; - } - - if (h->search_time > 0) { - if (h->search_time <= h->now) { - h->tosleep = 0; - } else if (h->tosleep > h->search_time - h->now) { - h->tosleep = h->search_time - h->now; - } - } - ks_safe_free(logmsg); - - ks_dht_store_prune(h->store, h->now); - - return 1; -} - -KS_DECLARE(int) dht_get_nodes(dht_handle_t *h, struct sockaddr_in *sin, int *num, - struct sockaddr_in6 *sin6, int *num6) -{ - int i, j; - struct bucket *b; - struct node *n; - - i = 0; - - /* For restoring to work without discarding too many nodes, the list - must start with the contents of our bucket. */ - b = find_bucket(h, h->myid, AF_INET); - if (b == NULL) { - goto no_ipv4; - } - - n = b->nodes; - while (n && i < *num) { - if (node_good(h, n)) { - sin[i] = *(struct sockaddr_in*)&n->ss; - i++; - } - n = n->next; - } - - b = h->buckets; - while (b && i < *num) { - if (!in_bucket(h->myid, b)) { - n = b->nodes; - while (n && i < *num) { - if (node_good(h, n)) { - sin[i] = *(struct sockaddr_in*)&n->ss; - i++; - } - n = n->next; - } - } - b = b->next; - } - - no_ipv4: - - j = 0; - - b = find_bucket(h, h->myid, AF_INET6); - if (b == NULL) { - goto no_ipv6; - } - - n = b->nodes; - while (n && j < *num6) { - if (node_good(h, n)) { - sin6[j] = *(struct sockaddr_in6*)&n->ss; - j++; - } - n = n->next; - } - - b = h->buckets6; - while (b && j < *num6) { - if (!in_bucket(h->myid, b)) { - n = b->nodes; - while (n && j < *num6) { - if (node_good(h, n)) { - sin6[j] = *(struct sockaddr_in6*)&n->ss; - j++; - } - n = n->next; - } - } - b = b->next; - } - - no_ipv6: - - *num = i; - *num6 = j; - return i + j; -} - -KS_DECLARE(int) dht_insert_node(dht_handle_t *h, const unsigned char *id, ks_sockaddr_t *sa) -{ - struct node *n; - - if (sa->family != AF_INET) { - errno = EAFNOSUPPORT; - return -1; - } - - n = new_node(h, id, sa, 0); - return !!n; -} - -KS_DECLARE(int) dht_ping_node(dht_handle_t *h, ks_sockaddr_t *sa) -{ - unsigned char tid[4]; - - ks_log(KS_LOG_DEBUG, "Sending ping.\n"); - make_tid(tid, "pn", 0); - return send_ping(h, sa, tid, 4); -} - -/* 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 (h->have_v) { \ - COPY(buf, offset, h->my_v, sizeof(h->my_v), size); \ - } - -static int dht_send(dht_handle_t *h, const void *buf, size_t len, int flags, const ks_sockaddr_t *sa) -{ - char ip[80] = ""; - ks_ip_t *ipt; - - if (node_blacklisted(h, sa)) { - ks_log(KS_LOG_DEBUG, "Attempting to send to blacklisted node.\n"); - errno = EPERM; - return -1; - } - - - ks_ip_route(ip, sizeof(ip), sa->host); - - if (!(ipt = ks_hash_search(h->iphash, ip, KS_UNLOCKED)) && h->autoroute) { - ipt = add_ip(h, ip, 0, sa->family); - } - - if (!ipt) { - ks_log(KS_LOG_ERROR, "No route to dest\n"); - errno = EINVAL; - return -1; - } - - ks_log(KS_LOG_INFO, "Sending message to [%s] port (%d)\n", sa->host, sa->port); - - if (ks_socket_sendto(ipt->sock, (void *)buf, &len, (ks_sockaddr_t *)sa) != KS_STATUS_SUCCESS) { - ks_log(KS_LOG_ERROR, "Socket Error (%s)\n", strerror(errno)); - return -1; - } - - return 0; -} - -/* Sample ping packet '{"t":"aa", "y":"q", "q":"ping", "a":{"id":"abcdefghij0123456789"}}' */ -/* http://www.bittorrent.org/beps/bep_0005.html */ -static int send_ping(dht_handle_t *h, const ks_sockaddr_t *sa, const unsigned char *tid, int tid_len) -{ - char buf[512]; - int i = 0; - struct bencode *bencode_p = ben_dict(); - struct bencode *bencode_a_p = ben_dict(); - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("q", 1)); - ben_dict_set(bencode_p, ben_blob("q", 1), ben_blob("ping", 4)); - ben_dict_set(bencode_a_p, ben_blob("id", 2), ben_blob(h->myid, 20)); - ben_dict_set(bencode_p, ben_blob("a", 1), bencode_a_p); - - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); /* This SHOULD free the bencode_a_p as well */ - - return dht_send(h, buf, i, 0, sa); -} - -/* Sample pong packet '{"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}' */ -/* http://www.bittorrent.org/beps/bep_0005.html */ -static int send_pong(dht_handle_t *h, const ks_sockaddr_t *sa, const unsigned char *tid, int tid_len) -{ - char buf[512]; - int i = 0; - struct bencode *bencode_p = ben_dict(); - struct bencode *bencode_a_p = ben_dict(); - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("r", 1)); - ben_dict_set(bencode_a_p, ben_blob("id", 2), ben_blob(h->myid, 20)); - ben_dict_set(bencode_p, ben_blob("r", 1), bencode_a_p); - - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); /* This SHOULD free the bencode_a_p as well */ - - ks_log(KS_LOG_DEBUG, "Encoded PONG\n"); - return dht_send(h, buf, i, 0, sa); -} - -/* Sample find_node packet '{"t":"aa", "y":"q", "q":"find_node", "a": {"id":"abcdefghij0123456789", "target":"mnopqrstuvwxyz123456"}}' */ -/* Sample find_node packet w/ want '{"t":"aa", "y":"q", "q":"find_node", "a": {"id":"abcdefghij0123456789", "target":"mnopqrstuvwxyz123456", "want":"n4"}}' */ -/* http://www.bittorrent.org/beps/bep_0005.html */ -/* http://www.bittorrent.org/beps/bep_0032.html for want parameter */ -static int send_find_node(dht_handle_t *h, const ks_sockaddr_t *sa, - const unsigned char *tid, int tid_len, - const unsigned char *target, int target_len, int want, int confirm) -{ - char buf[512]; - int i = 0; - struct bencode *bencode_p = ben_dict(); - struct bencode *bencode_a_p = ben_dict(); - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("q", 1)); - ben_dict_set(bencode_p, ben_blob("q", 1), ben_blob("find_node", 9)); - ben_dict_set(bencode_a_p, ben_blob("id", 2), ben_blob(h->myid, 20)); - - if (target) ben_dict_set(bencode_a_p, ben_blob("target", 6), ben_blob(target, target_len)); - - if (want > 0) { - struct bencode *bencode_w = ben_list(); - if (want & WANT4) { - ben_list_append(bencode_w, ben_blob("n4", 2)); - } - if (want & WANT6) { - ben_list_append(bencode_w, ben_blob("n6", 2)); - } - ben_dict_set(bencode_a_p, ben_blob("want", 4), bencode_w); - } - - ben_dict_set(bencode_p, ben_blob("a", 1), bencode_a_p); - - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); /* This SHOULD free the bencode_a_p as well */ - - return dht_send(h, buf, i, confirm ? MSG_CONFIRM : 0, sa); -} - -/* sample find_node response '{"t":"aa", "y":"r", "r": {"id":"0123456789abcdefghij", "nodes": "def456..."}}'*/ -/* http://www.bittorrent.org/beps/bep_0005.html */ -static int send_nodes_peers(dht_handle_t *h, const ks_sockaddr_t *sa, - const unsigned char *tid, int tid_len, - const unsigned char *nodes, int nodes_len, - const unsigned char *nodes6, int nodes6_len, - int af, struct storage *st, - const unsigned char *token, int token_len) -{ - char buf[2048]; - int i = 0, j0, j, k; - struct bencode *bencode_p = ben_dict(); - struct bencode *bencode_a_p = ben_dict(); - struct bencode *ben_array = ben_list(); - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("r", 1)); - ben_dict_set(bencode_a_p, ben_blob("id", 2), ben_blob(h->myid, 20)); - if (token_len) ben_dict_set(bencode_a_p, ben_blob("token", 5), ben_blob(token, token_len)); - if (nodes_len) ben_dict_set(bencode_a_p, ben_blob("nodes", 5), ben_blob(nodes, nodes_len)); - if (nodes6_len) ben_dict_set(bencode_a_p, ben_blob("nodes6", 6), ben_blob(nodes6, nodes6_len)); - - /* - Response with peers = {"t":"aa", "y":"r", "r": {"id":"abcdefghij0123456789", "token":"aoeusnth", "values": ["axje.u", "idhtnm"]}} - */ - - if (st && st->numpeers > 0) { - // We treat the storage as a circular list, and serve a randomly - // chosen slice. In order to make sure we fit within 1024 octets, - // we limit ourselves to 50 peers. - - j0 = random() % st->numpeers; - j = j0; - k = 0; - - do { - if (st->peers[j].addr.family == af) { - char data[18]; - unsigned short swapped = htons(st->peers[j].addr.port); - void *ip = NULL; - ks_size_t iplen = 0; - - ks_addr_raw_data(&st->peers[j].addr, &ip, &iplen); - - memcpy(data, ip, iplen); - memcpy(data + iplen, &swapped, 2); - ben_list_append(ben_array, ben_blob(data, iplen + 2)); - k++; - } - j = (j + 1) % st->numpeers; - } while(j != j0 && k < 50); - ben_dict_set(bencode_a_p, ben_blob("values", 6), ben_array); - } - - ben_dict_set(bencode_p, ben_blob("r", 1), bencode_a_p); - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); /* This SHOULD free the bencode_a_p as well */ - - return dht_send(h, buf, i, 0, sa); -} - -static int insert_closest_node(unsigned char *nodes, int numnodes, - const unsigned char *id, struct node *n) -{ - int i, size; - - if (n->ss.family == AF_INET) { - size = 26; - } else if (n->ss.family == AF_INET6) { - size = 38; - } else { - abort(); - } - - for (i = 0; i< numnodes; i++) { - if (id_cmp(n->id, nodes + size * i) == 0) { - return numnodes; - } - if (xorcmp(n->id, nodes + size * i, id) < 0) { - break; - } - } - - if (i == 8) { - return numnodes; - } - - if (numnodes < 8) { - numnodes++; - } - - if (i < numnodes - 1) { - memmove(nodes + size * (i + 1), nodes + size * i, size * (numnodes - i - 1)); - } - - if (n->ss.family == AF_INET) { - memcpy(nodes + size * i, n->id, 20); - memcpy(nodes + size * i + 20, &n->ss.v.v4.sin_addr, 4); - memcpy(nodes + size * i + 24, &n->ss.v.v4.sin_port, 2); - } else if (n->ss.family == AF_INET6) { - memcpy(nodes + size * i, n->id, 20); - memcpy(nodes + size * i + 20, &n->ss.v.v6.sin6_addr, 16); - memcpy(nodes + size * i + 36, &n->ss.v.v6.sin6_port, 2); - } else { - abort(); - } - - return numnodes; -} - -static int buffer_closest_nodes(dht_handle_t *h, unsigned char *nodes, int numnodes, const unsigned char *id, struct bucket *b) -{ - struct node *n = b->nodes; - while (n) { - if (node_good(h, n)) { - numnodes = insert_closest_node(nodes, numnodes, id, n); - } - n = n->next; - } - return numnodes; -} - -static int send_closest_nodes(dht_handle_t *h, const ks_sockaddr_t *sa, - const unsigned char *tid, int tid_len, - const unsigned char *id, int want, - int af, struct storage *st, - const unsigned char *token, int token_len) -{ - unsigned char nodes[8 * 26]; - unsigned char nodes6[8 * 38]; - int numnodes = 0, numnodes6 = 0; - struct bucket *b; - - if (want < 0) { - want = sa->family == AF_INET ? WANT4 : WANT6; - } - - if ((want & WANT4)) { - if ((b = find_bucket(h, id, AF_INET))) { - numnodes = buffer_closest_nodes(h, nodes, numnodes, id, b); - if (b->next) { - numnodes = buffer_closest_nodes(h, nodes, numnodes, id, b->next); - } - if ((b = previous_bucket(h, b))) { - numnodes = buffer_closest_nodes(h, nodes, numnodes, id, b); - } - } else { - ks_log(KS_LOG_DEBUG, "send_closest_nodes did not find a 'close' ipv4 bucket\n"); - } - } - - if ((want & WANT6)) { - if ((b = find_bucket(h, id, AF_INET6))) { - numnodes6 = buffer_closest_nodes(h, nodes6, numnodes6, id, b); - if (b->next) { - numnodes6 = buffer_closest_nodes(h, nodes6, numnodes6, id, b->next); - } - if ((b = previous_bucket(h, b))) { - numnodes6 = buffer_closest_nodes(h, nodes6, numnodes6, id, b); - } - } else { - ks_log(KS_LOG_DEBUG, "send_closest_nodes did not find a 'close' ipv6 bucket\n"); - } - } - ks_log(KS_LOG_DEBUG, "send_closest_nodes (%d+%d nodes.)\n", numnodes, numnodes6); - - return send_nodes_peers(h, sa, tid, tid_len, - nodes, numnodes * 26, - nodes6, numnodes6 * 38, - af, st, token, token_len); -} - -/* sample get_peers request '{"t":"aa", "y":"q", "q":"get_peers", "a": {"id":"abcdefghij0123456789", "info_hash":"mnopqrstuvwxyz123456"}}'*/ -/* sample get_peers w/ want '{"t":"aa", "y":"q", "q":"get_peers", "a": {"id":"abcdefghij0123456789", "info_hash":"mnopqrstuvwxyz123456": "want":"n4"}}'*/ -/* http://www.bittorrent.org/beps/bep_0005.html */ -/* http://www.bittorrent.org/beps/bep_0032.html for want parameter */ -static int send_get_peers(dht_handle_t *h, const ks_sockaddr_t *sa, - unsigned char *tid, int tid_len, unsigned char *infohash, - int want, int confirm) -{ - char buf[512]; - int i = 0; - struct bencode *bencode_p = ben_dict(); - struct bencode *bencode_a_p = ben_dict(); - int infohash_len = infohash ? strlen((const char*)infohash) : 0; - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("q", 1)); - ben_dict_set(bencode_p, ben_blob("q", 1), ben_blob("get_peers", 9)); - ben_dict_set(bencode_a_p, ben_blob("id", 2), ben_blob(h->myid, 20)); - if (want > 0) { - struct bencode *bencode_w_p = ben_list(); - if (want & WANT4) { - ben_list_append(bencode_w_p, ben_blob("n4", 2)); - } - if (want & WANT6) { - ben_list_append(bencode_w_p, ben_blob("n6", 2)); - } - ben_dict_set(bencode_a_p, ben_blob("want", 4), bencode_w_p); - } - ben_dict_set(bencode_a_p, ben_blob("info_hash", 9), ben_blob(infohash, infohash_len)); - ben_dict_set(bencode_p, ben_blob("a", 1), bencode_a_p); - - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); /* This SHOULD free the bencode_a_p as well */ - - ks_log(KS_LOG_DEBUG, "Encoded GET_PEERS\n"); - - return dht_send(h, buf, i, confirm ? MSG_CONFIRM : 0, sa); -} -/* '{"t":"aa", "y":"q", "q":"announce_peer", "a": {"id":"abcdefghij0123456789", "implied_port": 1, "info_hash":"mnopqrstuvwxyz123456", "port": 6881, "token": "aoeusnth"}}'*/ -static int send_announce_peer(dht_handle_t *h, const ks_sockaddr_t *sa, - unsigned char *tid, int tid_len, - unsigned char *infohash, unsigned short port, - unsigned char *token, int token_len, int confirm) -{ - char buf[512]; - int i = 0; - struct bencode *bencode_p = ben_dict(); - struct bencode *bencode_a_p = ben_dict(); - int infohash_len = infohash ? strlen((const char*)infohash) : 0; - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("q", 1)); - ben_dict_set(bencode_p, ben_blob("q", 1), ben_blob("announce_peer", 13)); - ben_dict_set(bencode_a_p, ben_blob("id", 2), ben_blob(h->myid, 20)); - ben_dict_set(bencode_a_p, ben_blob("info_hash", 9), ben_blob(infohash, infohash_len)); - ben_dict_set(bencode_a_p, ben_blob("port", 5), ben_int(port)); - ben_dict_set(bencode_a_p, ben_blob("token", 5), ben_blob(token, token_len)); - ben_dict_set(bencode_p, ben_blob("a", 1), bencode_a_p); - - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); /* This SHOULD free the bencode_a_p as well */ - - ks_log(KS_LOG_DEBUG, "Encoded ANNOUNCE_PEERS\n"); - return dht_send(h, buf, i, confirm ? MSG_CONFIRM : 0, sa); -} -/* '{"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}'*/ -static int send_peer_announced(dht_handle_t *h, const ks_sockaddr_t *sa, unsigned char *tid, int tid_len) -{ - char buf[512]; - int i = 0; - struct bencode *bencode_p = ben_dict(); - struct bencode *bencode_a_p = ben_dict(); - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("r", 1)); - ben_dict_set(bencode_a_p, ben_blob("id", 2), ben_blob(h->myid, 20)); - ben_dict_set(bencode_p, ben_blob("r", 1), bencode_a_p); - - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); /* This SHOULD free the bencode_a_p as well */ - - ks_log(KS_LOG_DEBUG, "Encoded peer_announced: %s\n\n", buf); - return dht_send(h, buf, i, 0, sa); -} - -/* '{"t":"aa", "y":"e", "e":[201, "A Generic Error Ocurred"]}'*/ -static int send_error(dht_handle_t *h, const ks_sockaddr_t *sa, - unsigned char *tid, int tid_len, - int code, const char *message) -{ - char buf[512]; - int i = 0; - struct bencode *bencode_p = ben_dict(); - struct bencode *ben_array = ben_list(); - - ben_dict_set(bencode_p, ben_blob("t", 1), ben_blob(tid, tid_len)); - ben_dict_set(bencode_p, ben_blob("y", 1), ben_blob("e", 1)); - ben_list_append(ben_array, ben_int(code)); - ben_list_append(ben_array, ben_blob(message, strlen(message))); - ben_dict_set(bencode_p, ben_blob("e", 1), ben_array); - - i = ben_encode2(buf, 512, bencode_p); - ben_free(bencode_p); - - ks_log(KS_LOG_DEBUG, "Encoded error: %s\n\n", buf); - return dht_send(h, buf, i, 0, sa); -} - -#undef CHECK -#undef INC -#undef COPY -#undef ADD_V - -/* - -#ifdef HAVE_MEMMEM - -static void *dht_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) -{ - return memmem(haystack, haystacklen, needle, needlelen); -} - -#else - -static void *dht_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) -{ - const char *h = haystack; - const char *n = needle; - size_t i; - - - 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 dht_msg_type_t parse_message(struct bencode *bencode_p, - unsigned char *tid_return, int *tid_len, - unsigned char *id_return) -{ - // const unsigned char *p; - dht_msg_type_t type = DHT_MSG_INVALID; - struct bencode *b_tmp = NULL; - struct bencode *key_t = ben_dict_get_by_str(bencode_p, "t"); - struct bencode *key_args = ben_dict_get_by_str(bencode_p, "a"); - struct bencode *key_resp = ben_dict_get_by_str(bencode_p, "r"); - - /* Need to set tid, tid_len, and id_return. Then return the message type or msg_error. */ - - if ( key_t ) { - const char *tran = ben_str_val(key_t); - int tran_len = ben_str_len(key_t); - - memcpy(tid_return, tran, (size_t) tran_len); - *tid_len = tran_len; - } - - if ( key_args ) { - struct bencode *b_id = ben_dict_get_by_str( key_args, "id"); - const char *id = b_id ? ben_str_val(b_id) : NULL; - int id_len = ben_str_len(b_id); - - if ( id ) { - memcpy(id_return, id, id_len); - } - } - - if ( key_resp ) { - struct bencode *b_id = ben_dict_get_by_str( key_resp, "id"); - const char *id = b_id ? ben_str_val(b_id) : NULL; - int id_len = ben_str_len(b_id); - - if ( id ) { - memcpy(id_return, id, id_len); - } - } - - - if ( ben_dict_get_by_str(bencode_p, "y") && key_t ){ - /* This message is a KRPC message(aka DHT message) */ - - if ( ( b_tmp = ben_dict_get_by_str(bencode_p, "y") ) ) { - if ( !ben_cmp_with_str(b_tmp, "q") ) { /* Inbound queries */ - struct bencode *b_query = NULL; - const char *val = ben_str_val(b_tmp); - ks_log(KS_LOG_DEBUG, "Message Query [%s]\n", val); - - if ( !( b_query = ben_dict_get_by_str(bencode_p, "q") ) ) { - ks_log(KS_LOG_DEBUG, "Unable to locate query type field\n"); - } else { /* Has a query type */ - const char *query_type = ben_str_val(b_query); - if (!ben_cmp_with_str(b_query, "get_peers")) { - /* - { - 'a': { - 'id': '~\x12*\xe6L3\xba\x83\xafT\xe3\x02\x93\x0e\xae\xbd\xf8\xe1\x98\x87', - 'info_hash': 'w"E\x85\xdd97\xd1\xfe\x13Q\xfa\xdae\x9d\x8f\x86\xddN9' - }, - 'q': 'get_peers', - 't': '?\xf1', - 'v': 'LT\x01\x00', - 'y': 'q' - } - */ - - ks_log(KS_LOG_DEBUG, "get_peers query recieved\n"); - type = DHT_MSG_GET_PEERS; - goto done; - } else if (!ben_cmp_with_str(b_query, "ping")) { - /* - {'a': { - 'id': 'T\x1cd2\xc1\x85\xf4>?\x84#\xa8)\xd0`\x19y\xcf;\xda' - }, - 'q': 'ping', - 't': 'pn\x00\x00', - 'v': 'JC\x00\x00', - 'y': 'q' - } - */ - ks_log(KS_LOG_DEBUG, "ping query recieved from client \n"); - type = DHT_MSG_PING; - goto done; - } else if (!ben_cmp_with_str(b_query, "find_node")) { - /* - {'a': { - 'id': 'T\x1cq\x7f\xa9^\xf2\x97S\xceE\xad\xc9S\x9b\xa1\x1cCX\x8d', - 'target': 'T\x1cq\x7f\xa9C{\x83\xf9\xf6i&\x8b\x87*\xa2\xad\xad\x1a\xdd' - }, - 'q': 'find_node', - 't': '\x915\xbe\xfb', - 'v': 'UTu\x13', - 'y': 'q' - } - */ - type = DHT_MSG_FIND_NODE; - goto done; - } else if (!ben_cmp_with_str(b_query, "put")) { - ks_log(KS_LOG_DEBUG, "Recieved a store put request\n"); - type = DHT_MSG_STORE_PUT; - goto done; - } else { - ks_log(KS_LOG_DEBUG, "Unknown query type field [%s]\n", query_type); - } - } - - } else if ( !ben_cmp_with_str(b_tmp, "r") ) { /* Responses */ - const char *val = ben_str_val(b_tmp); - ks_log(KS_LOG_DEBUG, "Message Response [%s]\n", val); - type = DHT_MSG_REPLY; - goto done; - } else if ( !ben_cmp_with_str(b_tmp, "e") ) { - const char *val = ben_str_val(b_tmp); - ks_log(KS_LOG_DEBUG, "Message Error [%s]\n", val); - } else { - ks_log(KS_LOG_DEBUG, "Message Type Unknown!!!\n"); - } - } else { - ks_log(KS_LOG_DEBUG, "Message Type Unknown, has no 'y' key!!!\n"); - } - - /* - Decode the request or response - (b_tmp = ben_dict_get_by_str(bencode_p, "y"))) { - ks_log(KS_LOG_DEBUG, "query value: %s\n", ben_print(b_tmp)); - */ - } else { - ks_log(KS_LOG_DEBUG, "Message not a remote DHT request nor query\n"); - } - - /* Default to MSG ERROR */ - ks_log(KS_LOG_DEBUG, "Unknown or unsupported message type\n"); - return type; - - done: - return type; - - /* - if (dht_memmem(buf, buflen, "1:q4:ping", 9)) { - return DHT_MSG_PING; - } - - if (dht_memmem(buf, buflen, "1:q9:find_node", 14)) { - return DHT_MSG_FIND_NODE; - } - - if (dht_memmem(buf, buflen, "1:q9:get_peers", 14)) { - return DHT_MSG_GET_PEERS; - } - - if (dht_memmem(buf, buflen, "1:q13:announce_peer", 19)) { - return DHT_MSG_ANNOUNCE_PEER; - } - - char *val = ben_str_val(b_tmp); - - */ - - - /* - if (tid_return) { - p = dht_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 = dht_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 = dht_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 = dht_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 = dht_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 = dht_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 = dht_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 = dht_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 = dht_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); - i = q + 1 + l - (char*)buf; - if (l == 6) { - if (j + l > *values_len) { - continue; - } - memcpy((char*)values_return + j, q + 1, l); - j += l; - } else if (l == 18) { - if (j6 + l > *values6_len) { - continue; - } - memcpy((char*)values6_return + j6, q + 1, l); - j6 += l; - } else { - ks_log(KS_LOG_DEBUG, "Received weird value -- %d bytes.\n", (int)l); - } - } else { - break; - } - } - if (i >= buflen || buf[i] != 'e') { - ks_log(KS_LOG_DEBUG, "eek... unexpected end for values.\n"); - } - if (values_len) { - *values_len = j; - } - if (values6_len) { - *values6_len = j6; - } - } else { - if (values_len) { - *values_len = 0; - } - if (values6_len) { - *values6_len = 0; - } - } - } - - if (want_return) { - p = dht_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 { - ks_log(KS_LOG_DEBUG, "eek... unexpected want flag (%c)\n", buf[i]); - } - i += 2 + buf[i] - '0'; - } - if (i >= buflen || buf[i] != 'e') { - ks_log(KS_LOG_DEBUG, "eek... unexpected end for want.\n"); - } - } else { - *want_return = -1; - } - } - -#undef CHECK - - if (dht_memmem(buf, buflen, "1:y1:r", 6)) { - return DHT_MSG_REPLY; - } - - if (dht_memmem(buf, buflen, "1:y1:e", 6)) { - return DHT_MSG_ERROR; - } - - if (!dht_memmem(buf, buflen, "1:y1:q", 6)) { - return DHT_MSG_INVALID; - } - - if (dht_memmem(buf, buflen, "1:q4:ping", 9)) { - return DHT_MSG_PING; - } - - if (dht_memmem(buf, buflen, "1:q9:find_node", 14)) { - return DHT_MSG_FIND_NODE; - } - - if (dht_memmem(buf, buflen, "1:q9:get_peers", 14)) { - return DHT_MSG_GET_PEERS; - } - - if (dht_memmem(buf, buflen, "1:q13:announce_peer", 19)) { - return DHT_MSG_ANNOUNCE_PEER; - } - - return DHT_MSG_INVALID; - - overflow: - ks_log(KS_LOG_DEBUG, "Truncated message.\n"); - return DHT_MSG_INVALID; - */ - -} - -/* b64encode function taken from kws.c. Maybe worth exposing a function like this. */ -static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static int b64encode(unsigned char *in, ks_size_t ilen, unsigned char *out, ks_size_t olen) -{ - int y=0,bytes=0; - ks_size_t x=0; - unsigned int b=0,l=0; - - if(olen) { - } - - for(x=0;x= 6) { - out[bytes++] = c64[(b>>(l-=6))%64]; - if(++y!=72) { - continue; - } - //out[bytes++] = '\n'; - y=0; - } - } - - if (l > 0) { - out[bytes++] = c64[((b%16)<<(6-l))%64]; - } - if (l != 0) while (l < 6) { - out[bytes++] = '=', l += 2; - } - - return 0; -} - - -/* - This function should generate the fields needed for the mutable message. - - Save the sending for another api, and possibly a third to generate and send all in one. - NOTE: - 1. When sending a mutable message, CAS(compare and swap) values need to be validated. - 2. Mutable messages MUST have a new key pair generated for each different mutable message. - The announce key is generated as a hash from the public key. To use one key pair for multiple messages, - a salt MUST be used that is unique and constant per message. - 3. The target hash will be generated here, and will be the hash that must be used for announcing the message, and updating it. - -*/ -KS_DECLARE(int) ks_dht_generate_mutable_storage_args(struct bencode *data, int64_t sequence, int cas, - unsigned char *id, int id_len, /* querying nodes id */ - const unsigned char *sk, const unsigned char *pk, - unsigned char *salt, unsigned long long salt_length, - unsigned char *token, unsigned long long token_length, - unsigned char *signature, unsigned long long *signature_length, - struct bencode **arguments) -{ - struct bencode *arg = NULL, *sig = NULL; - unsigned char *encoded_message = NULL, *encoded_data = NULL; - size_t encoded_message_size = 0, encoded_data_size = 0; - int err = 0; - - if ( !data || !sequence || !id || !id_len || !sk || !pk || - !token || !token_length || !signature || !signature_length) { - ks_log(KS_LOG_ERROR, "Missing required input\n"); - return -1; - } - - if ( arguments && *arguments) { - ks_log(KS_LOG_ERROR, "Arguments already defined.\n"); - return -1; - } - - if ( salt && salt_length > 64 ) { - ks_log(KS_LOG_ERROR, "Salt is too long. Can not be longer than 64 bytes\n"); - return -1; - } - - if ( sequence && sequence < 0 ) { - ks_log(KS_LOG_ERROR, "Sequence out of acceptable range\n"); - return -1; - } - - encoded_data = (unsigned char *) ben_encode(&encoded_data_size, data); - - if ( encoded_data_size > 1000 ) { - ks_log(KS_LOG_ERROR, "Message is too long. Max is 1000 bytes\n"); - free(encoded_data); - return -1; - } - - /* Need to dynamically allocate a bencoded object for the signature. */ - sig = ben_dict(); - - if ( salt ) { - ben_dict_set(sig, ben_blob("salt", 4), ben_blob(salt, salt_length)); - } - - ben_dict_set(sig, ben_blob("seq", 3), ben_int(sequence)); - ben_dict_set(sig, ben_blob("v", 1), ben_blob(encoded_data, encoded_data_size)); - - encoded_message = ben_encode(&encoded_message_size, sig); - ks_log(KS_LOG_DEBUG, "Encoded data %d [%.*s]\n", encoded_message_size, encoded_message_size, encoded_message); - - err = crypto_sign_detached(signature, NULL, encoded_message, encoded_message_size, sk); - if ( err ) { - ks_log(KS_LOG_ERROR, "Failed to sign message with provided secret key\n"); - return 1; - } - - free(encoded_message); - ben_free(sig); - - arg = ben_dict(); - - if ( cas ) { - ben_dict_set(arg, ben_blob("cas", 3), ben_int(cas)); - } - - ben_dict_set(arg, ben_blob("id", 2), ben_blob(id, id_len)); - ben_dict_set(arg, ben_blob("k", 1), ben_blob(pk, 32)); /* All ed25519 public keys are 32 bytes */ - - if ( salt ) { - ben_dict_set(arg, ben_blob("salt", 4), ben_blob(salt, salt_length)); - } - - ben_dict_set(arg, ben_blob("seq", 3), ben_int(sequence)); - ben_dict_set(arg, ben_blob("sig", 3), ben_blob(signature, (size_t) *signature_length)); - ben_dict_set(arg, ben_blob("token", 5), ben_blob(token, token_length)); - ben_dict_set(arg, ben_blob("v", 1), ben_blob(encoded_data, encoded_data_size)); - - *arguments = arg; - - free(encoded_data); - - return 0; -} - -KS_DECLARE(int) ks_dht_calculate_mutable_storage_target(unsigned char *pk, unsigned char *salt, int salt_length, unsigned char *target, int target_length) -{ - SHA_CTX sha; - unsigned char sha1[20] = {0}; - - /* Generate target sha-1 hash */ - SHA1_Init(&sha); - SHA1_Update(&sha, pk, 32); - - if ( salt ) { - SHA1_Update(&sha, salt, salt_length); - } - - SHA1_Final(sha1, &sha); - b64encode(sha1, 20, target, target_length); - - return 0; -} - -KS_DECLARE(int) ks_dht_send_message_mutable_cjson(dht_handle_t *h, unsigned char *sk, unsigned char *pk, char **node_id, - char *message_id, int sequence, cJSON *message, ks_time_t life) -{ - struct bencode *body = ben_dict(); - char *output = NULL; - char *json = cJSON_PrintUnformatted(message); - int err = 0; - size_t output_len = 0; - - ben_dict_set(body, ben_blob("ct", 2), ben_blob("json", 4)); - ben_dict_set(body, ben_blob("b", 1), ben_blob(json, strlen(json))); - - output = (char *)ben_encode(&output_len, body); - - err = ks_dht_send_message_mutable(h, sk, pk, node_id, message_id, sequence, output, life); - free(json); - free(output); - ben_free(body); - - return err; -} - -KS_DECLARE(int) ks_dht_send_message_mutable(dht_handle_t *h, unsigned char *sk, unsigned char *pk, char **node_id, - char *message_id, int sequence, char *message, ks_time_t life) -{ - unsigned char target[40], signature[crypto_sign_BYTES]; - unsigned long long signature_length = crypto_sign_BYTES; - int message_length = strlen(message); - unsigned char tid[4]; - unsigned char *salt = (unsigned char *)message_id; - int salt_length = strlen(message_id); - struct ks_dht_store_entry_s *entry = NULL; - struct bencode *b_message = ben_blob(message, message_length); - struct bencode *args = NULL, *data = NULL; - char buf[1500]; - size_t buf_len = 0; - int err = 0; - h->now = ks_time_now_sec(); - - if ( !life ) { - /* Default to now plus 10 minutes */ - life = 600; - } - - make_tid(tid, "mm", 0); - - ks_dht_calculate_mutable_storage_target(pk, salt, salt_length, target, 40); - - if ( (entry = ks_dht_store_fetch(h->store, (char *)target)) ) { - if ( sequence < entry->serial ) { - sequence = entry->serial; - } - } - - - /* -int ks_dht_generate_mutable_storage_args(struct bencode *data, int64_t sequence, int cas, - unsigned char *id, int id_len, - const unsigned char *sk, const unsigned char *pk, - unsigned char *salt, unsigned long long salt_length, - unsigned char *token, unsigned long long token_length, - unsigned char *signature, unsigned long long *signature_length, - struct bencode **arguments) */ - - - err = ks_dht_generate_mutable_storage_args(b_message, sequence, 1, - h->myid, 20, - sk, pk, - salt, salt_length, - (unsigned char *) target, 40, - signature, &signature_length, - &args); - - if ( err ) { - return err; - } - - data = ben_dict(); - ben_dict_set(data, ben_blob("a", 1), args); - ben_dict_set(data, ben_blob("t", 1), ben_blob(tid, 4)); - ben_dict_set(data, ben_blob("y", 1), ben_blob("q", 1)); - ben_dict_set(data, ben_blob("q", 1), ben_blob("put", 3)); - - buf_len = ben_encode2(buf, 1500, data); - - err = ks_dht_store_entry_create(h, data, &entry, life, 1); - if ( err ) { - return err; - } - - ks_dht_store_replace(h->store, entry); - - /* dht_search() announce of this hash */ - dht_search(h, (const unsigned char *)entry->key, h->port, AF_INET, NULL, NULL); - - if ( node_id && node_id[0] ) { - /* We're being told where to send these messages. */ - int x = 0; - - for ( x = 0; node_id[x] != NULL; x++ ) { - unsigned char node_id_bin[20] = {0}; - struct node *n = NULL; - size_t size = 0; - - sodium_hex2bin(node_id_bin, 20, node_id[x], 40, ":", &size, NULL); - - n = find_node(h, node_id_bin, AF_INET); - - if ( !n ) { - n = find_node(h, node_id_bin, AF_INET6); - } - - if ( !n ) { - ks_log(KS_LOG_INFO, "Unable to find node with id\n"); - continue; - } - - err |= dht_send(h, buf, buf_len, 0, &n->ss); - } - } else { - /* Client api assumes that we'll figure out where to send the message. - We should find a bucket that resolves to the key, and send to all nodes in that bucket. - */ - struct bucket *b4 = find_bucket(h, (const unsigned char *)entry->key, AF_INET); - struct bucket *b6 = find_bucket(h, (const unsigned char *)entry->key, AF_INET6); - struct node *n = NULL; - - if ( b4 ) { - for ( n = b4->nodes; n->next; n = n->next ) { - err |= dht_send(h, buf, buf_len, 0, &n->ss); - } - } - - if ( b6 ) { - for ( n = b6->nodes; n->next; n = n->next ) { - err |= dht_send(h, buf, buf_len, 0, &n->ss); - } - } - } - - return err; -} - -// KS_DECLARE(int) ks_dht_send_message_mutable( -KS_DECLARE(int) ks_dht_api_find_node(dht_handle_t *h, char *node_id_hex, char *target_hex, ks_bool_t ipv6) -{ - unsigned char node_id[20] = {0}, target[20] = {0}, tid[4] = {0}; - struct node *n = NULL; - size_t size = 0; - - if ( strlen(node_id_hex) != 40 || strlen(target_hex) != 40 ) { - ks_log(KS_LOG_INFO, "node_id(%s)[%d] and target(%s)[%d] must each be 40 hex characters\n", node_id_hex, strlen(node_id_hex), target_hex, strlen(target_hex)); - return 1; - } - - sodium_hex2bin(node_id, 20, node_id_hex, 40, ":", &size, NULL); - sodium_hex2bin(target, 20, target_hex, 40, ":", &size, NULL); - - n = find_node(h, node_id, ipv6 ? AF_INET6 : AF_INET); - - if ( !n ) { - ks_log(KS_LOG_INFO, "Unable to find node with id[%s]\n", node_id_hex); - return 1; - } - - make_tid(tid, "fn", 0); - - return send_find_node(h, &n->ss, tid, 4, target, sizeof(target), WANT4 | WANT6, n->reply_time >= h->now - 15); -} - - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: - */ diff --git a/libs/libks/test/Makefile.am b/libs/libks/test/Makefile.am index 89e1fd45fe..bd8ce48325 100644 --- a/libs/libks/test/Makefile.am +++ b/libs/libks/test/Makefile.am @@ -64,6 +64,11 @@ testdht2_SOURCES = testdht2.c tap.c testdht2_CFLAGS = $(AM_CFLAGS) testdht2_LDADD = $(TEST_LDADD) +check_PROGRAMS += nodeidgen +nodeidgen_SOURCES = nodeidgen.c tap.c +nodeidgen_CFLAGS = $(AM_CFLAGS) +nodeidgen_LDADD = $(TEST_LDADD) + #check_PROGRAMS += testdht_net #testdht_net_SOURCES = testdht-net.c tap.c #testdht_net_CFLAGS = $(AM_CFLAGS) diff --git a/libs/libks/test/nodeidgen.c b/libs/libks/test/nodeidgen.c new file mode 100644 index 0000000000..14f95d9336 --- /dev/null +++ b/libs/libks/test/nodeidgen.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +int main() { + uint8_t nodeid[KS_DHT_NODEID_SIZE]; + char buf[KS_DHT_NODEID_SIZE * 2 + 1]; + + randombytes_buf(nodeid, KS_DHT_NODEID_SIZE); + + ks_dht_hex(nodeid, buf, KS_DHT_NODEID_SIZE); + + printf(buf); + return 0; +} diff --git a/libs/libks/test/testdht2.c b/libs/libks/test/testdht2.c index 59eee76cf4..0c726dcb73 100644 --- a/libs/libks/test/testdht2.c +++ b/libs/libks/test/testdht2.c @@ -1,6 +1,6 @@ #include -#include <../dht/ks_dht.h> -#include <../dht/ks_dht-int.h> +#include +#include #include ks_dht_storageitem_skey_t sk; @@ -55,6 +55,7 @@ int main() { //ks_size_t buflen; ks_status_t err; int mask = 0; + ks_dht_nodeid_t nodeid; ks_dht_t *dht1 = NULL; ks_dht_t *dht2 = NULL; ks_dht_t *dht3 = NULL; @@ -112,20 +113,23 @@ int main() { diag("Binding to %s on ipv6\n", v6); } - err = ks_dht_create(&dht1, NULL, NULL); + ks_dht_dehex(nodeid.id, "0000000000000000000000000000000000000001", KS_DHT_NODEID_SIZE); + err = ks_dht_create(&dht1, NULL, NULL, &nodeid); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_create(&dht2, NULL, NULL); + ks_dht_dehex(nodeid.id, "0000000000000000000000000000000000000002", KS_DHT_NODEID_SIZE); + err = ks_dht_create(&dht2, NULL, NULL, &nodeid); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_create(&dht3, NULL, NULL); + ks_dht_dehex(nodeid.id, "0000000000000000000000000000000000000003", KS_DHT_NODEID_SIZE); + err = ks_dht_create(&dht3, NULL, NULL, &nodeid); ok(err == KS_STATUS_SUCCESS); if (have_v4) { err = ks_addr_set(&addr, v4, KS_DHT_DEFAULT_PORT, AF_INET); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_bind(dht1, NULL, &addr, &ep1); + err = ks_dht_bind(dht1, &addr, &ep1); ok(err == KS_STATUS_SUCCESS); raddr1 = addr; @@ -133,7 +137,7 @@ int main() { err = ks_addr_set(&addr, v4, KS_DHT_DEFAULT_PORT + 1, AF_INET); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_bind(dht2, NULL, &addr, &ep2); + err = ks_dht_bind(dht2, &addr, &ep2); ok(err == KS_STATUS_SUCCESS); //raddr2 = addr; @@ -141,7 +145,7 @@ int main() { err = ks_addr_set(&addr, v4, KS_DHT_DEFAULT_PORT + 2, AF_INET); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_bind(dht3, NULL, &addr, &ep3); + err = ks_dht_bind(dht3, &addr, &ep3); ok(err == KS_STATUS_SUCCESS); //raddr3 = addr; @@ -151,19 +155,19 @@ int main() { err = ks_addr_set(&addr, v6, KS_DHT_DEFAULT_PORT, AF_INET6); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_bind(dht1, NULL, &addr, NULL); + err = ks_dht_bind(dht1, &addr, NULL); ok(err == KS_STATUS_SUCCESS); err = ks_addr_set(&addr, v6, KS_DHT_DEFAULT_PORT + 1, AF_INET6); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_bind(dht2, NULL, &addr, NULL); + err = ks_dht_bind(dht2, &addr, NULL); ok(err == KS_STATUS_SUCCESS); err = ks_addr_set(&addr, v6, KS_DHT_DEFAULT_PORT + 2, AF_INET6); ok(err == KS_STATUS_SUCCESS); - err = ks_dht_bind(dht3, NULL, &addr, NULL); + err = ks_dht_bind(dht3, &addr, NULL); ok(err == KS_STATUS_SUCCESS); } @@ -175,11 +179,11 @@ int main() { ks_dht_pulse(dht1, 100); // Receive and process ping query from dht2, queue and send ping response - ok(ks_dhtrt_find_node(dht1->rt_ipv4, ep2->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet + ok(ks_dhtrt_find_node(dht1->rt_ipv4, dht2->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet ks_dht_pulse(dht2, 100); // Receive and process ping response from dht1 (PROCESSING then COMPLETING) - ok(ks_dhtrt_find_node(dht2->rt_ipv4, ep1->nodeid) != NULL); // The node should be good, and thus be returned as good + ok(ks_dhtrt_find_node(dht2->rt_ipv4, dht1->nodeid) != NULL); // The node should be good, and thus be returned as good ks_dht_pulse(dht2, 100); // Call finish callback and purge the job (COMPLETING) @@ -189,7 +193,7 @@ int main() { ks_dht_pulse(dht2, 100); ks_dht_pulse(dht3, 100); } - ok(ks_dhtrt_find_node(dht1->rt_ipv4, ep2->nodeid) != NULL); // The node should be good by now, and thus be returned as good + ok(ks_dhtrt_find_node(dht1->rt_ipv4, dht2->nodeid) != NULL); // The node should be good by now, and thus be returned as good ks_dht_ping(dht3, &raddr1, NULL, NULL); // (QUERYING) @@ -198,11 +202,11 @@ int main() { ks_dht_pulse(dht1, 100); // Receive and process ping query from dht3, queue and send ping response - ok(ks_dhtrt_find_node(dht1->rt_ipv4, ep3->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet + ok(ks_dhtrt_find_node(dht1->rt_ipv4, dht3->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet ks_dht_pulse(dht3, 100); // Receive and process ping response from dht1 (PROCESSING then COMPLETING) - ok(ks_dhtrt_find_node(dht3->rt_ipv4, ep1->nodeid) != NULL); // The node should be good, and thus be returned as good + ok(ks_dhtrt_find_node(dht3->rt_ipv4, dht1->nodeid) != NULL); // The node should be good, and thus be returned as good ks_dht_pulse(dht3, 100); // Call finish callback and purge the job (COMPLETING) @@ -212,26 +216,26 @@ int main() { ks_dht_pulse(dht2, 100); ks_dht_pulse(dht3, 100); } - ok(ks_dhtrt_find_node(dht1->rt_ipv4, ep2->nodeid) != NULL); // The node should be good by now, and thus be returned as good + ok(ks_dhtrt_find_node(dht1->rt_ipv4, dht2->nodeid) != NULL); // The node should be good by now, and thus be returned as good // Test bootstrap find_node from dht3 to dht1 to find dht2 nodeid /* diag("Find_Node test\n"); - ks_dht_findnode(dht3, NULL, &raddr1, NULL, NULL, &ep2->nodeid); + ks_dht_findnode(dht3, NULL, &raddr1, NULL, NULL, &dht2->nodeid); ks_dht_pulse(dht3, 100); // Send queued findnode from dht3 to dht1 ks_dht_pulse(dht1, 100); // Receive and process findnode query from dht3, queue and send findnode response - ok(ks_dhtrt_find_node(dht1->rt_ipv4, ep3->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet + ok(ks_dhtrt_find_node(dht1->rt_ipv4, dht3->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet ks_dht_pulse(dht3, 100); // Receive and process findnode response from dht1 ks_dht_pulse(dht3, 100); // Call finish callback and purge the job (COMPLETING) - ok(ks_dhtrt_find_node(dht3->rt_ipv4, ep2->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet + ok(ks_dhtrt_find_node(dht3->rt_ipv4, dht2->nodeid) == NULL); // The node should be dubious, and thus not be returned as good yet diag("Pulsing for route table pings\n"); // Wait for route table pinging to catch up for (int i = 0; i < 10; ++i) { @@ -239,12 +243,12 @@ int main() { ks_dht_pulse(dht2, 100); ks_dht_pulse(dht3, 100); } - ok(ks_dhtrt_find_node(dht3->rt_ipv4, ep2->nodeid) != NULL); // The node should be good by now, and thus be returned as good + ok(ks_dhtrt_find_node(dht3->rt_ipv4, dht2->nodeid) != NULL); // The node should be good by now, and thus be returned as good */ diag("Search test\n"); - ks_dht_search(dht3, dht2_search_callback, NULL, dht3->rt_ipv4, &ep2->nodeid); + ks_dht_search(dht3, dht2_search_callback, NULL, dht3->rt_ipv4, &dht2->nodeid); diag("Pulsing for route table pings\n"); // Wait for route table pinging to catch up for (int i = 0; i < 20; ++i) { ks_dht_pulse(dht1, 100);