use std::io;
use std::net::{SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock, Weak};
use std::thread;
use crate::iface::{L3Device, L3Handler};
use crate::namespace::{Cleanup, L3Connector};
use crate::wg::handler::{Config as HandlerConfig, Handler};
use crate::wg::multihandler::MultiHandler;
use crate::wg::server::{OnPacketFn, OnPeerConnectedFn, Server, ServerConfig};
use crate::wg::{NoisePresharedKey, NoisePrivateKey, NoisePublicKey};
use crate::{IpPrefix, Packet, Result};
#[derive(Clone)]
pub struct AdapterConfig {
pub private_key: NoisePrivateKey,
pub multi_handler: Option<Arc<MultiHandler>>,
pub connector: Arc<dyn L3Connector + Send + Sync>,
pub addr: IpPrefix,
pub on_unknown_peer: Option<crate::wg::handler::UnknownPeerFn>,
}
impl std::fmt::Debug for AdapterConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AdapterConfig")
.field("multi_handler", &self.multi_handler.is_some())
.field("addr", &self.addr)
.finish()
}
}
pub struct Adapter {
handler: Option<Arc<Handler>>,
multi_handler: Option<Arc<MultiHandler>>,
server: Arc<Server>,
connector: Arc<dyn L3Connector + Send + Sync>,
addr: IpPrefix,
peers: RwLock<std::collections::HashMap<NoisePublicKey, WgPeer>>,
closed: AtomicBool,
}
impl std::fmt::Debug for Adapter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Adapter").field("addr", &self.addr).finish()
}
}
struct WgPeer {
#[allow(dead_code)]
key: NoisePublicKey,
dev: Arc<PeerL3Device>,
cleanup: Mutex<Option<Cleanup>>,
}
pub(crate) struct PeerL3Device {
adapter: Weak<Adapter>,
key: NoisePublicKey,
handler: Mutex<Option<L3Handler>>,
addr: Mutex<IpPrefix>,
}
impl std::fmt::Debug for PeerL3Device {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PeerL3Device")
.field("key", &self.key)
.finish()
}
}
impl PeerL3Device {
fn new(adapter: &Arc<Adapter>, key: NoisePublicKey, addr: IpPrefix) -> Arc<Self> {
Arc::new(PeerL3Device {
adapter: Arc::downgrade(adapter),
key,
handler: Mutex::new(None),
addr: Mutex::new(addr),
})
}
fn deliver(&self, data: &[u8]) {
if let Some(h) = self.handler.lock().expect("handler lock").clone() {
let p = Packet::from_slice(data);
let _ = h(p);
}
}
}
impl L3Device for PeerL3Device {
fn set_handler(&self, h: L3Handler) {
*self.handler.lock().expect("handler lock") = Some(h);
}
fn send(&self, packet: &Packet) -> Result<()> {
let Some(adapter) = self.adapter.upgrade() else {
return Err(io::Error::new(
io::ErrorKind::NotConnected,
"adapter dropped",
));
};
adapter.server.send(packet.as_bytes(), &self.key)
}
fn addr(&self) -> IpPrefix {
*self.addr.lock().expect("addr lock")
}
fn set_addr(&self, p: IpPrefix) -> Result<()> {
*self.addr.lock().expect("addr lock") = p;
Ok(())
}
fn close(&self) -> Result<()> {
Ok(())
}
}
impl Adapter {
pub fn new(cfg: AdapterConfig) -> Result<Arc<Self>> {
let weak_for_cb: Mutex<Option<Weak<Adapter>>> = Mutex::new(None);
let weak_for_cb = Arc::new(weak_for_cb);
let weak_for_cb_pkt = weak_for_cb.clone();
let weak_for_cb_conn = weak_for_cb.clone();
let on_packet: OnPacketFn =
Arc::new(move |data: &[u8], key: NoisePublicKey, _h: &Arc<Handler>| {
if let Some(a) = weak_for_cb_pkt
.lock()
.expect("weak lock")
.as_ref()
.and_then(|w| w.upgrade())
{
a.on_packet(data, key);
}
});
let on_connected: OnPeerConnectedFn =
Arc::new(move |key: NoisePublicKey, _h: &Arc<Handler>| {
if let Some(a) = weak_for_cb_conn
.lock()
.expect("weak lock")
.as_ref()
.and_then(|w| w.upgrade())
{
a.on_peer_connected(key);
}
});
let server_cfg = if let Some(mh) = cfg.multi_handler.clone() {
if let Some(cb) = cfg.on_unknown_peer.clone() {
let _ = cb;
}
ServerConfig {
handler: None,
multi_handler: Some(mh),
on_packet,
on_peer_connected: Some(on_connected),
maintenance_interval: None,
read_buffer_size: 2048,
}
} else {
let h = Handler::new(HandlerConfig {
private_key: cfg.private_key.clone(),
on_unknown_peer: cfg.on_unknown_peer.clone(),
load_threshold: None,
})?;
ServerConfig {
handler: Some(h),
multi_handler: None,
on_packet,
on_peer_connected: Some(on_connected),
maintenance_interval: None,
read_buffer_size: 2048,
}
};
let handler = server_cfg.handler.clone();
let multi = server_cfg.multi_handler.clone();
let server = Server::new(server_cfg)?;
let me = Arc::new(Adapter {
handler,
multi_handler: multi,
server,
connector: cfg.connector,
addr: cfg.addr,
peers: RwLock::new(std::collections::HashMap::new()),
closed: AtomicBool::new(false),
});
*weak_for_cb.lock().expect("weak lock") = Some(Arc::downgrade(&me));
Ok(me)
}
pub fn serve(self: &Arc<Self>, conn: UdpSocket) -> Result<()> {
self.server.serve(Arc::new(conn))
}
pub fn spawn_serve(self: &Arc<Self>, conn: UdpSocket) -> thread::JoinHandle<Result<()>> {
let me = self.clone();
thread::spawn(move || me.server.serve(Arc::new(conn)))
}
pub fn add_peer(&self, key: NoisePublicKey) {
if let Some(mh) = self.multi_handler.as_ref() {
for h in mh.handlers() {
h.add_peer(key);
}
} else if let Some(h) = self.handler.as_ref() {
h.add_peer(key);
}
}
pub fn add_peer_to(&self, key: NoisePublicKey, handler: &Arc<Handler>) {
handler.add_peer(key);
}
pub fn add_peer_with_psk(&self, key: NoisePublicKey, psk: NoisePresharedKey) -> Result<()> {
if let Some(h) = self.handler.as_ref() {
h.add_peer_with_psk(key, psk);
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"add_peer_with_psk requires single-handler mode",
))
}
}
pub fn accept_unknown_peer(
&self,
key: NoisePublicKey,
packet: &[u8],
addr: SocketAddr,
) -> Result<()> {
if let Some(mh) = self.multi_handler.as_ref() {
for h in mh.handlers() {
if crate::wg::handshake::check_mac1(h.public_key().as_bytes(), packet) {
let _ = h.accept_unknown_peer(key, packet, &addr)?;
return Ok(());
}
}
Err(io::Error::new(
io::ErrorKind::NotFound,
"no handler matched MAC1",
))
} else if let Some(h) = self.handler.as_ref() {
let _ = h.accept_unknown_peer(key, packet, &addr)?;
Ok(())
} else {
Err(io::Error::other("no handler"))
}
}
pub fn remove_peer(&self, key: &NoisePublicKey) {
if let Some(mh) = self.multi_handler.as_ref() {
for h in mh.handlers() {
h.remove_peer(key);
}
} else if let Some(h) = self.handler.as_ref() {
h.remove_peer(key);
}
self.teardown_peer(key);
}
pub fn connect(&self, key: &NoisePublicKey, addr: SocketAddr) -> Result<()> {
self.server.connect(key, addr)
}
pub fn connect_with(
&self,
key: &NoisePublicKey,
addr: SocketAddr,
handler: &Arc<Handler>,
) -> Result<()> {
self.server.connect_with(key, addr, handler)
}
pub fn public_key(&self) -> NoisePublicKey {
self.handler
.as_ref()
.expect("public_key requires single-handler mode")
.public_key()
}
pub fn handler(&self) -> Option<Arc<Handler>> {
self.handler.clone()
}
pub fn multi_handler(&self) -> Option<Arc<MultiHandler>> {
self.multi_handler.clone()
}
pub fn close(&self) -> Result<()> {
if self.closed.swap(true, Ordering::SeqCst) {
return Ok(());
}
let _ = self.server.close();
let peers: Vec<_> = {
let mut g = self.peers.write().expect("peers lock");
g.drain().map(|(_, p)| p).collect()
};
for p in peers {
if let Some(cleanup) = p.cleanup.lock().expect("cleanup lock").take() {
let _ = cleanup();
}
}
if let Some(mh) = self.multi_handler.as_ref() {
return mh.close();
}
if let Some(h) = self.handler.as_ref() {
return h.close();
}
Ok(())
}
fn on_peer_connected(self: &Arc<Self>, key: NoisePublicKey) {
let mut peers = self.peers.write().expect("peers lock");
if peers.contains_key(&key) {
return;
}
let dev = PeerL3Device::new(self, key, self.addr);
let dev_dyn: Arc<dyn L3Device> = dev.clone();
let cleanup = match self.connector.connect_l3(dev_dyn) {
Ok(c) => c,
Err(_) => return,
};
peers.insert(
key,
WgPeer {
key,
dev,
cleanup: Mutex::new(Some(cleanup)),
},
);
}
fn on_packet(&self, data: &[u8], key: NoisePublicKey) {
let peer = {
let g = self.peers.read().expect("peers lock");
g.get(&key).map(|p| p.dev.clone())
};
if let Some(dev) = peer {
dev.deliver(data);
}
}
fn teardown_peer(&self, key: &NoisePublicKey) {
let removed = self.peers.write().expect("peers lock").remove(key);
if let Some(p) = removed {
if let Some(cleanup) = p.cleanup.lock().expect("cleanup lock").take() {
let _ = cleanup();
}
}
}
}