use crate::keys::{DiscoPublic, NodePublic};
use crate::netmap::derp::{DEFAULT_DERP_PORT, DerpMap, DerpNode, DerpRegion};
use crate::netmap::peer::{Cidr, Peer, PeerTable};
use crate::netmap::scanner::{Scanner, Token};
use crate::netmap::{Error, MAX_PEERS};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Context {
Root,
SelfNode,
PeerArray,
PeerObject,
PeerAddresses,
PeerAllowedIps,
PeerEndpoints,
SelfAddresses,
PatchArray,
PatchObject,
PatchEndpoints,
RemovedArray,
DerpMapObject,
DerpRegions,
DerpRegion,
DerpNodes,
DerpNode,
Ignored,
}
#[derive(Default, Clone)]
pub struct Netmap<const PEERS: usize = MAX_PEERS> {
pub peers: PeerTable<PEERS>,
pub derp: DerpMap,
pub node_key: Option<NodePublic>,
pub disco_key: Option<DiscoPublic>,
pub addresses: heapless::Vec<Cidr, { crate::netmap::MAX_ADDRESSES }>,
pub responses: usize,
}
impl<const PEERS: usize> Netmap<PEERS> {
pub const fn new() -> Self {
Self {
peers: PeerTable::new(),
derp: DerpMap::new(),
node_key: None,
disco_key: None,
addresses: heapless::Vec::new(),
responses: 0,
}
}
}
#[derive(Default)]
struct Pending {
id: u64,
node_key: Option<NodePublic>,
disco_key: Option<DiscoPublic>,
online: Option<bool>,
home_derp: Option<u16>,
addresses: heapless::Vec<Cidr, { crate::netmap::MAX_ADDRESSES }>,
allowed_ips: heapless::Vec<Cidr, { crate::netmap::MAX_ALLOWED_IPS }>,
endpoints: heapless::Vec<core::net::SocketAddr, { crate::netmap::MAX_ENDPOINTS }>,
endpoints_present: bool,
}
impl Pending {
const fn new() -> Self {
Self {
id: 0,
node_key: None,
disco_key: None,
online: None,
home_derp: None,
addresses: heapless::Vec::new(),
allowed_ips: heapless::Vec::new(),
endpoints: heapless::Vec::new(),
endpoints_present: false,
}
}
fn reset(&mut self) {
*self = Self::new();
}
}
#[derive(Default)]
struct PendingRegion {
id: u16,
code: heapless::String<16>,
nodes: heapless::Vec<DerpNode, { crate::netmap::MAX_DERP_NODES }>,
node_host: heapless::String<64>,
node_port: u16,
node_stun: u16,
}
impl PendingRegion {
const fn new() -> Self {
Self {
id: 0,
code: heapless::String::new(),
nodes: heapless::Vec::new(),
node_host: heapless::String::new(),
node_port: 0,
node_stun: 0,
}
}
}
#[derive(Default)]
struct State {
stack: heapless::Vec<Context, 16>,
key: heapless::String<64>,
pending: Pending,
region: PendingRegion,
failed: Option<Error>,
}
impl State {
fn context(&self) -> Context {
self.stack.last().copied().unwrap_or(Context::Ignored)
}
fn fail(&mut self, error: Error) {
self.failed.get_or_insert(error);
}
fn child_context(&self) -> Context {
let key = self.key.as_str();
match self.context() {
Context::Ignored if self.stack.is_empty() => Context::Root,
Context::Root => match key {
"Node" => Context::SelfNode,
"Peers" | "PeersChanged" => Context::PeerArray,
"PeersChangedPatch" => Context::PatchArray,
"PeersRemoved" => Context::RemovedArray,
"DERPMap" => Context::DerpMapObject,
_ => Context::Ignored,
},
Context::SelfNode => match key {
"Addresses" => Context::SelfAddresses,
_ => Context::Ignored,
},
Context::PeerArray => Context::PeerObject,
Context::PeerObject => match key {
"Addresses" => Context::PeerAddresses,
"AllowedIPs" => Context::PeerAllowedIps,
"Endpoints" => Context::PeerEndpoints,
_ => Context::Ignored,
},
Context::PatchArray => Context::PatchObject,
Context::PatchObject => match key {
"Endpoints" => Context::PatchEndpoints,
_ => Context::Ignored,
},
Context::DerpMapObject => match key {
"Regions" => Context::DerpRegions,
_ => Context::Ignored,
},
Context::DerpRegions => Context::DerpRegion,
Context::DerpRegion => match key {
"Nodes" => Context::DerpNodes,
_ => Context::Ignored,
},
Context::DerpNodes => Context::DerpNode,
_ => Context::Ignored,
}
}
fn on_start<const PEERS: usize>(&mut self, _netmap: &mut Netmap<PEERS>) {
let context = self.child_context();
match context {
Context::PeerObject | Context::PatchObject => self.pending.reset(),
Context::DerpRegion => {
self.region = PendingRegion::default();
self.region.id = self.key.parse().unwrap_or(0);
}
Context::DerpNode => {
self.region.node_host.clear();
self.region.node_port = DEFAULT_DERP_PORT;
self.region.node_stun = 0;
}
_ => {}
}
if self.stack.push(context).is_err() {
self.fail(Error::Full);
}
self.key.clear();
}
fn on_end<const PEERS: usize>(&mut self, netmap: &mut Netmap<PEERS>) {
let context = self.stack.pop().unwrap_or(Context::Ignored);
match context {
Context::PeerObject => self.commit_peer(netmap),
Context::PatchObject => self.apply_patch(netmap),
Context::DerpNode => {
let node = DerpNode {
host_name: self.region.node_host.clone(),
port: self.region.node_port,
stun_port: self.region.node_stun,
};
let _ = self.region.nodes.push(node);
}
Context::DerpRegion => {
let region = DerpRegion {
id: self.region.id,
code: self.region.code.clone(),
nodes: core::mem::take(&mut self.region.nodes),
};
if let Err(e) = netmap.derp.insert(region) {
self.fail(e);
}
}
Context::Root => netmap.responses += 1,
_ => {}
}
self.key.clear();
}
fn commit_peer<const PEERS: usize>(&mut self, netmap: &mut Netmap<PEERS>) {
let Some(node_key) = self.pending.node_key else {
return;
};
if netmap.node_key == Some(node_key) {
return;
}
let mut peer = Peer::new(self.pending.id, node_key);
peer.disco_key = self.pending.disco_key;
peer.online = self.pending.online.unwrap_or(false);
peer.home_derp = self.pending.home_derp.unwrap_or(0);
peer.addresses = self.pending.addresses.clone();
peer.allowed_ips = self.pending.allowed_ips.clone();
peer.endpoints = self.pending.endpoints.clone();
if let Err(e) = netmap.peers.upsert(peer) {
self.fail(e);
}
}
fn apply_patch<const PEERS: usize>(&mut self, netmap: &mut Netmap<PEERS>) {
let id = self.pending.id;
let Some(peer) = netmap.peers.get_mut(id) else {
return;
};
if let Some(online) = self.pending.online {
peer.online = online;
}
if let Some(derp) = self.pending.home_derp {
peer.home_derp = derp;
}
if let Some(key) = self.pending.disco_key {
peer.disco_key = Some(key);
}
if self.pending.endpoints_present {
peer.endpoints = self.pending.endpoints.clone();
}
}
fn on_string<const PEERS: usize>(&mut self, netmap: &mut Netmap<PEERS>, value: &str) {
let key = self.key.as_str();
match self.context() {
Context::SelfNode => match key {
"Key" => {
netmap.node_key = NodePublic::parse(value).ok();
if let Some(own) = netmap.node_key {
let ours: heapless::Vec<u64, 4> = netmap
.peers
.iter()
.filter(|p| p.node_key == own)
.map(|p| p.id)
.collect();
for id in ours {
netmap.peers.remove(id);
}
}
}
"DiscoKey" => netmap.disco_key = DiscoPublic::parse(value).ok(),
_ => {}
},
Context::SelfAddresses => match Cidr::parse(value) {
Ok(cidr) => {
let _ = netmap.addresses.push(cidr);
}
Err(e) => self.fail(e),
},
Context::PeerObject | Context::PatchObject => match key {
"Key" => match NodePublic::parse(value) {
Ok(parsed) => self.pending.node_key = Some(parsed),
Err(_) => self.fail(Error::Malformed),
},
"DiscoKey" => match DiscoPublic::parse(value) {
Ok(parsed) => self.pending.disco_key = Some(parsed),
Err(_) => self.fail(Error::Malformed),
},
_ => {}
},
Context::PeerAddresses => match Cidr::parse(value) {
Ok(cidr) => {
let _ = self.pending.addresses.push(cidr);
}
Err(e) => self.fail(e),
},
Context::PeerAllowedIps => match Cidr::parse(value) {
Ok(cidr) => {
let _ = self.pending.allowed_ips.push(cidr);
}
Err(e) => self.fail(e),
},
Context::PeerEndpoints | Context::PatchEndpoints => {
self.pending.endpoints_present = true;
if let Ok(endpoint) = value.parse() {
let _ = self.pending.endpoints.push(endpoint);
}
}
Context::DerpRegion if key == "RegionCode" => {
self.region.code = heapless::String::try_from(value).unwrap_or_default();
}
Context::DerpNode if key == "HostName" => {
self.region.node_host = heapless::String::try_from(value).unwrap_or_default();
}
_ => {}
}
self.key.clear();
}
fn on_number<const PEERS: usize>(&mut self, netmap: &mut Netmap<PEERS>, value: i64) {
let key = self.key.as_str();
match self.context() {
Context::PeerObject | Context::PatchObject => match key {
"ID" | "NodeID" => self.pending.id = value as u64,
"HomeDERP" | "DERPRegion" => self.pending.home_derp = Some(value as u16),
_ => {}
},
Context::RemovedArray => {
netmap.peers.remove(value as u64);
}
Context::DerpRegion if key == "RegionID" => self.region.id = value as u16,
Context::DerpNode => match key {
"DERPPort" => self.region.node_port = value as u16,
"STUNPort" => self.region.node_stun = value as u16,
_ => {}
},
_ => {}
}
self.key.clear();
}
fn on_bool(&mut self, value: bool) {
if matches!(self.context(), Context::PeerObject | Context::PatchObject)
&& self.key == "Online"
{
self.pending.online = Some(value);
}
self.key.clear();
}
}
impl State {
fn on_token<const PEERS: usize>(
&mut self,
netmap: &mut Netmap<PEERS>,
token: Token<'_>,
) -> Result<(), Error> {
match token {
Token::StartObject | Token::StartArray => self.on_start(netmap),
Token::EndObject | Token::EndArray => self.on_end(netmap),
Token::Key(key) => {
self.key = heapless::String::try_from(key).unwrap_or_default();
}
Token::Str(value) => self.on_string(netmap, value),
Token::Int(value) => self.on_number(netmap, value),
Token::Bool(value) => self.on_bool(value),
Token::Null => self.key.clear(),
}
Ok(())
}
}
#[derive(Default)]
pub struct Parser<const PEERS: usize = MAX_PEERS> {
scanner: Scanner,
state: State,
}
impl<const PEERS: usize> Parser<PEERS> {
pub const fn new() -> Self {
Self {
scanner: Scanner::new(),
state: State {
stack: heapless::Vec::new(),
key: heapless::String::new(),
pending: Pending::new(),
region: PendingRegion::new(),
failed: None,
},
}
}
pub fn push(&mut self, netmap: &mut Netmap<PEERS>, bytes: &[u8]) -> Result<(), Error> {
let Self { scanner, state } = self;
scanner.push(bytes, |token| state.on_token(netmap, token))
}
pub fn is_done(&self) -> bool {
self.scanner.is_done()
}
pub fn finish(mut self, netmap: &mut Netmap<PEERS>) -> Result<(), Error> {
let Self { scanner, state } = &mut self;
scanner.finish(|token| state.on_token(netmap, token))?;
match self.state.failed {
Some(error) => Err(error),
None => Ok(()),
}
}
}