First
This commit is contained in:
2
.config/vpn_config.toml
Normal file
2
.config/vpn_config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[mode.Router]
|
||||||
|
bind_address = "0.0.0.0:443"
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1485
Cargo.lock
generated
Normal file
1485
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
Cargo.toml
Normal file
26
Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "xvpn"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.37", features = ["derive"] }
|
||||||
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
serde_json = "1.0.149"
|
||||||
|
toml = "1.0.3"
|
||||||
|
tokio = { version = "1.49.0", features = [
|
||||||
|
"macros",
|
||||||
|
"rt-multi-thread",
|
||||||
|
"time",
|
||||||
|
"fs",
|
||||||
|
"net",
|
||||||
|
"io-util",
|
||||||
|
"sync",
|
||||||
|
"signal",
|
||||||
|
] }
|
||||||
|
anyhow = "1.0.102"
|
||||||
|
uuid = { version = "1.21.0", features = ["v4", "serde"] }
|
||||||
|
ipnet = { version = "2.11.0", features = ["serde"] }
|
||||||
|
base64 = "0.22.1"
|
||||||
|
tun-rs = "2.8.2"
|
||||||
|
chrono = "0.4.44"
|
||||||
131
src/client.rs
Normal file
131
src/client.rs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Args;
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
|
net::tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::router::{CLIENT_REGISTER_TIMEOUT, CliRegMessages, RouterMessages, SERVER_PACKET_SIZE};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Args)]
|
||||||
|
pub struct ClientCfg {
|
||||||
|
/// The server endpoint to connect to in host:port or ip:port format.
|
||||||
|
#[arg(long, short)]
|
||||||
|
pub server: String,
|
||||||
|
|
||||||
|
/// The local interface IP address (example: 10.8.0.2).
|
||||||
|
#[arg(long = "interface-ip")]
|
||||||
|
pub interface_ip: IpAddr,
|
||||||
|
|
||||||
|
/// The local interface name.
|
||||||
|
#[arg(long = "interface-name", default_value = "xvpn0")]
|
||||||
|
pub interface_name: String,
|
||||||
|
|
||||||
|
/// Local routes in CIDR format.
|
||||||
|
/// Example: --local-route 1.1.1.1/32,10.0.0.0/24
|
||||||
|
#[arg(long = "local-route", visible_alias = "lr", value_delimiter = ',')]
|
||||||
|
pub local_routes: Vec<Ipv4Net>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start(config: ClientCfg) -> Result<()> {
|
||||||
|
println!("Starting client with config: {:?}", config);
|
||||||
|
|
||||||
|
let stream = tokio::net::TcpStream::connect(&config.server).await?;
|
||||||
|
//stream.set_nodelay(true)?;
|
||||||
|
let (mut rx, mut tx) = stream.into_split();
|
||||||
|
// let client_stream = ClientStream::new(tx);
|
||||||
|
|
||||||
|
let mut buf = vec![0u8; SERVER_PACKET_SIZE];
|
||||||
|
register_client(&mut rx, &mut tx, config, &mut buf).await?;
|
||||||
|
|
||||||
|
println!("Client registration successful. Entering main loop to receive messages from router...");
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
msg = rx.read(&mut buf) => {
|
||||||
|
match msg {
|
||||||
|
Ok(0) => {
|
||||||
|
println!("Connection to router closed by peer.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ok(n) => {
|
||||||
|
println!("Received {} bytes from router: {:?}", n, RouterMessages::from_slice(&buf[..n]));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error reading from router: {}", e);
|
||||||
|
return Err(anyhow::anyhow!(format!("Error reading from router: {}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn register_client(
|
||||||
|
rx: &mut OwnedReadHalf,
|
||||||
|
tx: &mut OwnedWriteHalf,
|
||||||
|
config: ClientCfg,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let register_msg = RouterMessages::CliReg(CliRegMessages::Reg(config));
|
||||||
|
let mut client_registration_timeout =
|
||||||
|
tokio::time::interval_at(Instant::now() + CLIENT_REGISTER_TIMEOUT, CLIENT_REGISTER_TIMEOUT);
|
||||||
|
tx.write_all(®ister_msg.to_bytes()).await?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
msg = rx.read(buf) => {
|
||||||
|
match msg {
|
||||||
|
Ok(0) => {
|
||||||
|
let msg = "Connection closed by router while waiting for registration confirmation.";
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
return Err(anyhow::anyhow!(msg));
|
||||||
|
}
|
||||||
|
Ok(n) => {
|
||||||
|
let response = RouterMessages::from_slice(&buf[..n]);
|
||||||
|
println!("Received registration response from router: {:?}", response);
|
||||||
|
match response {
|
||||||
|
RouterMessages::CliReg(CliRegMessages::RegOk(uuid)) => {
|
||||||
|
println!("Client registration successful with UUID: {}", uuid);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
RouterMessages::CliReg(CliRegMessages::RegFailed(err_msg)) => {
|
||||||
|
eprintln!("Client registration failed: {}", err_msg);
|
||||||
|
return Err(anyhow::anyhow!(format!("Client registration failed: {}", err_msg)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let msg = "Unexpected message type received during client registration.";
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
return Err(anyhow::anyhow!(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error reading from router during client registration: {}", e);
|
||||||
|
return Err(anyhow::anyhow!(format!("Error reading from router: {}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
_= client_registration_timeout.tick() => {
|
||||||
|
let msg = "Client registration timed out waiting for confirmation from router.";
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
eprintln!("Closing connection with Server");
|
||||||
|
tx.shutdown().await?;
|
||||||
|
return Err(anyhow::anyhow!(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ok(())
|
||||||
|
}
|
||||||
82
src/config.rs
Normal file
82
src/config.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub static CONFIG_DIR: &str = ".config";
|
||||||
|
pub static CONFIG_FILE_NAME: &str = "vpn_config.toml";
|
||||||
|
|
||||||
|
pub async fn file_path<T>() -> PathBuf {
|
||||||
|
let full_name = std::any::type_name::<T>();
|
||||||
|
let struct_name = full_name.split("::").last().unwrap();
|
||||||
|
let mut path = PathBuf::from(CONFIG_DIR);
|
||||||
|
path.push(struct_name);
|
||||||
|
path.set_extension("toml");
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn file_path_with_name(name: &str) -> PathBuf {
|
||||||
|
let mut path = PathBuf::from(CONFIG_DIR);
|
||||||
|
path.push(name);
|
||||||
|
if path.extension().is_none() {
|
||||||
|
path.set_extension("toml");
|
||||||
|
}
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_load_or_default<T>() -> T
|
||||||
|
where
|
||||||
|
T: Default + DeserializeOwned,
|
||||||
|
{
|
||||||
|
let path = file_path::<T>().await;
|
||||||
|
match tokio::fs::read(&path).await {
|
||||||
|
Ok(content) => match toml::from_slice(&content) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(_) => T::default(),
|
||||||
|
},
|
||||||
|
Err(_) => T::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_load<T>() -> Result<T>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
let path = file_path::<T>().await;
|
||||||
|
let content = tokio::fs::read(&path).await?;
|
||||||
|
let config = toml::from_slice(&content)?;
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn config_load_from_file<T>(name: &str) -> Result<T>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
let path = file_path_with_name(name).await;
|
||||||
|
let content = tokio::fs::read(&path).await?;
|
||||||
|
let config = toml::from_slice(&content)?;
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn save<T>(data: &T) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
T: serde::Serialize,
|
||||||
|
{
|
||||||
|
let path = file_path::<T>().await;
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
tokio::fs::create_dir_all(parent).await?;
|
||||||
|
}
|
||||||
|
let content = toml::to_string(data).unwrap();
|
||||||
|
tokio::fs::write(path, content).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn save_to_file<T>(name: &str, data: &T) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
T: serde::Serialize,
|
||||||
|
{
|
||||||
|
let path = file_path_with_name(name).await;
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
tokio::fs::create_dir_all(parent).await?;
|
||||||
|
}
|
||||||
|
let content = toml::to_string(data).unwrap();
|
||||||
|
tokio::fs::write(path, content).await
|
||||||
|
}
|
||||||
90
src/main.rs
Normal file
90
src/main.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
pub mod client;
|
||||||
|
pub mod config;
|
||||||
|
pub mod router;
|
||||||
|
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::env;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use crate::client::ClientCfg;
|
||||||
|
use crate::config::{CONFIG_FILE_NAME, config_load_from_file, file_path_with_name, save_to_file};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Serialize, Deserialize)]
|
||||||
|
#[command(name = "x_vpn", version, about = "VPN mesh CLI (step 1: options only)")]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
mode: OpModes,
|
||||||
|
#[arg(long, global = true, help = "Enable debug logs", default_value = "false")]
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
|
debug: bool,
|
||||||
|
#[arg(long, short, help = "Save the configuration to a file", default_value = "false")]
|
||||||
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
|
save_config: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum OpModes {
|
||||||
|
/// Run the node in client mode.
|
||||||
|
Client(ClientCfg),
|
||||||
|
|
||||||
|
/// Run the node in router mode.
|
||||||
|
Router {
|
||||||
|
/// The local address to listen on in ip:port format.
|
||||||
|
#[arg(
|
||||||
|
long = "bind-address",
|
||||||
|
visible_alias = "bind_address",
|
||||||
|
short,
|
||||||
|
default_value = "0.0.0.0:443"
|
||||||
|
)]
|
||||||
|
bind_address: SocketAddr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for OpModes {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
OpModes::Client { .. } => write!(f, "Client"),
|
||||||
|
OpModes::Router { .. } => write!(f, "Router"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
// Check if some commanline or check if config file.
|
||||||
|
let commandline = if env::args().nth(1).is_some() {
|
||||||
|
Cli::parse()
|
||||||
|
} else {
|
||||||
|
match config_load_from_file::<Cli>(CONFIG_FILE_NAME).await {
|
||||||
|
Ok(config) => {
|
||||||
|
println!(
|
||||||
|
"Loaded configuration from file {}.",
|
||||||
|
file_path_with_name(CONFIG_FILE_NAME).await.to_str().unwrap()
|
||||||
|
);
|
||||||
|
config
|
||||||
|
}
|
||||||
|
Err(_) => Cli::parse(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if commandline.save_config {
|
||||||
|
if let Err(e) = save_to_file(CONFIG_FILE_NAME, &commandline).await {
|
||||||
|
eprintln!("Failed to save configuration: {}", e);
|
||||||
|
} else {
|
||||||
|
println!("Configuration saved successfully.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Parsed command line arguments: {:?}", commandline);
|
||||||
|
|
||||||
|
match commandline.mode {
|
||||||
|
OpModes::Client(client) => client::start(client).await?,
|
||||||
|
OpModes::Router { bind_address } => {
|
||||||
|
router::start(bind_address).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
264
src/router.rs
Normal file
264
src/router.rs
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration};
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
|
net::{
|
||||||
|
TcpStream,
|
||||||
|
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||||
|
},
|
||||||
|
sync::{Mutex, RwLock},
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::client::ClientCfg;
|
||||||
|
|
||||||
|
pub static KEEP_ALIVE_INTERVAL: Duration = tokio::time::Duration::from_secs(30);
|
||||||
|
pub static CLIENT_REGISTER_TIMEOUT: Duration = tokio::time::Duration::from_millis(100);
|
||||||
|
pub const SERVER_PACKET_SIZE: usize = 1024 * 9;
|
||||||
|
|
||||||
|
pub trait ReceiverTrait {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum RouterMessages {
|
||||||
|
CliReg(CliRegMessages),
|
||||||
|
KeepAlive(i64),
|
||||||
|
Data(Vec<u8>),
|
||||||
|
Quit(String),
|
||||||
|
Uknown(String),
|
||||||
|
}
|
||||||
|
impl RouterMessages {
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
serde_json::to_vec(self).expect("Unable to serialize RouteMessages")
|
||||||
|
}
|
||||||
|
pub fn from_slice(slice: &[u8]) -> Self {
|
||||||
|
serde_json::from_slice(slice).unwrap_or(RouterMessages::Uknown(
|
||||||
|
String::from_utf8(slice.to_vec()).unwrap_or_else(|b| format!("Invalid UTF-8: {:?}", b.as_bytes())),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum CliRegMessages {
|
||||||
|
Reg(ClientCfg),
|
||||||
|
RegOk(Uuid),
|
||||||
|
RegFailed(String),
|
||||||
|
Uknown(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CliRegMessages {
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
serde_json::to_vec(self).expect("Unable to serialize RegisterMessages")
|
||||||
|
}
|
||||||
|
pub fn from_slice(slice: &[u8]) -> Self {
|
||||||
|
serde_json::from_slice(slice).unwrap_or(CliRegMessages::Uknown(
|
||||||
|
String::from_utf8(slice.to_vec()).unwrap_or_else(|b| format!("Invalid UTF-8: {:?}", b.as_bytes())),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct VPNClient {
|
||||||
|
id: Uuid,
|
||||||
|
stream: ClientStream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VPNClient {
|
||||||
|
pub fn new(id: Uuid, stream: ClientStream) -> Self {
|
||||||
|
Self { id, stream }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> Uuid {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
pub async fn send(&self, msg: RouterMessages) -> Result<()> {
|
||||||
|
self.stream.send(msg).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn close(&self) -> Result<()> {
|
||||||
|
self.stream.close().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ClientStream {
|
||||||
|
tx: Arc<Mutex<OwnedWriteHalf>>,
|
||||||
|
}
|
||||||
|
impl ClientStream {
|
||||||
|
pub fn new(tx: OwnedWriteHalf) -> Self {
|
||||||
|
Self {
|
||||||
|
// write can be shared
|
||||||
|
tx: Arc::new(Mutex::new(tx)),
|
||||||
|
// read is done only by one task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn send(&self, msg: RouterMessages) -> Result<()> {
|
||||||
|
let bytes = msg.to_bytes();
|
||||||
|
let mut tx = self.tx.lock().await;
|
||||||
|
tx.write_all(&bytes).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn close(&self) -> Result<()> {
|
||||||
|
let mut tx = self.tx.lock().await;
|
||||||
|
tx.shutdown().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Router {
|
||||||
|
clients: Arc<RwLock<HashMap<Uuid, VPNClient>>>,
|
||||||
|
routing_table: Arc<RwLock<HashMap<Ipv4Net, Uuid>>>,
|
||||||
|
}
|
||||||
|
impl Router {
|
||||||
|
pub async fn register_client(&self, routing_table: &[Ipv4Net], vpn_client: VPNClient) -> Result<()> {
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
for net in routing_table {
|
||||||
|
self.routing_table.write().await.insert(*net, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clients.write().await.insert(id, vpn_client);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start(bind_address: SocketAddr) {
|
||||||
|
println!("Starting router on {}...", bind_address);
|
||||||
|
|
||||||
|
let router = Router::default();
|
||||||
|
|
||||||
|
let socket = tokio::net::TcpListener::bind(bind_address).await.unwrap();
|
||||||
|
println!("Router is listening on {}...", bind_address);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match socket.accept().await {
|
||||||
|
Ok((tcp_stream, addr)) => {
|
||||||
|
println!("Accepted connection from {}", addr);
|
||||||
|
//Clone the router for the new task
|
||||||
|
let router = router.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
println!("Handling connection from {}", addr);
|
||||||
|
match handle_client(router.clone(), tcp_stream).await {
|
||||||
|
Ok(_) => println!("Finished handling connection from {}", addr),
|
||||||
|
Err(e) => eprintln!("Error handling connection from {}: {}", addr, e),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to accept connection: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_client(router: Router, stream: TcpStream) -> Result<()> {
|
||||||
|
let (mut rx, tx) = stream.into_split();
|
||||||
|
let vpn_client = VPNClient::new(Uuid::new_v4(), ClientStream::new(tx));
|
||||||
|
|
||||||
|
let mut keep_alive_tick = tokio::time::interval_at(Instant::now() + KEEP_ALIVE_INTERVAL, KEEP_ALIVE_INTERVAL);
|
||||||
|
let mut buf = vec![0u8; SERVER_PACKET_SIZE];
|
||||||
|
|
||||||
|
match client_init(&mut rx, &vpn_client, &mut buf).await {
|
||||||
|
Ok(client) => {
|
||||||
|
println!(
|
||||||
|
"Client {} registered with routing table: {:?}",
|
||||||
|
vpn_client.id(),
|
||||||
|
client.local_routes
|
||||||
|
);
|
||||||
|
println!("Registering client {} with router...", vpn_client.id());
|
||||||
|
router.register_client(&client.local_routes, vpn_client.clone()).await?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to initialize client {}: {}", vpn_client.id(), e);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
msg = rx.read(&mut buf) => {
|
||||||
|
match msg {
|
||||||
|
Ok(0) => {
|
||||||
|
println!("Client {} closed the connection", vpn_client.id());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ok(n) => {
|
||||||
|
let msg = RouterMessages::from_slice(&buf[..n]);
|
||||||
|
println!("Received message from client {}: {:?}", vpn_client.id(), msg);
|
||||||
|
// Here you would implement the logic to handle messages from the client, such as routing data to other clients based on the routing table
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error reading from client {}: {}", vpn_client.id(), e);
|
||||||
|
return Err(anyhow::anyhow!(format!("Error reading from client: {}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_= keep_alive_tick.tick() => {
|
||||||
|
// Send keep-alive message to the client
|
||||||
|
println!("Sent keep-alive message to client");
|
||||||
|
vpn_client.send(RouterMessages::KeepAlive(Utc::now().timestamp_micros())).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn client_init(rx: &mut OwnedReadHalf, vpn_client: &VPNClient, buf: &mut [u8]) -> Result<ClientCfg> {
|
||||||
|
let mut client_registration_timeout =
|
||||||
|
tokio::time::interval_at(Instant::now() + CLIENT_REGISTER_TIMEOUT, CLIENT_REGISTER_TIMEOUT);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
msg = rx.read(buf) => {
|
||||||
|
match msg {
|
||||||
|
Ok(0) => {
|
||||||
|
println!("Client {} closed the connection during registration", vpn_client.id());
|
||||||
|
return Err(anyhow::anyhow!("Client closed the connection during registration"));
|
||||||
|
}
|
||||||
|
Ok(n) => {
|
||||||
|
let msg = RouterMessages::from_slice(&buf[..n]);
|
||||||
|
match msg {
|
||||||
|
RouterMessages::CliReg(CliRegMessages::Reg(client))=> {
|
||||||
|
println!("Received client registration with routing table: {:?}", client.local_routes);
|
||||||
|
let uuid = Uuid::new_v4();
|
||||||
|
vpn_client.send(RouterMessages::CliReg(CliRegMessages::RegOk(uuid))).await?;
|
||||||
|
return Ok(client);
|
||||||
|
}
|
||||||
|
router_msg => {
|
||||||
|
let msg = format!("Expected client registration message, but received: {:?}", router_msg);
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
eprintln!("Closing connection with client {}", vpn_client.id());
|
||||||
|
vpn_client.close().await?;
|
||||||
|
return Err(anyhow::anyhow!(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error reading from client {} during registration: {}", vpn_client.id(), e);
|
||||||
|
return Err(anyhow::anyhow!(format!("Error reading from client during registration: {}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = client_registration_timeout.tick() => {
|
||||||
|
let msg = format!("Client registration timed out after {}ms", (CLIENT_REGISTER_TIMEOUT.as_millis()));
|
||||||
|
vpn_client.send(RouterMessages::Quit(msg.clone())).await?;
|
||||||
|
vpn_client.close().await?;
|
||||||
|
return Err(anyhow::anyhow!(msg));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user