0
0
mirror of https://github.com/cjdelisle/cjdns synced 2025-10-05 16:22:54 +02:00

Allow hard-coding of device public IP in case that it might not be detectable

This commit is contained in:
Caleb James DeLisle
2024-11-05 12:20:17 +00:00
parent 93d2033184
commit 4be793cb0b
9 changed files with 155 additions and 17 deletions

View File

@@ -432,12 +432,29 @@ static void seeders(List* seeders, struct Allocator* tempAlloc, struct Context*
}
}
static void publicPeer(String_t* peerID, Allocator_t* tempAlloc, struct Context* ctx)
static void publicPeer(Dict_t* routerConf, Allocator_t* tempAlloc, struct Context* ctx)
{
String_t* peerID = NULL;
String_t* ipv4 = NULL;
String_t* ipv6 = NULL;
Dict_t* peerInfo = Dict_getDictC(routerConf, "publicPeer");
if (peerInfo) {
peerID = Dict_getStringC(peerInfo, "id");
ipv4 = Dict_getStringC(peerInfo, "ipv4");
ipv6 = Dict_getStringC(peerInfo, "ipv6");
} else {
peerID = Dict_getStringC(routerConf, "publicPeer");
}
if (!peerID) { return; }
Log_debug(ctx->logger, "Setting PeerID [%s]", peerID->bytes);
Dict* reqDict = Dict_new(tempAlloc);
Dict_putStringC(reqDict, "peerID", peerID, tempAlloc);
if (ipv4) {
Dict_putStringC(reqDict, "ipv4", ipv4, tempAlloc);
}
if (ipv6) {
Dict_putStringC(reqDict, "ipv6", ipv6, tempAlloc);
}
rpcCall0(String_CONST("PeeringSeeder_publicPeer"), reqDict, ctx, tempAlloc, NULL, true);
}
@@ -448,7 +465,7 @@ static void routerConfig(Dict* routerConf, struct Allocator* tempAlloc, struct C
ipTunnel(Dict_getDictC(routerConf, "ipTunnel"), tempAlloc, ctx);
supernodes(Dict_getListC(routerConf, "supernodes"), tempAlloc, ctx);
seeders(Dict_getListC(routerConf, "dnsSeeds"), tempAlloc, ctx);
publicPeer(Dict_getStringC(routerConf, "publicPeer"), tempAlloc, ctx);
publicPeer(routerConf, tempAlloc, ctx);
}
static void ethInterfaceSetBeacon(int ifNum, Dict* eth, struct Context* ctx)

View File

