0
0
mirror of https://github.com/cjdelisle/cjdns synced 2025-10-06 00:32:50 +02:00
Files
cjdns/subnode/PeeringSeeder.c

303 lines
9.3 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 "subnode/PeeringSeeder.h"
#include "benc/Dict.h"
#include "benc/String.h"
#include "crypto/Ca.h"
#include "dht/Address.h"
#include "exception/Err.h"
#include "memory/Allocator.h"
#include "net/SwitchPinger.h"
#include "rust/cjdns_sys/Rffi.h"
#include "subnode/ReachabilityCollector.h"
#include "util/Assert.h"
#include "util/Bits.h"
#include "util/Identity.h"
#include "util/events/EventBase.h"
#include "util/events/Time.h"
#include "util/events/Timeout.h"
#include "util/log/Log.h"
typedef struct PeeringSeeder_pvt {
PeeringSeeder_t pub;
struct SwitchPinger* sp;
struct ReachabilityCollector* rc;
Allocator_t* alloc;
Log_t* log;
struct MsgCore* mc;
Ca_t* ca;
Rffi_Seeder* seeder;
bool active;
char login[24];
// Our peering credentials
uint16_t userNum;
// 8 random bytes used to derive the password
uint64_t pass;
int lastPeerQueried;
uint64_t lastQueryTimeSec;
struct SwitchPinger_Ping* requestOutstanding;
struct Address snode;
uint64_t lastPostTimeSec;
struct MsgCore_Promise* snodePost;
Identity
} PeeringSeeder_pvt_t;
void* PeeringSeeder_getRsSeeder(PeeringSeeder_t* self)
{
PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
return pq->seeder;
}
static void ipQueryResp(struct SwitchPinger_Response* resp, void* userData)
{
PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) userData);
if (resp->ping != pq->requestOutstanding) {
Log_info(pq->log, "Ping does not match requestOutstanding");
}
pq->requestOutstanding = NULL;
char* err = "";
switch (resp->res) {
case SwitchPinger_Result_OK:
if (Rffi_Seeder_got_lladdr(pq->seeder, &resp->lladdr)) {
// Since there was a change, post ASAP.
pq->lastPostTimeSec = 0;
}
return;
case SwitchPinger_Result_LABEL_MISMATCH: err = "LABEL_MISMATCH"; break;
case SwitchPinger_Result_WRONG_DATA: err = "WRONG_DATA"; break;
case SwitchPinger_Result_ERROR_RESPONSE: err = "ERROR_RESPONSE"; break;
case SwitchPinger_Result_LOOP_ROUTE: err = "LOOP_ROUTE"; break;
case SwitchPinger_Result_TIMEOUT: err = "TIMEOUT"; break;
default: err = "unknown error"; break;
}
Log_debug(pq->log, "Error sending LLADDR query to peer [%llx] [%s]",
(long long)resp->label, err);
}
static void publicIPQuery(PeeringSeeder_pvt_t* pq) {
if (pq->requestOutstanding) {
return;
}
uint64_t now = Time_currentTimeSeconds();
if (pq->lastQueryTimeSec + 10 > now) {
Log_debug(pq->log, "Last query within 10 seconds");
return;
}
if (Rffi_Seeder_has_lladdr(pq->seeder)) {
if (pq->lastQueryTimeSec + 300 > now) {
Log_debug(pq->log, "Last query within 5 minutes and we have our lladdr");
return;
}
}
int peerCount = ReachabilityCollector_peerCount(pq->rc);
if (!peerCount) {
Log_debug(pq->log, "No peers");
return;
}
int tryPeerN = (pq->lastPeerQueried + 1) % peerCount;
pq->lastPeerQueried = tryPeerN;
struct ReachabilityCollector_PeerInfo* tryPeer =
ReachabilityCollector_getPeerInfo(pq->rc, tryPeerN);
struct SwitchPinger_Ping* q = SwitchPinger_newPing(
tryPeer->addr.path,
NULL,
10000,
ipQueryResp,
pq->alloc,
pq->sp);
q->onResponseContext = pq;
q->type = SwitchPinger_Type_LLADDR;
pq->requestOutstanding = q;
String_t* peerAddr = Address_toString(&tryPeer->addr, q->pingAlloc);
Log_debug(pq->log, "Sent LlAddr query to [%s]", peerAddr->bytes);
}
static void snodeResp(Dict* msg, Gcc_UNUSED struct Address* src, struct MsgCore_Promise* prom)
{
PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) prom->userData);
String* peers = Dict_getStringC(msg, "pr");
if (!peers) {
Log_debug(pq->log, "Got snode reply with no peers");
return;
}
Rffi_Seeder_got_peers(pq->seeder, peers, prom->alloc);
}
static void cycle(void* vpq)
{
PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) vpq);
if (!pq->active) {
return;
}
// Log_debug(pq->log, "cycle()");
// Try to get our IP if we can
publicIPQuery(pq);
uint64_t now = Time_currentTimeSeconds();
if (pq->lastPostTimeSec + 5*60 > now) {
// Only post to the snode every 5 minutes (or on new news)
return;
}
if (!Rffi_Seeder_has_lladdr(pq->seeder)) {
return;
}
if (Bits_isZero(&pq->snode, sizeof pq->snode)) {
return;
}
struct MsgCore_Promise* snodeQuery = MsgCore_createQuery(pq->mc, 0, pq->alloc);
snodeQuery->cb = snodeResp;
snodeQuery->userData = pq;
snodeQuery->target = Address_clone(&pq->snode, snodeQuery->alloc);
String* creds = NULL;
Err_assert(Rffi_Seeder_mk_creds(pq->seeder, &creds, snodeQuery->alloc));
snodeQuery->msg = Dict_new(snodeQuery->alloc);
Dict_putStringCC(snodeQuery->msg, "sq", "pc", snodeQuery->alloc);
Dict_putStringC(snodeQuery->msg, "pc", creds, snodeQuery->alloc);
pq->snodePost = snodeQuery;
Log_debug(pq->log, "Posting our public IP addresses to snode");
}
void PeeringSeeder_setSnode(PeeringSeeder_t* self, struct Address* snode)
{
PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
struct Address esnode = { .path = 0 };
if (snode != NULL) {
Bits_memcpy(&esnode, snode, sizeof esnode);
}
if (Bits_memcmp(&esnode, &pq->snode, sizeof esnode)) {
uint8_t fromAddr[60];
Address_print(fromAddr, &pq->snode);
if (snode == NULL) {
Log_debug(pq->log, "Snode [%s] was lost", fromAddr);
} else {
uint8_t toAddr[60];
Address_print(toAddr, &pq->snode);
if (Bits_isZero(&pq->snode, sizeof pq->snode)) {
Log_debug(pq->log, "Snode discovered [%s]", toAddr);
} else {
Log_debug(pq->log, "Snode was changed [%s] -> [%s]", fromAddr, toAddr);
}
}
pq->snode = esnode;
}
}
Err_DEFUN PeeringSeeder_publicStatus(PeeringSeeder_PublicStatus_t** outP, PeeringSeeder_t* self, Allocator_t* alloc)
{
PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
PeeringSeeder_PublicStatus_t* out = Allocator_calloc(alloc, sizeof(PeeringSeeder_PublicStatus_t), 1);
out->active = pq->active;
if (!Bits_isZero(&pq->snode, sizeof pq->snode)) {
out->snode = Address_toString(&pq->snode, alloc);
}
Err(Rffi_Seeder_public_status(
pq->seeder, &out->ipv4, &out->ipv6, &out->peerId, alloc));
*outP = out;
return NULL;
}
Err_DEFUN PeeringSeeder_publicPeer(
PeeringSeeder_t* self,
String_t* code,
String_t* addr4,
String_t* addr6,
Allocator_t* reqAlloc)
{
PeeringSeeder_pvt_t* pq = Identity_check((PeeringSeeder_pvt_t*) self);
if (code == NULL) {
if (pq->active) {
String_t* s = String_new(pq->login, reqAlloc);
Ca_removeUsers(pq->ca, s);
}
return NULL;
}
if (code->len > PeeringSeeder_publicPeer_ID_MAX_LEN) {
Err_raise(reqAlloc, "PeerID is too long, max length: [%d]",
PeeringSeeder_publicPeer_ID_MAX_LEN);
}
String_t* user = NULL;
String_t* pass = NULL;
Err(Rffi_Seeder_public_peer(
pq->seeder,
&user,
&pass,
pq->userNum,
pq->pass,
code,
addr4,
addr6,
reqAlloc));
Assert_true(user->len < sizeof pq->login);
Bits_memset(pq->login, 0, sizeof pq->login);
Bits_memcpy(pq->login, user->bytes, user->len);
Assert_true(pq->login[sizeof pq->login - 1] == 0);
Ca_addUser(pass, user, pq->ca);
pq->active = true;
return NULL;
}
struct PeeringSeeder* PeeringSeeder_new(
struct SwitchPinger* sp,
struct ReachabilityCollector* rc,
Allocator_t* alloc,
Log_t* log,
struct MsgCore* mc,
EventBase_t* base,
Ca_t* ca)
{
PeeringSeeder_pvt_t* out = Allocator_calloc(alloc, sizeof(PeeringSeeder_pvt_t), 1);
Identity_set(out);
out->sp = sp;
out->rc = rc;
out->alloc = alloc;
out->log = log;
out->mc = mc;
out->ca = ca;
uint8_t myPubKey[32];
Ca_getPubKey(ca, myPubKey);
Rffi_Seeder_new(&out->seeder, &out->pub.seederIface, myPubKey, alloc);
// Random (stable across restarts) user num and passwd
uint8_t secretBuf[64];
Assert_true(!Ca_getSecret(ca, String_CONST("SEED_PASSWD"), secretBuf));
Bits_memcpy(&out->pass, secretBuf, sizeof out->pass);
Bits_memcpy(&out->userNum, &secretBuf[8], sizeof out->userNum);
Timeout_setInterval(cycle, out, 3000, base, alloc);
return &out->pub;
}