v2
This commit is contained in:
@@ -0,0 +1,468 @@
|
||||
use anyhow::anyhow;
|
||||
use clap::Parser;
|
||||
use ipnet::Ipv4Net;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use vnt_core::context::config::Config;
|
||||
use vnt_core::nat::NetInput;
|
||||
use vnt_core::tls::verifier::CertValidationMode;
|
||||
use vnt_core::tunnel_core::server::transport::config::ProtocolAddress;
|
||||
use vnt_ipc as vnt_core;
|
||||
use vnt_ipc::port_mapping::PortMapping;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||
pub struct FileConfig {
|
||||
pub server: Option<Vec<String>>,
|
||||
pub network_code: Option<String>,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
pub no_punch: Option<bool>,
|
||||
pub rtx: Option<bool>,
|
||||
pub compress: Option<bool>,
|
||||
pub fec: Option<bool>,
|
||||
pub input: Option<Vec<NetInput>>,
|
||||
pub output: Option<Vec<Ipv4Net>>,
|
||||
pub no_nat: Option<bool>,
|
||||
pub no_tun: Option<bool>,
|
||||
pub mtu: Option<u16>,
|
||||
pub ctrl_port: Option<u16>,
|
||||
pub port_mapping: Option<Vec<String>>,
|
||||
pub allow_mapping: Option<bool>,
|
||||
pub device_id: Option<String>,
|
||||
pub device_name: Option<String>,
|
||||
pub tun_name: Option<String>,
|
||||
pub password: Option<String>,
|
||||
pub cert_mode: Option<String>,
|
||||
pub udp_stun: Option<Vec<String>>,
|
||||
pub tcp_stun: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl FileConfig {
|
||||
pub fn load(path: &Path) -> anyhow::Result<Self> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
Ok(toml::from_str(&content)?)
|
||||
}
|
||||
|
||||
pub fn save(&self, path: &Path) -> anyhow::Result<()> {
|
||||
let content = toml::to_string_pretty(self)?;
|
||||
std::fs::write(path, content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_server_addr(&self) -> anyhow::Result<Vec<ProtocolAddress>> {
|
||||
if let Some(server_raw) = &self.server {
|
||||
let mut server_addr = Vec::with_capacity(server_raw.len());
|
||||
for x in server_raw {
|
||||
server_addr.push(
|
||||
x.parse::<ProtocolAddress>()
|
||||
.map_err(|e| anyhow!("invalid server address '{}': {}", x, e))?,
|
||||
)
|
||||
}
|
||||
Ok(server_addr)
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
pub fn to_port_mapping(&self) -> anyhow::Result<Vec<PortMapping>> {
|
||||
if let Some(port_mapping_raw) = &self.port_mapping {
|
||||
let mut port_mapping = Vec::with_capacity(port_mapping_raw.len());
|
||||
for x in port_mapping_raw {
|
||||
port_mapping.push(
|
||||
x.parse::<PortMapping>()
|
||||
.map_err(|e| anyhow!("invalid port_mapping '{}': {}", x, e))?,
|
||||
)
|
||||
}
|
||||
Ok(port_mapping)
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Args {
|
||||
/// 服务器地址 例如 `-s quic://127.0.0.1:29872`, 支持quic/tcp/wss/dynamic
|
||||
#[clap(short, long)]
|
||||
pub server: Vec<ProtocolAddress>,
|
||||
/// 网络编号,相同编号的会组同一个局域网
|
||||
#[clap(short, long)]
|
||||
pub network_code: Option<String>,
|
||||
#[clap(short = 'k', long, hide = true)]
|
||||
pub token: Option<String>,
|
||||
/// 自定义虚拟IP
|
||||
#[clap(long)]
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
/// 启用加密,设置加密密码
|
||||
#[clap(short, long)]
|
||||
pub password: Option<String>,
|
||||
/// 启用quic优化传输
|
||||
#[clap(long)]
|
||||
pub rtx: bool,
|
||||
/// 启用压缩 (LZ4)
|
||||
#[clap(short = 'z', long)]
|
||||
pub compress: bool,
|
||||
/// 启用 FEC 前向纠错,损失一定带宽来提升网络稳定性
|
||||
#[clap(long)]
|
||||
pub fec: bool,
|
||||
/// 入栈监听网段
|
||||
#[clap(short, long)]
|
||||
pub input: Vec<NetInput>,
|
||||
/// 出栈允许网段
|
||||
#[clap(short, long)]
|
||||
pub output: Vec<Ipv4Net>,
|
||||
/// 自定义设备名称
|
||||
#[clap(long, alias = "name")]
|
||||
pub device_name: Option<String>,
|
||||
/// 设备id
|
||||
#[clap(long, alias = "id")]
|
||||
pub device_id: Option<String>,
|
||||
/// 关闭打洞
|
||||
#[clap(long)]
|
||||
pub no_punch: bool,
|
||||
/// 服务端证书验证
|
||||
#[clap(long)]
|
||||
pub cert_mode: Option<CertValidationMode>,
|
||||
/// 虚拟网卡名称
|
||||
#[clap(long)]
|
||||
pub tun_name: Option<String>,
|
||||
/// 关闭内置子网NAT
|
||||
#[clap(long)]
|
||||
pub no_nat: bool,
|
||||
/// 禁用tun,禁用后只能充当流量出口或者进行端口映射,无需管理员权限
|
||||
#[clap(long)]
|
||||
pub no_tun: bool,
|
||||
/// 端口映射,格式为:协议://本地监听地址-目标虚拟IP-目标映射地址
|
||||
#[clap(long)]
|
||||
pub port_mapping: Vec<PortMapping>,
|
||||
/// 是否允许作为端口映射出口,开启后其他设备才可使用本设备的ip为"目标虚拟IP"
|
||||
#[clap(long)]
|
||||
pub allow_mapping: bool,
|
||||
/// 设置mtu
|
||||
#[clap(long)]
|
||||
pub mtu: Option<u16>,
|
||||
/// 控制端口,设置0时禁用控制服务
|
||||
#[clap(long)]
|
||||
pub ctrl_port: Option<u16>,
|
||||
/// 读取配置文件
|
||||
#[arg(long)]
|
||||
pub conf: Option<PathBuf>,
|
||||
/// 输出配置文件示例
|
||||
#[clap(long)]
|
||||
pub conf_example: bool,
|
||||
}
|
||||
impl Args {
|
||||
pub fn parse_compatible() -> Self {
|
||||
let mut args = Args::parse();
|
||||
if args.network_code.is_none() {
|
||||
args.network_code = args.token.clone();
|
||||
}
|
||||
args
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CtrlConfig {
|
||||
pub ctrl_port: Option<u16>,
|
||||
}
|
||||
|
||||
pub fn build_config_from_args_and_file(
|
||||
args: Option<Args>,
|
||||
file: Option<FileConfig>,
|
||||
) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
match (args, file) {
|
||||
(Some(args), Some(file)) => build_from_args_and_file(args, file),
|
||||
(Some(args), None) => build_from_args_only(args),
|
||||
(None, Some(file)) => build_from_file_only(file),
|
||||
(None, None) => Err(anyhow!("neither args nor config file provided")),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_from_args_and_file(args: Args, file: FileConfig) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
let server_addr = if args.server.is_empty() {
|
||||
file.to_server_addr()?
|
||||
} else {
|
||||
args.server
|
||||
};
|
||||
let port_mapping = if args.port_mapping.is_empty() {
|
||||
file.to_port_mapping()?
|
||||
} else {
|
||||
args.port_mapping
|
||||
};
|
||||
|
||||
let network_code = args
|
||||
.network_code
|
||||
.or_else(|| file.network_code.clone())
|
||||
.ok_or_else(|| anyhow!("network_code is required"))?;
|
||||
|
||||
let cert_mode = args
|
||||
.cert_mode
|
||||
.or_else(|| file.cert_mode.as_deref().and_then(|s| s.parse().ok()))
|
||||
.unwrap_or(CertValidationMode::InsecureSkipVerification);
|
||||
|
||||
let device_id = match args.device_id.or_else(|| file.device_id.clone()) {
|
||||
Some(id) => id,
|
||||
None => vnt_core::utils::device_id::get_device_id()?,
|
||||
};
|
||||
|
||||
let input = if args.input.is_empty() {
|
||||
file.input.unwrap_or_default()
|
||||
} else {
|
||||
args.input
|
||||
};
|
||||
|
||||
let output = if args.output.is_empty() {
|
||||
file.output.unwrap_or_default()
|
||||
} else {
|
||||
args.output
|
||||
};
|
||||
let mut udp_stun = file.udp_stun.unwrap_or_default();
|
||||
for x in udp_stun.iter_mut() {
|
||||
if !x.contains(':') {
|
||||
x.push_str(":3478");
|
||||
}
|
||||
}
|
||||
let mut tcp_stun = file.tcp_stun.unwrap_or_default();
|
||||
for x in tcp_stun.iter_mut() {
|
||||
if !x.contains(':') {
|
||||
x.push_str(":3478");
|
||||
}
|
||||
}
|
||||
|
||||
let config = Config {
|
||||
server_addr,
|
||||
network_code,
|
||||
ip: args.ip.or(file.ip),
|
||||
no_punch: args.no_punch || file.no_punch.unwrap_or(false),
|
||||
rtx: args.rtx || file.rtx.unwrap_or(false),
|
||||
compress: args.compress || file.compress.unwrap_or(false),
|
||||
fec: args.fec || file.fec.unwrap_or(false),
|
||||
device_id,
|
||||
device_name: args
|
||||
.device_name
|
||||
.or_else(|| file.device_name.clone())
|
||||
.unwrap_or_else(default_hostname),
|
||||
tun_name: args.tun_name.or_else(|| file.tun_name.clone()),
|
||||
password: args.password.or_else(|| file.password.clone()),
|
||||
cert_mode,
|
||||
input,
|
||||
output,
|
||||
no_nat: args.no_nat || file.no_nat.unwrap_or(false),
|
||||
no_tun: args.no_tun || file.no_tun.unwrap_or(false),
|
||||
mtu: args.mtu.or(file.mtu),
|
||||
port_mapping,
|
||||
allow_port_mapping: args.allow_mapping || file.allow_mapping.unwrap_or(false),
|
||||
udp_stun,
|
||||
tcp_stun,
|
||||
};
|
||||
|
||||
let ctrl_config = CtrlConfig {
|
||||
ctrl_port: args.ctrl_port.or(file.ctrl_port),
|
||||
};
|
||||
Ok((config, ctrl_config))
|
||||
}
|
||||
|
||||
fn build_from_args_only(args: Args) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
let device_id = match args.device_id {
|
||||
Some(id) => id,
|
||||
None => vnt_core::utils::device_id::get_device_id()?,
|
||||
};
|
||||
let config = Config {
|
||||
server_addr: args.server,
|
||||
network_code: args
|
||||
.network_code
|
||||
.ok_or_else(|| anyhow!("network_code is required"))?,
|
||||
ip: args.ip,
|
||||
no_punch: args.no_punch,
|
||||
rtx: args.rtx,
|
||||
input: args.input,
|
||||
compress: args.compress,
|
||||
fec: args.fec,
|
||||
device_id,
|
||||
device_name: args.device_name.unwrap_or_else(default_hostname),
|
||||
tun_name: args.tun_name,
|
||||
password: args.password,
|
||||
cert_mode: args
|
||||
.cert_mode
|
||||
.unwrap_or(CertValidationMode::InsecureSkipVerification),
|
||||
output: args.output,
|
||||
no_nat: args.no_nat,
|
||||
no_tun: args.no_tun,
|
||||
mtu: args.mtu,
|
||||
port_mapping: args.port_mapping,
|
||||
allow_port_mapping: args.allow_mapping,
|
||||
..Default::default()
|
||||
};
|
||||
let ctrl_config = CtrlConfig {
|
||||
ctrl_port: args.ctrl_port,
|
||||
};
|
||||
Ok((config, ctrl_config))
|
||||
}
|
||||
|
||||
fn build_from_file_only(file: FileConfig) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
let server_addr = file.to_server_addr()?;
|
||||
let port_mapping = file.to_port_mapping()?;
|
||||
|
||||
let cert_mode = file
|
||||
.cert_mode
|
||||
.as_deref()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(CertValidationMode::InsecureSkipVerification);
|
||||
|
||||
let device_id = match file.device_id.clone() {
|
||||
Some(id) => id,
|
||||
None => vnt_core::utils::device_id::get_device_id()?,
|
||||
};
|
||||
let mut udp_stun = file.udp_stun.unwrap_or_default();
|
||||
for x in udp_stun.iter_mut() {
|
||||
if !x.contains(':') {
|
||||
x.push_str(":3478");
|
||||
}
|
||||
}
|
||||
let mut tcp_stun = file.tcp_stun.unwrap_or_default();
|
||||
for x in tcp_stun.iter_mut() {
|
||||
if !x.contains(':') {
|
||||
x.push_str(":3478");
|
||||
}
|
||||
}
|
||||
|
||||
let config = Config {
|
||||
server_addr,
|
||||
network_code: file
|
||||
.network_code
|
||||
.ok_or_else(|| anyhow!("network_code is required"))?,
|
||||
ip: file.ip,
|
||||
no_punch: file.no_punch.unwrap_or(false),
|
||||
rtx: file.rtx.unwrap_or(false),
|
||||
input: file.input.unwrap_or_default(),
|
||||
compress: file.compress.unwrap_or(false),
|
||||
fec: file.fec.unwrap_or(false),
|
||||
device_id,
|
||||
device_name: file.device_name.clone().unwrap_or_else(default_hostname),
|
||||
tun_name: file.tun_name.clone(),
|
||||
password: file.password.clone(),
|
||||
cert_mode,
|
||||
output: file.output.unwrap_or_default(),
|
||||
no_nat: file.no_nat.unwrap_or(false),
|
||||
no_tun: file.no_tun.unwrap_or(false),
|
||||
mtu: file.mtu,
|
||||
port_mapping,
|
||||
allow_port_mapping: file.allow_mapping.unwrap_or(false),
|
||||
udp_stun,
|
||||
tcp_stun,
|
||||
};
|
||||
let ctrl_config = CtrlConfig {
|
||||
ctrl_port: file.ctrl_port,
|
||||
};
|
||||
Ok((config, ctrl_config))
|
||||
}
|
||||
|
||||
fn default_hostname() -> String {
|
||||
hostname::get()
|
||||
.ok()
|
||||
.and_then(|v| v.into_string().ok())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
impl FileConfig {
|
||||
pub fn print_example(path: Option<&Path>) -> anyhow::Result<()> {
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
let example = format!(
|
||||
r#"# ==================================
|
||||
# VNT 配置文件示例(程序版本 v{version})
|
||||
# ==================================
|
||||
|
||||
# --- 网络配置 ---
|
||||
# 网络编号,相同网络编号的会组在同一个虚拟网 (必填)
|
||||
network_code = "your_network_code"
|
||||
|
||||
# 服务器地址列表(支持 quic / tcp / wss / dynamic) (必填)
|
||||
# dynamic 协议使用dns txt解析记录值
|
||||
server = ["quic://1.2.3.4:29872"]
|
||||
|
||||
# ===简单使用以下参数可以不动===
|
||||
|
||||
# 自定义虚拟 IP (可选)
|
||||
# ip = "10.10.0.2"
|
||||
|
||||
# 是否启用quic优化传输 (默认 false,设置为true时开启)
|
||||
# rtx = false
|
||||
|
||||
# 是否启用 FEC 前向纠错,损失一定带宽来提升网络稳定性(默认 false,设置为true时开启)
|
||||
# fec = false
|
||||
|
||||
# 是否关闭 P2P 打洞 (默认 false,设置为true时关闭)
|
||||
# no_punch = false
|
||||
|
||||
# 是否启用 LZ4 压缩 (默认 false,设置为true时开启)
|
||||
# compress = false
|
||||
|
||||
# 入栈监听网段 (逗号分隔的 CIDR 和目标 IP),用于点对网,将指定网段的流量发送到目标节点
|
||||
# input = ["192.168.0.0/24,10.26.0.2", "192.168.1.0/24,10.26.0.3"]
|
||||
|
||||
# 出栈允许网段,用于点对网,允许指定网段的转发
|
||||
# output = ["0.0.0.0/0"]
|
||||
|
||||
# 是否关闭内置子网NAT,关闭(设为true)后需要配置网卡转发,否则无法使用点对网。通常关闭内置子网NAT,使用系统的网卡转发,点对网性能会更好
|
||||
# no_nat = false
|
||||
|
||||
# 是否关闭TUN虚拟网卡,关闭(设为true)后只能充当流量出口或者进行端口映射,关闭后无需管理员权限
|
||||
# no_tun = false
|
||||
|
||||
# 端口映射,格式为:协议://本地监听地址-目标虚拟IP-目标映射地址
|
||||
# 端口映射用于在本地监听指定端口,并将收到的网络流量经由指定虚拟节点转发到目标地址,从而实现跨网络或内网服务访问
|
||||
# 例如 port_mapping = ["tcp://0.0.0.0:81-10.0.0.2-10.0.0.2:80"]
|
||||
# tcp://0.0.0.0:81-10.0.0.2-10.0.0.2:80 则表示将本地tcp的81端口的数据转发到10.0.0.2:80
|
||||
# tcp://0.0.0.0:81-10.0.0.2-192.168.1.10:80 则表示将本地tcp的81端口的数据经过10.0.0.2转到192.168.1.10:80
|
||||
# tcp://0.0.0.0:81-10.0.0.2-anyonehost:80 则表示将本地tcp的81端口的数据经过10.0.0.2转到anyonehost:80
|
||||
# port_mapping = []
|
||||
|
||||
# 是否允许作为端口映射出口,开启(设置为true)后其他设备才可使用本设备的ip为"目标虚拟IP"
|
||||
# 开启后虚拟网络其他设备可以使用此设备当跳板访问其他网络
|
||||
# allow_mapping = false
|
||||
|
||||
# 控制服务的 tcp 端口
|
||||
# ctrl_port = 11233
|
||||
|
||||
# MTU 设置
|
||||
# mtu = 1400
|
||||
|
||||
# --- 设备配置 ---
|
||||
|
||||
# 设备名称 (可选,默认读取本机 hostname)
|
||||
# device_name = "my-device"
|
||||
|
||||
# 设备 ID (可选,不填自动生成,不同设备ID不能相同)
|
||||
# device_id = "device-id-xxxx"
|
||||
|
||||
# 虚拟网卡名称
|
||||
# tun_name = "vnt-tun"
|
||||
|
||||
# --- 安全配置 ---
|
||||
|
||||
# 加密密码 (可选)
|
||||
# password = "123456"
|
||||
|
||||
# 证书校验方式:
|
||||
# skip 跳过验证(默认)
|
||||
# standard 使用系统证书验证
|
||||
# finger 使用证书指纹验证,服务端启动时日志会输出指纹,
|
||||
# 例如 finger:3bdd8675606837cdf95d5e13445606315762315a78555f9da652940a25feaec1
|
||||
# cert_mode = "skip"
|
||||
|
||||
# --- 其他配置 ---
|
||||
# 自定义stun地址,分别用于udp打洞和tcp打洞,需要单独配置,不设置则用默认stun
|
||||
# udp_stun = ["stun.chat.bilibili.com"]
|
||||
# tcp_stun = ["stun.nextcloud.com:443"]
|
||||
"#,
|
||||
version = VERSION
|
||||
);
|
||||
println!("--- 示例配置文件内容 ---\n{}", example);
|
||||
if let Some(p) = path {
|
||||
std::fs::write(p, &example)?;
|
||||
println!("示例配置文件已写入 {}", p.display());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const WINTUN_DLL: &[u8] = include_bytes!("../dll/amd64/wintun.dll");
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
const WINTUN_DLL: &[u8] = include_bytes!("../dll/x86/wintun.dll");
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const WINTUN_DLL: &[u8] = include_bytes!("../dll/arm64/wintun.dll");
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
const WINTUN_DLL: &[u8] = include_bytes!("../dll/arm/wintun.dll");
|
||||
|
||||
pub fn extract_wintun() {
|
||||
if let Err(e) = extract_wintun_impl() {
|
||||
log::error!("extract wintun.dll {:?}", e);
|
||||
}
|
||||
}
|
||||
fn extract_wintun_impl() -> io::Result<()> {
|
||||
let path = std::env::current_exe()
|
||||
.ok()
|
||||
.and_then(|p| p.parent().map(|d| d.join("wintun.dll")))
|
||||
.unwrap_or_else(|| Path::new("wintun.dll").to_path_buf());
|
||||
|
||||
if !path.exists() {
|
||||
let mut file = fs::File::create(&path)?;
|
||||
file.write_all(WINTUN_DLL)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod log;
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
pub fn log_init(log_name: &str) {
|
||||
log_init0(log_name, None);
|
||||
}
|
||||
pub fn log_init0(log_name: &str, yaml: Option<&str>) {
|
||||
let path = PathBuf::from("logs");
|
||||
|
||||
if !path.exists() {
|
||||
let _ = std::fs::create_dir(&path);
|
||||
}
|
||||
|
||||
let log_config = path.join(yaml.unwrap_or("log4rs.yaml"));
|
||||
|
||||
if !log_config.exists()
|
||||
&& let Ok(mut f) = std::fs::File::create(&log_config)
|
||||
{
|
||||
let log_file = path.join(format!("{log_name}.log"));
|
||||
let mut string = String::new();
|
||||
string.push_str(log_name);
|
||||
string.push_str(".{}.log");
|
||||
let log_file_pattern = path.join(&string);
|
||||
|
||||
let c = format!(
|
||||
r#"
|
||||
refresh_rate: 30 seconds
|
||||
appenders:
|
||||
rolling_file:
|
||||
kind: rolling_file
|
||||
path: {}
|
||||
append: true
|
||||
encoder:
|
||||
pattern: "{{d}} [{{f}}:{{L}}] {{h({{l}})}} {{M}}:{{m}}{{n}}"
|
||||
policy:
|
||||
kind: compound
|
||||
trigger:
|
||||
kind: size
|
||||
limit: 10 mb
|
||||
roller:
|
||||
kind: fixed_window
|
||||
pattern: {}
|
||||
base: 1
|
||||
count: 5
|
||||
console:
|
||||
kind: console
|
||||
encoder:
|
||||
pattern: "{{d}} {{h({{l}})}} {{m}}{{n}}"
|
||||
root:
|
||||
level: info
|
||||
appenders:
|
||||
- rolling_file
|
||||
- console
|
||||
"#,
|
||||
log_file.display(),
|
||||
log_file_pattern.display()
|
||||
);
|
||||
let _ = f.write_all(c.as_bytes());
|
||||
}
|
||||
|
||||
let _ = log4rs::init_file(log_config, Default::default());
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
use anyhow::Context;
|
||||
use args_config::{build_config_from_args_and_file, Args, FileConfig};
|
||||
use route_manager::Route;
|
||||
use std::path::Path;
|
||||
use vnt_ipc as vnt_core;
|
||||
|
||||
use vnt_core::core::NetworkManager;
|
||||
use vnt_core::utils::task_control::TaskGroupManager;
|
||||
pub mod args_config;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod extract_wintun_dll;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
if let Err(e) = main0().await {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
async fn main0() -> anyhow::Result<()> {
|
||||
let args = Args::parse_compatible();
|
||||
|
||||
vnt2::log::log_init("vnt2");
|
||||
log::info!("version: {:?}", env!("CARGO_PKG_VERSION"));
|
||||
#[cfg(windows)]
|
||||
extract_wintun_dll::extract_wintun();
|
||||
if args.conf_example {
|
||||
FileConfig::print_example(Some(Path::new("example_config.toml")))?;
|
||||
return Ok(());
|
||||
}
|
||||
let file_cfg = if let Some(path) = args.conf.as_ref() {
|
||||
log::info!("loaded config from {:?}", path);
|
||||
Some(FileConfig::load(path).context("failed to load config")?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (config, ctrl_config) =
|
||||
build_config_from_args_and_file(Some(args), file_cfg).context("invalid configuration")?;
|
||||
|
||||
log::info!(
|
||||
"server: {}",
|
||||
config
|
||||
.server_addr
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
);
|
||||
log::info!("network code: {}", config.network_code);
|
||||
log::info!("device id: {}", config.device_id);
|
||||
log::info!("device name: {}", config.device_name);
|
||||
log::info!("cert mode: {}", config.cert_mode);
|
||||
log::info!("compress: {}", config.compress);
|
||||
log::info!("rtx(quic channel): {}", config.rtx);
|
||||
let sub_input = config.input.clone();
|
||||
if !config.input.is_empty() {
|
||||
let x = config
|
||||
.input
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
log::info!("Sub network input:{x}");
|
||||
}
|
||||
if !config.output.is_empty() {
|
||||
let x = config
|
||||
.output
|
||||
.iter()
|
||||
.map(|v| v.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
log::info!("Sub network output:{x}");
|
||||
}
|
||||
|
||||
if let Some(password_sign) = config.key_sign() {
|
||||
log::info!("password sign: {:?}", password_sign);
|
||||
}
|
||||
|
||||
let group_manager = TaskGroupManager::new();
|
||||
let (task_group, task_group_guard) =
|
||||
group_manager.create_task().context("create task group")?;
|
||||
|
||||
let mut network_manager = NetworkManager::create_network(Box::new(config), task_group)
|
||||
.await
|
||||
.context("create network")?;
|
||||
let reg_msg = network_manager.register().await.context("register")?;
|
||||
|
||||
if !network_manager.is_no_tun() {
|
||||
log::info!("启动网络:{}/{}", reg_msg.ip, reg_msg.prefix_len);
|
||||
network_manager.start_tun().await.context("start tun")?;
|
||||
network_manager
|
||||
.set_network_ip(reg_msg.ip, reg_msg.prefix_len)
|
||||
.await
|
||||
.context("set network ip")?;
|
||||
if !sub_input.is_empty() {
|
||||
let if_index = network_manager
|
||||
.tun_if_index()
|
||||
.await
|
||||
.context("tun_if_index")?;
|
||||
let mut route_manager = route_manager::RouteManager::new()?;
|
||||
for x in sub_input {
|
||||
let route = Route::new(x.net.network().into(), x.net.prefix_len())
|
||||
.with_gateway(x.target_ip.into())
|
||||
.with_if_index(if_index);
|
||||
if let Err(e) = route_manager.add(&route) {
|
||||
log::error!("add route [{route}] error: {e:?}");
|
||||
} else {
|
||||
log::info!("add route [{route}] successful");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::info!(
|
||||
"启动网络:{}/{} (无虚拟网卡)",
|
||||
reg_msg.ip,
|
||||
reg_msg.prefix_len
|
||||
);
|
||||
}
|
||||
let vnt_api = network_manager.vnt_api();
|
||||
if ctrl_config.ctrl_port.is_none_or(|p| p != 0) {
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = vnt_ipc::server::run_server(ctrl_config.ctrl_port, vnt_api).await {
|
||||
log::error!("ipc:{e:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = network_manager.wait_all_stopped() => {
|
||||
break;
|
||||
}
|
||||
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
log::info!("Ctrl+c received!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(task_group_guard);
|
||||
log::info!("stop network");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
use vnt_ipc::message::ipc_request::IpcCmd;
|
||||
|
||||
/// 操作vnt进程
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
/// 核心进程控制端口
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Commands {
|
||||
/// 输出当前程序信息
|
||||
Info,
|
||||
/// 输出客户端ip列表
|
||||
Ips,
|
||||
/// 输出客户端信息列表
|
||||
#[command(alias = "list")]
|
||||
Clients,
|
||||
/// 输出IP路由信息
|
||||
Route,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
let args = Args::parse();
|
||||
let port = args.port;
|
||||
let cmd = match args.command {
|
||||
Commands::Info => IpcCmd::AppInfo(Default::default()),
|
||||
|
||||
Commands::Ips => IpcCmd::ClientIps(Default::default()),
|
||||
Commands::Clients => IpcCmd::ClientList(Default::default()),
|
||||
Commands::Route => IpcCmd::AllRoute(Default::default()),
|
||||
};
|
||||
if let Err(e) = vnt_ipc::client::run_client(cmd, port).await {
|
||||
eprintln!("{:?}", e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
use clap::Parser;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod extract_wintun_dll;
|
||||
|
||||
/// vnt web服务
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// 本地http服务监听地址
|
||||
#[clap(long)]
|
||||
addr: Option<SocketAddr>,
|
||||
/// 加载vnt配置路径,配置内容参考web端的配置格式
|
||||
#[clap(long)]
|
||||
conf: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
if let Err(e) = main0().await {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
async fn main0() -> anyhow::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
vnt2::log::log_init("vnt2");
|
||||
log::info!("version: {:?}", env!("CARGO_PKG_VERSION"));
|
||||
#[cfg(windows)]
|
||||
extract_wintun_dll::extract_wintun();
|
||||
let addr = args.addr.unwrap_or("127.0.0.1:19099".parse()?);
|
||||
vnt_web::run_http_server(addr, args.conf).await?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user