mirror of
https://github.com/cjdelisle/cjdns
synced 2025-10-06 00:32:50 +02:00
458 lines
14 KiB
C
458 lines
14 KiB
C
/* vim: set expandtab ts=4 sw=4: */
|
|
/*
|
|
* You may redistribute this program and/or modify it under the terms of
|
|
* the GNU General Public License as published by the Free Software Foundation,
|
|
* either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "benc/serialization/standard/BencMessageReader.h"
|
|
#include "benc/serialization/standard/BencMessageWriter.h"
|
|
#include "benc/serialization/json/JsonBencSerializer.h"
|
|
#include "benc/serialization/BencSerializer.h"
|
|
#include "benc/List.h"
|
|
#include "io/ArrayReader.h"
|
|
#include "admin/angel/Core.h"
|
|
#include "client/AdminClient.h"
|
|
#include "interface/ASynchronizer.h"
|
|
#include "interface/addressable/AddrIfaceAdapter.h"
|
|
#include "memory/Allocator.h"
|
|
#include "util/log/FileWriterLog.h"
|
|
#include "wire/Message.h"
|
|
#include "util/events/EventBase.h"
|
|
#include "crypto/random/Random.h"
|
|
#include "crypto/random/nanotime/NanotimeEntropyProvider.h"
|
|
#include "exception/Except.h"
|
|
#include "util/events/Timeout.h"
|
|
#include "crypto/Key.h"
|
|
#include "util/log/Log_impl.h"
|
|
#include "io/FileReader.h"
|
|
#include "io/ArrayWriter.h"
|
|
#include "util/Hex.h"
|
|
#include "util/events/FakeNetwork.h"
|
|
#include "util/Hash.h"
|
|
|
|
#include <sodium/crypto_scalarmult_curve25519.h>
|
|
|
|
#include <unistd.h> // isatty()
|
|
|
|
struct NodeContext {
|
|
struct Sockaddr* boundAddr;
|
|
struct Allocator* alloc;
|
|
struct EventBase* base;
|
|
uint8_t privateKey[32];
|
|
String* publicKey;
|
|
struct AdminClient* adminClient;
|
|
struct Admin* admin;
|
|
|
|
char* nodeName;
|
|
|
|
/** Admin socket to bind */
|
|
String* bind;
|
|
|
|
/** Admin password */
|
|
String* pass;
|
|
|
|
/** UDPInterface */
|
|
int ifNum;
|
|
struct Sockaddr* udpAddr;
|
|
|
|
struct Log nodeLog;
|
|
struct Log* parentLogger;
|
|
|
|
List* peers;
|
|
|
|
Identity
|
|
};
|
|
|
|
struct RPCCall;
|
|
|
|
typedef void (* RPCCallback)(struct RPCCall* call, struct AdminClient_Result* res);
|
|
|
|
struct RPCCall
|
|
{
|
|
String* func;
|
|
Dict* args;
|
|
struct NodeContext* node;
|
|
RPCCallback callback;
|
|
};
|
|
|
|
#define Map_KEY_TYPE String*
|
|
#define Map_VALUE_TYPE struct NodeContext*
|
|
#define Map_NAME OfNodes
|
|
#define Map_USE_COMPARATOR
|
|
#define Map_USE_HASH
|
|
#include "util/Map.h"
|
|
static inline int Map_OfNodes_compare(String** a, String** b)
|
|
{
|
|
return String_compare(*a, *b);
|
|
}
|
|
static inline uint32_t Map_OfNodes_hash(String** a)
|
|
{
|
|
return Hash_compute(a[0]->bytes, a[0]->len);
|
|
}
|
|
|
|
struct Context
|
|
{
|
|
struct RPCCall* rpcCalls;
|
|
int rpcCallCount;
|
|
int nextCall;
|
|
struct Allocator* rpcAlloc;
|
|
|
|
struct Random* rand;
|
|
struct EventBase* base;
|
|
struct Log* logger;
|
|
struct Allocator* alloc;
|
|
|
|
struct Map_OfNodes nodeMap;
|
|
Dict* confNodes;
|
|
|
|
Identity
|
|
};
|
|
|
|
static String* pubKeyForPriv(uint8_t* privateKey, struct Allocator* alloc)
|
|
{
|
|
uint8_t publicKey[32];
|
|
crypto_scalarmult_curve25519_base(publicKey, privateKey);
|
|
return Key_stringify(publicKey, alloc);
|
|
}
|
|
|
|
static void printLog(struct Log* log,
|
|
enum Log_Level logLevel,
|
|
const char* file,
|
|
int line,
|
|
const char* format,
|
|
va_list args)
|
|
{
|
|
struct NodeContext* ctx = Identity_check(
|
|
(struct NodeContext*) (((char*)log) - offsetof(struct NodeContext, nodeLog))
|
|
);
|
|
struct Allocator* alloc = Allocator_child(ctx->alloc);
|
|
String* str = String_printf(alloc, "[%s] %s", ctx->nodeName, file);
|
|
ctx->parentLogger->print(ctx->parentLogger, logLevel, str->bytes, line, format, args);
|
|
Allocator_free(alloc);
|
|
}
|
|
|
|
static struct RPCCall* pushCall(struct Context* ctx)
|
|
{
|
|
ctx->rpcCalls = Allocator_realloc(ctx->rpcAlloc,
|
|
ctx->rpcCalls,
|
|
sizeof(struct RPCCall) * (ctx->rpcCallCount+1));
|
|
Bits_memset(&ctx->rpcCalls[ctx->rpcCallCount], 0, sizeof(struct RPCCall));
|
|
return &ctx->rpcCalls[ctx->rpcCallCount++];
|
|
}
|
|
|
|
static void bindUDPCallback(struct RPCCall* call, struct AdminClient_Result* res)
|
|
{
|
|
Assert_true(!res->err);
|
|
// Indirection to shutup clang warning
|
|
struct Log* logger = &call->node->nodeLog;
|
|
Log_debug(logger, "UDPInterface_new() -> [%s]", res->messageBytes);
|
|
String* addr = Dict_getStringC(res->responseDict, "bindAddress");
|
|
int64_t* ifNum = Dict_getIntC(res->responseDict, "interfaceNumber");
|
|
struct Sockaddr_storage ss;
|
|
Assert_true(!Sockaddr_parse(addr->bytes, &ss));
|
|
call->node->ifNum = *ifNum;
|
|
call->node->udpAddr = Sockaddr_clone(&ss.addr, call->node->alloc);
|
|
}
|
|
|
|
static void bindUDP(struct Context* ctx, struct NodeContext* node)
|
|
{
|
|
struct RPCCall* call = pushCall(ctx);
|
|
call->func = String_new("UDPInterface_new", ctx->rpcAlloc);
|
|
call->args = Dict_new(ctx->rpcAlloc);
|
|
call->node = node;
|
|
call->callback = bindUDPCallback;
|
|
}
|
|
|
|
static void securitySetupComplete(struct Context* ctx, struct NodeContext* node)
|
|
{
|
|
struct RPCCall* call = pushCall(ctx);
|
|
call->func = String_new("Security_setupComplete", ctx->rpcAlloc);
|
|
call->args = Dict_new(ctx->rpcAlloc);
|
|
call->node = node;
|
|
}
|
|
|
|
static struct NodeContext* startNode(char* nodeName,
|
|
char* privateKeyHex,
|
|
Dict* admin,
|
|
struct Context* ctx,
|
|
struct Except* eh,
|
|
struct FakeNetwork* fakeNet)
|
|
{
|
|
struct Allocator* alloc = Allocator_child(ctx->alloc);
|
|
struct NodeContext* node = Allocator_clone(alloc, (&(struct NodeContext) {
|
|
.alloc = alloc,
|
|
.base = ctx->base,
|
|
.nodeLog = {
|
|
.print = printLog
|
|
},
|
|
.parentLogger = ctx->logger,
|
|
.nodeName = nodeName
|
|
}));
|
|
Identity_set(node);
|
|
|
|
node->bind = Dict_getStringC(admin, "bind");
|
|
if (!node->bind) {
|
|
node->bind = String_new("127.0.0.1:0", alloc);
|
|
}
|
|
node->pass = Dict_getStringC(admin, "password");
|
|
if (!node->pass) {
|
|
node->pass = String_new("x", alloc);
|
|
}
|
|
|
|
Assert_true(Hex_decode(node->privateKey, 32, privateKeyHex, 64) == 32);
|
|
|
|
struct AddrIfaceAdapter* adminClientIface = AddrIfaceAdapter_new(node->alloc);
|
|
struct AddrIfaceAdapter* adminIface = AddrIfaceAdapter_new(node->alloc);
|
|
struct ASynchronizer* asyncer = ASynchronizer_new(node->alloc, ctx->base, ctx->logger);
|
|
Iface_plumb(&asyncer->ifA, &adminClientIface->inputIf);
|
|
Iface_plumb(&asyncer->ifB, &adminIface->inputIf);
|
|
|
|
String* pass = String_new("12345", node->alloc);
|
|
node->adminClient = AdminClient_new(&adminClientIface->generic,
|
|
Sockaddr_clone(Sockaddr_LOOPBACK, node->alloc),
|
|
pass,
|
|
ctx->base,
|
|
&node->nodeLog,
|
|
node->alloc);
|
|
|
|
node->admin = Admin_new(&adminIface->generic, &node->nodeLog, ctx->base, pass);
|
|
|
|
Core_init(node->alloc,
|
|
&node->nodeLog,
|
|
ctx->base,
|
|
node->privateKey,
|
|
node->admin,
|
|
ctx->rand,
|
|
eh,
|
|
fakeNet,
|
|
true);
|
|
|
|
securitySetupComplete(ctx, node);
|
|
bindUDP(ctx, node);
|
|
node->publicKey = pubKeyForPriv(node->privateKey, node->alloc);
|
|
|
|
return node;
|
|
}
|
|
|
|
static void beginConnectionCallback(struct RPCCall* call, struct AdminClient_Result* res)
|
|
{
|
|
Assert_true(!res->err);
|
|
// Indirection to shutup clang warning
|
|
struct Log* logger = &call->node->nodeLog;
|
|
Log_debug(logger, "UDPInterface_beginConnection() -> [%s]", res->messageBytes);
|
|
}
|
|
|
|
static void linkNodes(struct Context* ctx, struct NodeContext* client, struct NodeContext* server)
|
|
{
|
|
Dict* addPasswordArgs = Dict_new(ctx->rpcAlloc);
|
|
String* clientStr = String_printf(ctx->rpcAlloc, "%ld", (long) (uintptr_t) client);
|
|
Dict_putString(addPasswordArgs,
|
|
String_new("password", ctx->rpcAlloc),
|
|
clientStr,
|
|
ctx->rpcAlloc);
|
|
Dict_putString(addPasswordArgs,
|
|
String_new("user", ctx->rpcAlloc),
|
|
clientStr,
|
|
ctx->rpcAlloc);
|
|
struct RPCCall* addPasswordCall = pushCall(ctx);
|
|
addPasswordCall->func = String_new("AuthorizedPasswords_add", ctx->rpcAlloc);
|
|
addPasswordCall->args = addPasswordArgs;
|
|
addPasswordCall->node = server;
|
|
|
|
// client
|
|
Dict* beginConnectionArgs = Dict_new(ctx->rpcAlloc);
|
|
Dict_putInt(beginConnectionArgs,
|
|
String_new("interfaceNumber", ctx->rpcAlloc),
|
|
client->ifNum,
|
|
ctx->rpcAlloc);
|
|
Dict_putString(beginConnectionArgs,
|
|
String_new("password", ctx->rpcAlloc),
|
|
clientStr,
|
|
ctx->rpcAlloc);
|
|
Dict_putString(beginConnectionArgs,
|
|
String_new("publicKey", ctx->rpcAlloc),
|
|
server->publicKey,
|
|
ctx->rpcAlloc);
|
|
char* udpAddr = Sockaddr_print(server->udpAddr, ctx->rpcAlloc);
|
|
Dict_putString(beginConnectionArgs,
|
|
String_new("address", ctx->rpcAlloc),
|
|
String_new(udpAddr, ctx->rpcAlloc),
|
|
ctx->rpcAlloc);
|
|
|
|
Log_info(ctx->logger, "Linking [%s] with [%s/%s]",
|
|
client->nodeName, server->nodeName, udpAddr);
|
|
|
|
struct RPCCall* connectCall = pushCall(ctx);
|
|
connectCall->func = String_new("UDPInterface_beginConnection", ctx->rpcAlloc);
|
|
connectCall->args = beginConnectionArgs;
|
|
connectCall->node = client;
|
|
connectCall->callback = beginConnectionCallback;
|
|
}
|
|
|
|
static void linkAllNodes(struct Context* ctx)
|
|
{
|
|
int i = 0;
|
|
String* key = NULL;
|
|
Dict_forEach(ctx->confNodes, key) {
|
|
int nodeIdx = Map_OfNodes_indexForKey(&key, &ctx->nodeMap);
|
|
Assert_true(nodeIdx >= 0);
|
|
struct NodeContext* nc = ctx->nodeMap.values[nodeIdx];
|
|
List* connectTo = nc->peers;
|
|
for (int j = 0; j < List_size(connectTo); j++) {
|
|
String* server = List_getString(connectTo, j);
|
|
Assert_true(server);
|
|
int nodeIdxB = Map_OfNodes_indexForKey(&server, &ctx->nodeMap);
|
|
Assert_true(nodeIdxB >= 0);
|
|
struct NodeContext* ncB = ctx->nodeMap.values[nodeIdxB];
|
|
linkNodes(ctx, nc, ncB);
|
|
}
|
|
i++;
|
|
}
|
|
ctx->confNodes = NULL;
|
|
}
|
|
|
|
static void startRpc(void* vcontext);
|
|
|
|
static void rpcCallback(struct AdminClient_Promise* promise, struct AdminClient_Result* res)
|
|
{
|
|
struct Context* ctx = promise->userData;
|
|
Identity_check(ctx);
|
|
|
|
struct RPCCall* thisCall = &ctx->rpcCalls[ctx->nextCall];
|
|
if (thisCall->callback) {
|
|
thisCall->callback(thisCall, res);
|
|
}
|
|
|
|
ctx->nextCall++;
|
|
startRpc(ctx);
|
|
}
|
|
|
|
static void startRpc(void* vcontext)
|
|
{
|
|
struct Context* ctx = vcontext;
|
|
Identity_check(ctx);
|
|
|
|
if (ctx->nextCall >= ctx->rpcCallCount) {
|
|
if (ctx->confNodes) {
|
|
linkAllNodes(ctx);
|
|
}
|
|
}
|
|
if (ctx->nextCall >= ctx->rpcCallCount) {
|
|
Log_info(ctx->logger, "\n\nCompleted setting up simulation\n\n");
|
|
Allocator_free(ctx->rpcAlloc);
|
|
ctx->rpcAlloc = NULL;
|
|
ctx->rpcCalls = NULL;
|
|
ctx->rpcCallCount = 0;
|
|
return;
|
|
}
|
|
|
|
struct RPCCall* nextCall = &ctx->rpcCalls[ctx->nextCall];
|
|
struct AdminClient_Promise* promise = AdminClient_rpcCall(nextCall->func,
|
|
nextCall->args,
|
|
nextCall->node->adminClient,
|
|
ctx->rpcAlloc);
|
|
promise->callback = rpcCallback;
|
|
promise->userData = ctx;
|
|
}
|
|
|
|
static void letErRip(Dict* config, struct Allocator* alloc)
|
|
{
|
|
struct Except* eh = NULL;
|
|
struct Log* logger = FileWriterLog_new(stdout, alloc);
|
|
struct EventBase* base = EventBase_new(alloc);
|
|
struct Random* rand = NanotimeEntropyProvider_newDefaultRandom(base, logger, eh, alloc);
|
|
Allocator_setCanary(alloc, (uintptr_t)Random_uint64(rand));
|
|
|
|
struct Context sctx = {
|
|
.rpcAlloc = Allocator_child(alloc),
|
|
|
|
.logger = logger,
|
|
.base = base,
|
|
.rand = rand,
|
|
.alloc = alloc,
|
|
.nodeMap = {
|
|
.allocator = alloc
|
|
}
|
|
};
|
|
struct Context* ctx = &sctx;
|
|
Identity_set(ctx);
|
|
|
|
ctx->confNodes = Dict_getDictC(config, "nodes");
|
|
|
|
struct FakeNetwork* fakeNet = FakeNetwork_new(base, alloc, logger);
|
|
|
|
String* key = NULL;
|
|
Dict_forEach(ctx->confNodes, key) {
|
|
Dict* val = Dict_getDict(ctx->confNodes, key);
|
|
String* privateKeyHex = Dict_getStringC(val, "privateKey");
|
|
Dict* admin = Dict_getDictC(val, "admin");
|
|
struct NodeContext* nc =
|
|
startNode(key->bytes, privateKeyHex->bytes, admin, ctx, eh, fakeNet);
|
|
nc->peers = Dict_getListC(val, "peers");
|
|
Map_OfNodes_put(&key, &nc, &ctx->nodeMap);
|
|
}
|
|
|
|
Log_info(ctx->logger, "\n\nAll nodes initialized\n\n");
|
|
|
|
// begin the chain of RPC calls which sets up the net
|
|
Timeout_setTimeout(startRpc, ctx, 0, base, ctx->rpcAlloc);
|
|
|
|
EventBase_beginLoop(base);
|
|
|
|
Allocator_free(alloc);
|
|
}
|
|
|
|
static int usage(char* appName)
|
|
{
|
|
printf("Example usage: %s < config.json\n"
|
|
"Example config:\n"
|
|
"{\n"
|
|
" \"nodes\": {\n"
|
|
" \"alice\": {\n"
|
|
" \"privateKey\": "
|
|
"\"5e2295679394e5e1db67c238abbc10292ad9b127904394c52cc5fff39383e920\",\n"
|
|
" \"peers\": []\n"
|
|
" },\n"
|
|
" \"bob\": {\n"
|
|
" \"privateKey\": "
|
|
"\"6569bf3f0d168faa6dfb2912f8ee5ee9b938319e97618fdf06caed73b1aad1cc\",\n"
|
|
" \"peers\": [\n"
|
|
" \"alice\"\n"
|
|
" ]\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n", appName);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This is invoked from sybilsim.rs
|
|
int sybilsim_main(int argc, char** argv);
|
|
int sybilsim_main(int argc, char** argv)
|
|
{
|
|
Assert_true(argc > 0);
|
|
if (isatty(STDIN_FILENO)) {
|
|
return usage(argv[0]);
|
|
}
|
|
|
|
struct Allocator* alloc = Allocator_new(1LL<<31);
|
|
|
|
struct Reader* stdinReader = FileReader_new(stdin, alloc);
|
|
Dict config;
|
|
if (JsonBencSerializer_get()->parseDictionary(stdinReader, alloc, &config)) {
|
|
fprintf(stderr, "Failed to parse configuration.\n");
|
|
return -1;
|
|
}
|
|
|
|
letErRip(&config, alloc);
|
|
return 0;
|
|
}
|