ddat interop with dat js cable interop with cabal js ddat's frontend = dat-thang cable fe = cable-thang dillo support based on cel's dillo-dat bootstrap node xplatform support Signed-off-by: divo <418@cryspace.space>
235 lines
9.3 KiB
C
235 lines
9.3 KiB
C
#include "cesspool.h"
|
|
#include "dat_wire.h"
|
|
#include "sleep_crypto.h"
|
|
#include "sleep.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#include <sodium.h>
|
|
#include <string.h>
|
|
#ifndef _WIN32
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
typedef SOCKET test_socket_t;
|
|
#else
|
|
#include <unistd.h>
|
|
typedef int test_socket_t;
|
|
#endif
|
|
|
|
static uint16_t free_udp_port(void)
|
|
{
|
|
test_socket_t fd=socket(AF_INET,SOCK_DGRAM,0);struct sockaddr_in address;
|
|
uint16_t port;
|
|
#ifdef _WIN32
|
|
int length=sizeof(address);
|
|
assert(fd!=INVALID_SOCKET);
|
|
#else
|
|
socklen_t length=sizeof(address);
|
|
assert(fd>=0);
|
|
#endif
|
|
memset(&address,0,sizeof(address));address.sin_family=AF_INET;address.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
|
|
assert(bind(fd,(struct sockaddr*)&address,sizeof(address))==0);assert(getsockname(fd,(struct sockaddr*)&address,&length)==0);
|
|
port=ntohs(address.sin_port);
|
|
#ifdef _WIN32
|
|
closesocket(fd);
|
|
#else
|
|
close(fd);
|
|
#endif
|
|
return port;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
static const uint8_t hypercore[] = "hypercore";
|
|
static const uint8_t namespace[] = DAT_NETWORK_PROTOCOL;
|
|
uint8_t key[32], expected[32], actual[32], nonce[24], input[513];
|
|
uint8_t whole[513], chunked[513];
|
|
memset(key, 0x42, sizeof(key));
|
|
#ifdef _WIN32
|
|
{
|
|
WSADATA winsock;
|
|
assert(WSAStartup(MAKEWORD(2, 2), &winsock) == 0);
|
|
}
|
|
#endif
|
|
assert(crypto_generichash(expected, sizeof(expected), hypercore,
|
|
sizeof(hypercore) - 1, key, sizeof(key)) == 0);
|
|
assert(sleep_compute_discovery_key(key, actual) == 0);
|
|
assert(!memcmp(expected, actual, sizeof(actual)));
|
|
/* Generated by extra/dillo-dat/node_modules/hypercore-crypto for a
|
|
* 32-byte feed key filled with 0x42 (historical Dat v6/v7 stack). */
|
|
{
|
|
static const uint8_t js_hypercore_discovery_key[32] = {
|
|
0xb0,0xfa,0xa6,0x16,0x21,0x2d,0x99,0x23,
|
|
0x9f,0x50,0x7a,0x68,0x8f,0x50,0xcd,0x60,
|
|
0xe6,0x51,0x4d,0xfb,0x62,0xae,0xf4,0xfc,
|
|
0xfc,0x82,0x59,0xd6,0x46,0x96,0x35,0x91
|
|
};
|
|
assert(!memcmp(actual, js_hypercore_discovery_key, sizeof(actual)));
|
|
}
|
|
memset(nonce, 0x19, sizeof(nonce));
|
|
for (size_t i = 0; i < sizeof(input); i++) input[i] = (uint8_t)(i * 37u + 11u);
|
|
assert(sleep_stream_xsalsa20_xor(whole, input, sizeof(input), nonce, key, 0) == 0);
|
|
{
|
|
size_t offset = 0;
|
|
static const size_t pieces[] = {1, 63, 64, 65, 127, 193};
|
|
for (size_t i = 0; i < sizeof(pieces) / sizeof(pieces[0]); i++) {
|
|
assert(sleep_stream_xsalsa20_xor(chunked + offset, input + offset,
|
|
pieces[i], nonce, key, offset) == 0);
|
|
offset += pieces[i];
|
|
}
|
|
assert(offset == sizeof(input));
|
|
}
|
|
assert(!memcmp(whole, chunked, sizeof(whole)));
|
|
{
|
|
cesspool_t transactions;
|
|
uint16_t id;
|
|
memset(&transactions, 0, sizeof(transactions));
|
|
assert(cesspool_transaction_begin(&transactions, CESSPOOL_LOOKUP, key,
|
|
100, &id) == 0);
|
|
assert(cesspool_transaction_finish(&transactions, id) == 0);
|
|
assert(cesspool_transaction_begin(&transactions, CESSPOOL_PING, key,
|
|
10, &id) == 0);
|
|
assert(cesspool_tick(&transactions, 10) == 0);
|
|
errno = 0;
|
|
assert(cesspool_transaction_finish(&transactions, id) != 0 && errno == ENOENT);
|
|
}
|
|
assert(crypto_generichash(expected, sizeof(expected), namespace,
|
|
sizeof(namespace) - 1, key, sizeof(key)) == 0);
|
|
assert(cesspool_discovery_key(key, actual) == 0);
|
|
assert(!memcmp(expected, actual, sizeof(actual)));
|
|
{
|
|
cesspool_t relay, publisher, lookup;
|
|
cesspool_peer_t *peers[4];
|
|
uint8_t relay_id[32] = {0x10}, publisher_id[32] = {0x11}, lookup_id[32] = {0x22};
|
|
uint8_t topic[32] = {0x33}, archive_topic_a[32] = {0x34};
|
|
uint8_t archive_topic_b[32] = {0x35};
|
|
uint8_t packet[80];
|
|
cesspool_message_t message, decoded;
|
|
size_t packet_len;
|
|
uint16_t relay_port = free_udp_port(), publisher_port = free_udp_port(), lookup_port = free_udp_port();
|
|
assert(cesspool_init(&relay, relay_id) == 0);
|
|
assert(cesspool_init(&publisher, publisher_id) == 0);
|
|
assert(cesspool_init(&lookup, lookup_id) == 0);
|
|
assert(cesspool_listen(&relay, "localhost", relay_port) == 0);
|
|
assert(cesspool_listen(&publisher, "localhost", publisher_port) == 0);
|
|
assert(cesspool_listen(&lookup, "localhost", lookup_port) == 0);
|
|
assert(relay_port && publisher_port && lookup_port && relay_port != publisher_port && relay_port != lookup_port && publisher_port != lookup_port);
|
|
assert(cesspool_set_bootstrap_server(&relay, 1) == 0);
|
|
assert(cesspool_add_bootstrap(&publisher, "localhost", relay_port) == 0);
|
|
assert(cesspool_add_bootstrap(&lookup, "127.0.0.1", relay_port) == 0);
|
|
/* A third party's REPLY packet must not mark the configured node
|
|
* online or inject forged rendezvous entries. */
|
|
{
|
|
cesspool_message_t forged;
|
|
uint8_t forged_packet[80];
|
|
size_t forged_length;
|
|
struct sockaddr_in *target;
|
|
dat_peer_t forged_target;
|
|
memset(&forged, 0, sizeof(forged));
|
|
forged.type = CESSPOOL_REPLY;
|
|
memcpy(forged.peer_id, publisher_id, sizeof(publisher_id));
|
|
memcpy(forged.topic, topic, sizeof(topic));
|
|
forged.payload[0] = 0x1a; /* malformed one-byte peer record */
|
|
forged.payload[1] = 0x01;
|
|
forged.payload[2] = 0xff;
|
|
forged.payload_len = 3;
|
|
assert(cesspool_encode(&forged, forged_packet, sizeof(forged_packet), &forged_length) == 0);
|
|
memset(&forged_target, 0, sizeof(forged_target));
|
|
target = (struct sockaddr_in *)&forged_target.addr;
|
|
target->sin_family = AF_INET;
|
|
target->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
target->sin_port = htons(lookup_port);
|
|
forged_target.addrlen = sizeof(*target);
|
|
assert(datnet_send(&publisher.net, &forged_target, forged_packet, forged_length) == 0);
|
|
assert(cesspool_poll(&lookup, 100) >= 0);
|
|
assert(!lookup.bootstrap_reachable && lookup.announcement_count == 0);
|
|
}
|
|
assert(cesspool_join(&publisher, topic, publisher_port, 1, 0) == 0);
|
|
assert(cesspool_poll(&relay, 100) >= 0);
|
|
/* One client identity may announce a room and multiple archives. The
|
|
* rendezvous counters must report one peer across three distinct topics. */
|
|
assert(cesspool_join(&publisher, archive_topic_a, publisher_port, 1, 0) == 0);
|
|
assert(cesspool_poll(&relay, 100) >= 0);
|
|
assert(cesspool_join(&publisher, archive_topic_b, publisher_port, 1, 0) == 0);
|
|
assert(cesspool_poll(&relay, 100) >= 0);
|
|
assert(cesspool_announced_peer_count(&relay) == 1);
|
|
assert(cesspool_announced_topic_count(&relay) == 3);
|
|
assert(cesspool_join(&lookup, topic, lookup_port, 0, 1) == 0);
|
|
assert(cesspool_poll(&relay, 100) >= 0);
|
|
assert(cesspool_poll(&lookup, 100) >= 0);
|
|
assert(lookup.bootstrap_reachable);
|
|
assert(cesspool_topic_peers(&lookup, topic, peers, 4) >= 1);
|
|
assert(!memcmp(peers[0]->public_key, publisher_id, 32));
|
|
/* Expiring the lookup record must clear the client's Discovered row. */
|
|
for (size_t a = 0; a < lookup.announcement_count; a++)
|
|
lookup.announcements[a].last_seen = 0;
|
|
lookup.bootstrap_count = 0;
|
|
for (size_t t = 0; t < lookup.topic_count; t++)
|
|
lookup.topics[t].refresh_at = UINT64_MAX;
|
|
assert(cesspool_tick(&lookup, 0) == 0);
|
|
assert(cesspool_topic_peers(&lookup, topic, peers, 4) == 0);
|
|
assert(!cesspool_find_peer(&lookup, publisher_id)->discovered);
|
|
assert(cesspool_leave(&publisher, archive_topic_a) == 0);
|
|
assert(cesspool_poll(&relay, 100) >= 0);
|
|
assert(cesspool_announced_peer_count(&relay) == 1);
|
|
assert(cesspool_announced_topic_count(&relay) == 2);
|
|
assert(cesspool_tick(&relay, UINT64_MAX) == 0);
|
|
assert(cesspool_announced_peer_count(&relay) == 1); /* Fresh announcements keep it live. */
|
|
memset(&message, 0, sizeof(message));
|
|
message.type = CESSPOOL_PING;
|
|
assert(cesspool_encode(&message, packet, sizeof(packet), &packet_len) == 0);
|
|
assert(packet_len == 72);
|
|
assert(cesspool_decode(&decoded, packet, packet_len) == 0);
|
|
assert(decoded.type == CESSPOOL_PING);
|
|
assert(cesspool_decode(&decoded, packet, packet_len + 1) != 0);
|
|
memset(&message, 0, sizeof(message));
|
|
message.type = CESSPOOL_REPLY;
|
|
message.payload_len = sizeof(message.payload);
|
|
assert(cesspool_encode(&message, packet, sizeof(packet), &packet_len) != 0);
|
|
assert(cesspool_leave(&publisher, topic) == 0);
|
|
assert(cesspool_leave(&publisher, archive_topic_b) == 0);
|
|
assert(cesspool_leave(&lookup, topic) == 0);
|
|
assert(cesspool_destroy(&relay) == 0);
|
|
assert(cesspool_destroy(&publisher) == 0);
|
|
assert(cesspool_destroy(&lookup) == 0);
|
|
}
|
|
{
|
|
cesspool_t routing;
|
|
cesspool_response_t response;
|
|
cesspool_peer_t *closest[4];
|
|
uint8_t local_prefix[2] = {192, 168}, local_id[32] = {0};
|
|
struct sockaddr_in *address;
|
|
memset(&response, 0, sizeof(response));
|
|
response.peers_len = 6;
|
|
response.peers[0] = 127; response.peers[3] = 1;
|
|
response.peers[4] = 0x0c; response.peers[5] = 0xd2;
|
|
response.local_peers_len = 4;
|
|
response.local_peers[0] = 10; response.local_peers[1] = 1;
|
|
response.local_peers[2] = 0x0c; response.local_peers[3] = 0xd2;
|
|
assert(cesspool_init(&routing, local_id) == 0);
|
|
assert(cesspool_process_response(&routing, &response, local_prefix) == 0);
|
|
assert(routing.peer_count == 2 && routing.peers[1].local);
|
|
address = (struct sockaddr_in *)&routing.peers[1].address.addr;
|
|
assert(ntohs(address->sin_port) == 3282);
|
|
assert(((uint8_t *)&address->sin_addr)[0] == 192 &&
|
|
((uint8_t *)&address->sin_addr)[1] == 168 &&
|
|
((uint8_t *)&address->sin_addr)[2] == 10 &&
|
|
((uint8_t *)&address->sin_addr)[3] == 1);
|
|
assert(cesspool_closest(&routing, local_id, closest, 4) == 2);
|
|
assert(cesspool_destroy(&routing) == 0);
|
|
}
|
|
#ifdef _WIN32
|
|
WSACleanup();
|
|
#endif
|
|
return 0;
|
|
}
|