@@ -262,15 +262,37 @@ static int genconf(struct Allocator* alloc, struct Random* rand, bool eth, bool
" \"seed.cjdns.fr\"\n"
" ],\n"
"\n"
" // When publicPeer is set, this node will post its public peering credentials\n"
" // When publicPeer id is set, this node will post its public peering credentials\n"
" // to its supernode. The specified peerID will be used to identify itself.\n"
" // For PKT yielding this must be set to the registered peerID, otherwise\n"
" // you can set it to anything. By *convention*, peerIDs that begin with \"PUB_\"\n"
" // are shared publicly and those which do not are tested by the snode but not\n"
" // shared, allowing you to use the snode's peer tester on an otherwise private\n"
" // node.\n"
" // node. If you leave \"id\" commented, your peering credentials will remain\n"
" // entirely private.\n"
" //\n"
" // \"publicPeer\": \"PUB_XXX\",\n"
" \"publicPeer\": {\n"
" // \"id\": \"PUB_XXX\",\n"
"\n"
" // If you set the public peer, you may also hardcode the IPv4 address.\n"
" // By default, cjdns will request its public IP address from its peers, but\n"
" // in cases with non-standard routing, you may have a different IP address\n"
" // for traffic initiated from outside. In this case, you must manually enter\n"
" // the IP address. If the address is entered in the form of \"x.x.x.x\", then\n"
" // the IP address will be used, but the port will be detected. If it is entered\n"
" // as \"0.0.0.0:xxx\" then the port will be used, but the address will be detected\n"
" // finally, if it is in the form of \"x.x.x.x:xxx\" then the address AND port will\n"
" // be used.\n"
" //\n"
" // \"ipv4\": \"1.2.3.4:56789\",\n"
"\n"
" // If you have a public IPv6 address which cannot be detected, you may hard-code\n"
" // it here. The same rules apply as IPv4 addresses: \"xxxx:xxxx::\" means use ip\n"
" // but detect port. \"[::]:xxx\" means use port but detect ip, and\n"
" // \"[xxxx:xxxx::]:xxx\" means use ip and port from configuration.\n"
" //\n"
" // \"ipv6\": \"[1234:5678::]:9012\",\n"
" },\n"
"\n"
" // supernodes, if none are specified they'll be taken from your peers\n"
" \"supernodes\": [\n"

View File

@@ -33,7 +33,7 @@ trust-dns-resolver = { workspace = true }
ipnetwork = { workspace = true }
num_enum = { workspace = true }
[build_dependencies]
[build-dependencies]
cc = { workspace = true }
anyhow = { workspace = true }
cbindgen = { workspace = true }

View File

@@ -306,6 +306,8 @@ RTypes_Error_t *Rffi_Seeder_public_peer(Rffi_Seeder *seeder,
uint16_t user_num,
uint64_t passwd,
const String_t *code,
const String_t *addr4,
const String_t *addr6,
Allocator_t *alloc);
RTypes_Error_t *Rffi_Seeder_public_status(Rffi_Seeder *seeder,

View File

@@ -1,4 +1,6 @@
use anyhow::anyhow;
use std::{net::{SocketAddrV4, SocketAddrV6}, str::FromStr};
use anyhow::{anyhow, Result};
use cjdns_keys::CJDNSPublicKey;
use crate::{
@@ -127,6 +129,19 @@ pub unsafe extern "C" fn Rffi_Seeder_mk_creds(
std::ptr::null_mut()
}
fn parse_addr<T: FromStr>(s: *const String_t) -> Result<Option<T>>
where <T as FromStr>::Err: 'static + std::error::Error + Send + Sync,
{
let code = match cstr(s) {
Some(code) => String::from_utf8(code.0)?,
None => {
return Ok(None);
}
};
let out: T = code.parse()?;
Ok(Some(out))
}
#[no_mangle]
pub unsafe extern "C" fn Rffi_Seeder_public_peer(
seeder: *mut Rffi_Seeder,
@@ -135,6 +150,8 @@ pub unsafe extern "C" fn Rffi_Seeder_public_peer(
user_num: u16,
passwd: u64,
code: *const String_t,
addr4: *const String_t,
addr6: *const String_t,
alloc: *mut Allocator_t,
) -> *mut RTypes_Error_t {
let s = identity::from_c!(seeder);
@@ -142,7 +159,21 @@ pub unsafe extern "C" fn Rffi_Seeder_public_peer(
Some(code) => code,
None => c_bail!(alloc, anyhow!("code must not be null")),
};
let (user, pass) = s.seeder.public_peer(user_num, passwd, code.0);
let addr4: Option<SocketAddrV4> = match parse_addr(addr4) {
Ok(x) => x,
Err(e) => c_bail!(alloc, e),
};
let addr6: Option<SocketAddrV6> = match parse_addr(addr6) {
Ok(x) => x,
Err(e) => c_bail!(alloc, e),
};
let (user, pass) = s.seeder.public_peer(
user_num,
passwd,
code.0,
addr4,
addr6,
);
*user_out = strc(alloc, user);
*pass_out = strc(alloc, pass);
std::ptr::null_mut()

View File

@@ -1,5 +1,6 @@
use std::{collections::HashMap, net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, sync::Arc, time::Duration};
use anyhow::{anyhow, bail, Result, Context};
use boringtun::device::Sock;
use cjdns_keys::{CJDNSPublicKey, PublicKey};
use parking_lot::Mutex;
use tokio::sync::mpsc;
@@ -285,11 +286,56 @@ struct MyPeeringPasswd {
code: Vec<u8>,
}
macro_rules! split_ip {
($ip:expr) => {
if let Some(ip) = $ip {
(
if !ip.ip().is_unspecified() {
Some(ip.ip())
} else {
None
},
if ip.port() > 0 {
Some(ip.port())
} else {
None
}
)
} else {
Default::default()
}
}
}
#[derive(Default)]
struct MyPeeringInfo {
passwd: Option<MyPeeringPasswd>,
v4: Option<SocketAddrV4>,
v6: Option<SocketAddrV6>,
manual_v4: Option<SocketAddrV4>,
manual_v6: Option<SocketAddrV6>,
}
impl MyPeeringInfo {
fn get_lladdr4(&self) -> Option<SocketAddrV4> {
match (split_ip!(&self.manual_v4), split_ip!(&self.v4)) {
((_, None), (_, None)) => None,
((None, _), (None, _)) => None,
((Some(ip), Some(port)), (_, _)) => Some(SocketAddrV4::new(ip.clone(), port)),
((None, Some(port)), (Some(ip), _)) => Some(SocketAddrV4::new(ip.clone(), port)),
((Some(ip), None), (_, Some(port))) => Some(SocketAddrV4::new(ip.clone(), port)),
((None, None), (Some(ip), Some(port))) => Some(SocketAddrV4::new(ip.clone(), port)),
}
}
fn get_lladdr6(&self) -> Option<SocketAddrV6> {
match (split_ip!(&self.manual_v6), split_ip!(&self.v6)) {
((_, None), (_, None)) => None,
((None, _), (None, _)) => None,
((Some(ip), Some(port)), (_, _)) => Some(SocketAddrV6::new(ip.clone(), port, 0, 0)),
((None, Some(port)), (Some(ip), _)) => Some(SocketAddrV6::new(ip.clone(), port, 0, 0)),
((Some(ip), None), (_, Some(port))) => Some(SocketAddrV6::new(ip.clone(), port, 0, 0)),
((None, None), (Some(ip), Some(port))) => Some(SocketAddrV6::new(ip.clone(), port, 0, 0)),
}
}
}
pub struct SeederStatus {
@@ -310,7 +356,7 @@ impl Seeder {
/// Whether we have an address from got_lladdr
pub fn has_lladdr(&self) -> bool {
let m = self.mpi.lock();
m.v4.is_some() || m.v6.is_some()
m.get_lladdr4().is_some() || m.get_lladdr6().is_some()
}
fn got_lladdr4(&self, lla: &Control_LlAddr_Udp4_t) -> bool {
let sa =
@@ -392,10 +438,10 @@ impl Seeder {
bail!("Missing passwd");
};
let mut addrs = Vec::new();
if let Some(x) = &m.v4 {
if let Some(x) = &m.get_lladdr4() {
addrs.push(SocketAddr::V4(x.clone()));
}
if let Some(x) = &m.v6 {
if let Some(x) = &m.get_lladdr6() {
addrs.push(SocketAddr::V6(x.clone()));
}
if addrs.is_empty() {
@@ -421,9 +467,13 @@ impl Seeder {
user_num: u16,
passwd: u64,
code: Vec<u8>,
sa4: Option<SocketAddrV4>,
sa6: Option<SocketAddrV6>,
) -> (String, String) {
let mut m = self.mpi.lock();
m.passwd = Some(MyPeeringPasswd { user_num, passwd, code });
m.manual_v4 = sa4;
m.manual_v6 = sa6;
let p = cjdns_bytes::dnsseed::CjdnsPeer {
address: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1,1,1,1)), 1),
pubkey: [0_u8; 32],
@@ -450,8 +500,8 @@ impl Seeder {
pub fn get_status(&self) -> SeederStatus {
let m = self.mpi.lock();
let v4 = m.v4.map(|x|x.to_string());
let v6 = m.v6.map(|x|x.to_string());
let v4 = m.get_lladdr4().map(|x|x.to_string());
let v6 = m.get_lladdr6().map(|x|x.to_string());
let peer_id = m.passwd.as_ref().map(|p|p.code.clone());
drop(m);
let peer_id = if let Some(peer_id) = peer_id {

View File

@@ -227,7 +227,12 @@ Err_DEFUN PeeringSeeder_publicStatus(PeeringSeeder_PublicStatus_t** outP, Peerin
return NULL;
}
Err_DEFUN PeeringSeeder_publicPeer(PeeringSeeder_t* self, String_t* code, Allocator_t* reqAlloc)
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) {
@@ -250,6 +255,8 @@ Err_DEFUN PeeringSeeder_publicPeer(PeeringSeeder_t* self, String_t* code, Alloca
pq->userNum,
pq->pass,
code,
addr4,
addr6,
reqAlloc));
Assert_true(user->len < sizeof pq->login);

View File

@@ -45,7 +45,12 @@ void PeeringSeeder_setSnode(PeeringSeeder_t* self, struct Address* snode);
#define PeeringSeeder_publicPeer_ID_MAX_LEN 30
Err_DEFUN PeeringSeeder_publicPeer(PeeringSeeder_t* self, String* code, Allocator_t* reqAlloc);
Err_DEFUN PeeringSeeder_publicPeer(
PeeringSeeder_t* self,
String_t* code,
String_t* addr4,
String_t* addr6,
Allocator_t* reqAlloc);
// Get as a void pointer so that we don't need to pull in Rffi
void* PeeringSeeder_getRsSeeder(PeeringSeeder_t* self);

View File

@@ -108,9 +108,11 @@ static void publicPeer(Dict* args, void* vcontext, String* txid, struct Allocato
{
struct Context* ctx = Identity_check((struct Context*) vcontext);
String* peerCode = Dict_getStringC(args, "peerID");
String* ipv4 = Dict_getStringC(args, "ipv4");
String* ipv6 = Dict_getStringC(args, "ipv6");
char* err = "none";
RTypes_Error_t* er =
PeeringSeeder_publicPeer(ctx->sp, peerCode, requestAlloc);
PeeringSeeder_publicPeer(ctx->sp, peerCode, ipv4, ipv6, requestAlloc);
if (er) {
err = Rffi_printError(er, requestAlloc);
}
@@ -171,7 +173,9 @@ void PeeringSeeder_admin_register(PeeringSeeder_t* sp,
Admin_registerFunction("PeeringSeeder_publicPeer", publicPeer, ctx, true,
((struct Admin_FunctionArg[]) {
{ .name = "peerID", .required = false, .type = "String" }
{ .name = "peerID", .required = false, .type = "String" },
{ .name = "ipv4", .required = false, .type = "String" },
{ .name = "ipv6", .required = false, .type = "String" }
}), admin);
Admin_registerFunctionNoArgs("PeeringSeeder_publicStatus", publicStatus, ctx, true, admin);