增加桌面端

This commit is contained in:
lubeilin
2023-07-17 01:32:34 +08:00
parent c2b7b02f3f
commit 6b140b0f71
43 changed files with 1038 additions and 2607 deletions
-74
View File
@@ -1,74 +0,0 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::time::Duration;
use crate::command::entity::{DeviceItem, RouteItem, Status};
pub struct CommandClient {
udp: UdpSocket,
}
impl CommandClient {
pub fn new() -> io::Result<Self> {
let port = crate::config::read_command_port()?;
let udp = UdpSocket::bind("127.0.0.1:0")?;
udp.set_read_timeout(Some(Duration::from_secs(2)))?;
udp.connect(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
port,
)))?;
Ok(Self { udp })
}
}
impl CommandClient {
pub fn list(&self) -> io::Result<Vec<DeviceItem>> {
self.udp.send(b"list")?;
let mut buf = [0; 10240];
let len = self.udp.recv(&mut buf)?;
match serde_json::from_slice::<Vec<DeviceItem>>(&buf[..len]) {
Ok(val) => {
Ok(val)
}
Err(e) => {
log::error!("{:?}",e);
Err(io::Error::new(io::ErrorKind::Other, "data error"))
}
}
}
pub fn route(&self) -> io::Result<Vec<RouteItem>> {
self.udp.send(b"route")?;
let mut buf = [0; 10240];
let len = self.udp.recv(&mut buf)?;
match serde_json::from_slice::<Vec<RouteItem>>(&buf[..len]) {
Ok(val) => {
Ok(val)
}
Err(e) => {
log::error!("{:?}",e);
Err(io::Error::new(io::ErrorKind::Other, "data error"))
}
}
}
pub fn status(&self) -> io::Result<Status> {
self.udp.send(b"status")?;
let mut buf = [0; 10240];
let len = self.udp.recv(&mut buf)?;
match serde_json::from_slice::<Status>(&buf[..len]) {
Ok(val) => {
Ok(val)
}
Err(e) => {
log::error!("{:?},{:?}",&buf[..len],e);
Err(io::Error::new(io::ErrorKind::Other, "data error"))
}
}
}
#[cfg(any(unix))]
pub fn stop(&self) -> io::Result<String> {
self.udp.send(b"stop")?;
let mut buf = [0; 10240];
let len = self.udp.recv(&mut buf)?;
Ok(String::from_utf8(buf[..len].to_vec()).unwrap())
}
}
-34
View File
@@ -1,34 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Status {
pub name: String,
pub virtual_ip: String,
pub virtual_gateway: String,
pub virtual_netmask: String,
pub connect_status: String,
pub relay_server: String,
pub nat_type: String,
pub public_ips: String,
pub local_ip: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RouteItem {
pub destination: String,
pub next_hop: String,
pub metric: String,
pub rt: String,
pub interface: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DeviceItem {
pub name: String,
pub virtual_ip: String,
pub nat_type: String,
pub public_ips: String,
pub local_ip: String,
pub nat_traversal_type: String,
pub rt: String,
pub status: String,
}
-59
View File
@@ -1,59 +0,0 @@
use std::io;
use console::style;
use crate::console_out;
pub mod client;
pub mod server;
pub mod entity;
pub enum CommandEnum {
Route,
List,
ListAll,
Status,
#[cfg(any(unix))]
Stop,
}
pub fn command(cmd: CommandEnum) {
if let Err(e) = command_(cmd) {
println!("{}:{:?}", style("连接后台服务错误(Connection background service error)").red(), e);
}
}
fn command_(cmd: CommandEnum) -> io::Result<()> {
match client::CommandClient::new() {
Ok(command_client) => {
match cmd {
CommandEnum::Route => {
let list = command_client.route()?;
console_out::console_route_table(list);
}
CommandEnum::List => {
let list = command_client.list()?;
console_out::console_device_list(list);
}
CommandEnum::ListAll => {
let list = command_client.list()?;
console_out::console_device_list_all(list);
}
CommandEnum::Status => {
let status = command_client.status()?;
console_out::console_status(status);
}
#[cfg(any(unix))]
CommandEnum::Stop => {
command_client.stop()?;
}
}
}
Err(e) => {
log::error!("{:?}",e);
println!(
"{}:{:?}",
style("连接后台服务错误(Connection background service error)").red(), e
);
}
};
Ok(())
}
-189
View File
@@ -1,189 +0,0 @@
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::sync::Arc;
use switch::core::Switch;
use crate::command::entity::{DeviceItem, RouteItem, Status};
pub struct CommandServer {}
impl CommandServer {
pub fn new() -> Self {
Self {}
}
}
impl CommandServer {
pub fn start(&self, switch: Arc<Switch>) -> io::Result<()> {
let mut port = 21637 as u16;
let udp = loop {
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(127, 0, 0, 1),
port,
))) {
Ok(udp) => {
break udp;
}
Err(e) => {
if e.kind() == io::ErrorKind::AddrInUse {
port += 1;
} else {
log::error!("创建udp失败 {:?}", e);
return Err(e);
}
}
}
};
crate::config::update_command_port(port)?;
let mut buf = [0u8; 64];
loop {
let (len, addr) = udp.recv_from(&mut buf)?;
match std::str::from_utf8(&buf[..len]) {
Ok(cmd) => {
if let Ok(out) = command(cmd, &switch) {
udp.send_to(out.as_bytes(), addr)?;
}
}
Err(e) => {
log::warn!("{:?}", e);
}
}
}
}
}
pub fn command_route(switch: &Switch) -> Vec<RouteItem> {
let route_table = switch.route_table();
let mut route_list = Vec::with_capacity(route_table.len());
for (destination, route) in route_table {
let next_hop = switch.route_key(&route.route_key()).map_or(String::new(), |v| v.to_string());
let metric = route.metric.to_string();
let rt = if route.rt < 0 {
"".to_string()
} else {
route.rt.to_string()
};
let interface = route.addr.to_string();
let item = RouteItem {
destination: destination.to_string(),
next_hop,
metric,
rt,
interface,
};
route_list.push(item);
}
route_list
}
pub fn command_list(switch: &Switch) -> Vec<DeviceItem> {
let device_list = switch.device_list();
let mut list = Vec::new();
for peer in device_list {
let name = peer.name;
let virtual_ip = peer.virtual_ip.to_string();
let (nat_type, public_ips, local_ip) = if let Some(nat_info) = switch.peer_nat_info(&peer.virtual_ip) {
let nat_type = format!("{:?}", nat_info.nat_type);
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
let public_ips = public_ips.join(",");
let local_ip = nat_info.local_ip.to_string();
(nat_type, public_ips, local_ip)
} else {
("".to_string(), "".to_string(), "".to_string())
};
let (nat_traversal_type, rt) = if let Some(route) = switch.route(&peer.virtual_ip) {
let nat_traversal_type = if route.metric == 1 { "p2p" } else { "relay" }.to_string();
let rt = if route.rt < 0 {
"".to_string()
} else {
route.rt.to_string()
};
(nat_traversal_type, rt)
} else {
("relay".to_string(), "".to_string())
};
let status = format!("{:?}", peer.status);
let item = DeviceItem {
name,
virtual_ip,
nat_type,
public_ips,
local_ip,
nat_traversal_type,
rt,
status,
};
list.push(item);
}
list
}
pub fn command_status(switch: &Switch) -> Status {
let current_device = switch.current_device();
let nat_info = switch.nat_info();
let name = switch.name().to_string();
let virtual_ip = current_device.virtual_ip().to_string();
let virtual_gateway = current_device.virtual_gateway().to_string();
let virtual_netmask = current_device.virtual_netmask.to_string();
let connect_status = format!("{:?}", switch.connection_status());
let relay_server = current_device.connect_server.to_string();
let nat_type = format!("{:?}", nat_info.nat_type);
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
let public_ips = public_ips.join(",");
let local_ip = nat_info.local_ip.to_string();
Status {
name,
virtual_ip,
virtual_gateway,
virtual_netmask,
connect_status,
relay_server,
nat_type,
public_ips,
local_ip,
}
}
fn command(cmd: &str, switch: &Switch) -> io::Result<String> {
let out_str = match cmd {
"route" => {
match serde_json::to_string(&command_route(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"list" => {
match serde_json::to_string(&command_list(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"status" => {
match serde_json::to_string(&command_status(switch)) {
Ok(str) => {
str
}
Err(e) => {
format!("{:?}", e)
}
}
}
"stop" => {
switch.stop()?;
"stopping".to_string()
}
_ => {
format!("command '{}' not found. \n Try to enter: 'help'\n", cmd)
}
};
Ok(out_str)
}
-140
View File
@@ -1,140 +0,0 @@
use clap::{Arg, ArgAction, Command};
use clap::builder::BoolishValueParser;
use crate::i18n::*;
fn common() -> Command {
Command::new("switch-desktop")
.about(switch_about())
// .version(switch_version())
.subcommand_required(true)
.arg_required_else_help(true)
// .author(switch_author())
.override_usage(switch_usage())
.subcommand(
Command::new("start")
.about(switch_start_about())
.arg(
Arg::new("token")
.long("token")
.help(switch_token_help())
.action(ArgAction::Set)
)
.arg(
Arg::new("name")
.long("name")
.help(switch_name_help())
.action(ArgAction::Set)
)
.arg(
Arg::new("device_id")
.long("device-id")
.help(switch_device_id_help())
.action(ArgAction::Set)
).arg(
Arg::new("server")
.long("server")
.help(switch_server_help())
.action(ArgAction::Set)
).arg(
Arg::new("nat_test_server")
.long("nat-test-server")
.help(switch_nat_test_server_help())
.action(ArgAction::Set)
).arg(
Arg::new("log")
.long("log")
.help(switch_log_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()),
).arg(
Arg::new("tap")
.long("tap")
.help(switch_tap_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()),
).arg(
Arg::new("in_ip")
.long("in-ip")
.help(switch_in_ip_help())
.action(ArgAction::Append)
.num_args(1..),
).arg(
Arg::new("out_ip")
.long("out-ip")
.help(switch_out_ip_help())
.action(ArgAction::Append)
).arg(
Arg::new("password")
.long("password")
.help(switch_password_help())
.action(ArgAction::Set)
).arg(
Arg::new("simulate_multicast")
.long("simulate-multicast")
.help(switch_simulate_multicast_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()),
).arg(
Arg::new("config")
.long("config")
.help(switch_config_help())
.action(ArgAction::Set)
)
,
).subcommand(
Command::new("stop")
.about(switch_stop_about()))
.subcommand(
Command::new("route")
.about(switch_route_about()))
.subcommand(Command::new("list")
.about(switch_list_about()).arg(
Arg::new("all")
.long("all")
.short('a')
.help(switch_list_all_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()), ))
.subcommand(Command::new("status")
.about(switch_status_about()))
}
pub fn check() -> bool {
#[cfg(windows)]
let cmd = common().subcommand(Command::new("install")
.about(switch_install_about())
.arg(
Arg::new("path")
.long("path")
.help(switch_path_help())
.action(ArgAction::Set)
.num_args(1..))
.arg(
Arg::new("auto")
.long("auto")
.help(switch_auto_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()), ))
.subcommand(Command::new("uninstall")
.about(switch_uninstall_about()))
.subcommand(Command::new("config")
.about(switch_config_about())
.arg(
Arg::new("auto")
.long("auto")
.help(switch_auto_help())
.action(ArgAction::SetTrue)
.value_parser(BoolishValueParser::new()), ));
#[cfg(any(unix))]
let cmd = common();
match cmd.try_get_matches() {
Ok(_) => {
true
}
Err(e) => {
println!("{}", e);
false
}
}
}
-45
View File
@@ -1,45 +0,0 @@
use std::io;
use std::path::PathBuf;
use crate::config::get_home;
#[cfg(target_os = "windows")]
pub fn log_service_init() -> io::Result<()> {
log_init_(crate::config::get_win_server_home().join("switch-service.log"))
}
pub fn log_init() -> io::Result<()> {
log_init_(get_home().join("switch-desktop.log"))
}
fn log_init_(file_name: PathBuf) -> io::Result<()> {
let stderr = log4rs::append::console::ConsoleAppender::builder()
.target(log4rs::append::console::Target::Stderr)
.build();
let logfile = log4rs::append::file::FileAppender::builder()
// Pattern: https://docs.rs/log4rs/*/log4rs/encode/pattern/index.html
.encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new(
"{d(%+)(utc)} [{f}:{L}] {h({l})} {M}:{m}{n}\n",
)))
.build(file_name)?;
match log4rs::Config::builder()
.appender(log4rs::config::Appender::builder().build("logfile", Box::new(logfile)))
.appender(
log4rs::config::Appender::builder()
.filter(Box::new(log4rs::filter::threshold::ThresholdFilter::new(
log::LevelFilter::Error,
)))
.build("stderr", Box::new(stderr)),
)
.build(
log4rs::config::Root::builder()
.appender("logfile")
.appender("stderr")
.build(log::LevelFilter::Info),
) {
Ok(config) => {
let _ = log4rs::init_config(config);
}
Err(_) => {}
}
Ok(())
}
-506
View File
@@ -1,515 +1,9 @@
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs};
use std::path::PathBuf;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use crate::{i18n, StartArgs};
pub mod log_config;
lazy_static! {
pub static ref SWITCH_HOME_PATH: Mutex<Option<PathBuf>> = Mutex::new(None);
}
#[cfg(windows)]
pub fn get_win_server_home() -> PathBuf {
SWITCH_HOME_PATH.lock().as_ref().unwrap().clone()
}
#[cfg(windows)]
pub fn set_win_server_home(home: PathBuf) {
let _ = SWITCH_HOME_PATH.lock().insert(home);
}
#[derive(Clone, Debug)]
pub struct StartConfig {
pub tap: bool,
pub name: String,
pub token: String,
pub server: SocketAddr,
pub nat_test_server: Vec<SocketAddr>,
pub device_id: String,
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
pub out_ips: Vec<(u32, u32, Ipv4Addr)>,
#[cfg(any(unix))]
pub off_command_server: bool,
pub log: bool,
pub password: Option<String>,
pub simulate_multicast:bool,
}
fn ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32, Ipv4Addr)>, String> {
let mut in_ips_c = vec![];
for x in ips {
let mut split = x.split(",");
let net = if let Some(net) = split.next() {
net
} else {
return Err("参数错误".to_string());
};
let ip = if let Some(ip) = split.next() {
ip
} else {
return Err("参数错误".to_string());
};
let ip = if let Ok(ip) = ip.parse::<Ipv4Addr>() {
ip
} else {
return Err("参数错误".to_string());
};
let mut split = net.split("/");
let dest = if let Some(dest) = split.next() {
dest
} else {
return Err("参数错误".to_string());
};
let mask = if let Some(mask) = split.next() {
mask
} else {
return Err("参数错误".to_string());
};
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
dest
} else {
return Err("参数错误".to_string());
};
let mask = if let Ok(m) = mask.parse::<u32>() {
let mut mask = 0 as u32;
for i in 0..m {
mask = mask | (1 << (31 - i));
}
mask
} else {
return Err("参数错误".to_string());
};
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask, ip));
}
Ok(in_ips_c)
}
pub fn default_config(start_args: StartArgs) -> Result<StartConfig, String> {
println!("========参数配置========");
if start_args.log {
println!("print log");
}
let tap = start_args.tap;
if tap {
println!("use tap");
} else {
println!("use tun");
}
if start_args.token.is_none() {
return Err(i18n::switch_token_not_found_print());
}
let token = start_args.token.unwrap();
if token.is_empty() {
return Err(i18n::switch_token_cannot_be_empty_print());
}
if token.len() > 64 {
return Err(i18n::switch_token_cannot_exceed_64_print());
}
println!("token:{:?}", token);
let name = start_args.name.unwrap_or_else(|| {
os_info::get().to_string()
});
let name = name.trim();
let name = if name.len() > 64 {
name[..64].to_string()
} else {
name.to_string()
};
println!("name:{:?}", name);
let device_id = start_args.device_id.unwrap_or_else(|| {
if let Ok(Some(mac_address)) = mac_address::get_mac_address() {
mac_address.to_string()
} else {
"".to_string()
}
});
if device_id.is_empty() || device_id.len() > 64 {
return Err(i18n::switch_device_id_is_empty_print());
}
println!("device_id:{:?}", device_id);
let in_ips = start_args.in_ip.unwrap_or_else(|| {
vec![]
});
let out_ips = start_args.out_ip.unwrap_or_else(|| {
vec![]
});
println!("in_ips:{:?}", in_ips);
let in_ips_c = if let Ok(in_ips_c) = ips_parse(&in_ips) {
in_ips_c
} else {
return Err(i18n::switch_in_ips_example_print());
};
println!("out_ips:{:?}", out_ips);
let out_ips_c = if let Ok(out_ips_c) = ips_parse(&out_ips) {
out_ips_c
} else {
return Err(i18n::switch_out_ips_example_print());
};
let server = match start_args.server.unwrap_or_else(|| {
"nat1.wherewego.top:29871".to_string()
}).to_socket_addrs() {
Ok(mut server) => {
if let Some(addr) = server.next() {
addr
} else {
return Err(i18n::switch_relay_server_address_error());
}
}
Err(e) => {
return Err(format!("{} :{:?}", i18n::switch_relay_server_address_error(), e));
}
};
println!("中继服务器:{:?}", server);
let nat_test_server = start_args.nat_test_server.unwrap_or_else(|| {
"nat1.wherewego.top:35061,nat1.wherewego.top:35062,nat2.wherewego.top:35061,nat2.wherewego.top:35062".to_string()
}).split(",").flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
if nat_test_server.is_empty() {
return Err(i18n::switch_nat_test_server_address_error());
}
println!("NAT探测服务器:{:?}", nat_test_server);
let base_config = StartConfig {
tap,
name,
token,
server,
nat_test_server,
device_id,
in_ips: in_ips_c,
out_ips: out_ips_c,
#[cfg(any(unix))]
off_command_server: start_args.off_command_server,
log: start_args.log,
password: start_args.password,
simulate_multicast:start_args.simulate_multicast,
};
println!("========参数配置========");
Ok(base_config)
}
pub fn read_config_file(config_path: PathBuf) -> Result<StartConfig, String> {
println!("========读取配置文件========");
let args_config = if let Ok(config) = read_config(config_path) {
config
} else {
return Err("读取配置文件失败".to_string());
};
let log = args_config.log;
if log {
println!("print log");
}
let tap = args_config.tap;
if tap {
println!("use tap");
} else {
println!("use tun");
}
let token = args_config.token;
if token.is_empty() {
return Err(i18n::switch_token_cannot_be_empty_print());
}
if token.len() > 64 {
return Err(i18n::switch_token_cannot_exceed_64_print());
}
println!("token:{:?}", token);
let name = args_config.name;
let name = name.trim();
let name = if name.len() > 64 {
name[..64].to_string()
} else {
name.to_string()
};
println!("name:{:?}", name);
let device_id = if !args_config.device_id.is_empty() {
args_config.device_id
} else {
if let Ok(Some(mac_address)) = mac_address::get_mac_address() {
mac_address.to_string()
} else {
"".to_string()
}
};
if device_id.is_empty() || device_id.len() > 64 {
return Err(i18n::switch_device_id_is_empty_print());
}
println!("device_id:{:?}", device_id);
let in_ips = args_config.in_ips;
let out_ips = args_config.out_ips;
println!("in_ips:{:?}", in_ips);
let in_ips_c = if let Ok(in_ips_c) = ips_parse(&in_ips) {
in_ips_c
} else {
return Err(i18n::switch_in_ips_example_print());
};
println!("out_ips:{:?}", out_ips);
let out_ips_c = if let Ok(out_ips_c) = ips_parse(&out_ips) {
out_ips_c
} else {
return Err(i18n::switch_out_ips_example_print());
};
let server = match {
if !args_config.server.is_empty() {
args_config.server
} else {
"nat1.wherewego.top:29871".to_string()
}
}.to_socket_addrs()
{
Ok(mut server) => {
if let Some(addr) = server.next() {
addr
} else {
return Err(i18n::switch_relay_server_address_error());
}
}
Err(e) => {
return Err(format!("{}:{:?}", i18n::switch_relay_server_address_error(), e));
}
};
println!("中继服务器:{:?}", server);
let nat_test_server = if args_config.nat_test_server.is_empty() {
vec!["nat1.wherewego.top:35061".to_string(), "nat1.wherewego.top:35062".to_string(), "nat2.wherewego.top:35061".to_string(), "nat2.wherewego.top:35062".to_string()]
} else {
args_config.nat_test_server
}.iter().flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
if nat_test_server.is_empty() {
return Err(i18n::switch_nat_test_server_address_error());
}
println!("NAT探测服务器:{:?}", nat_test_server);
let base_config = StartConfig {
tap,
name,
token,
server,
nat_test_server,
device_id,
in_ips: in_ips_c,
out_ips: out_ips_c,
#[cfg(any(unix))]
off_command_server: args_config.off_command_server,
log,
password:args_config.password,
simulate_multicast:args_config.simulate_multicast
};
println!("========参数配置========");
Ok(base_config)
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RuntimeData {
#[serde(default = "default_pid")]
pub pid: u32,
pub command_port: Option<u16>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ArgsConfig {
#[serde(default = "default_false")]
pub tap: bool,
#[serde(default = "default_version")]
pub version: String,
#[serde(default = "default_str")]
pub token: String,
#[serde(default = "default_str")]
pub name: String,
#[serde(default = "default_str")]
pub server: String,
#[serde(default = "default_vec")]
pub nat_test_server: Vec<String>,
#[serde(default = "default_str")]
pub device_id: String,
#[serde(default = "default_vec")]
pub in_ips: Vec<String>,
#[serde(default = "default_vec")]
pub out_ips: Vec<String>,
#[cfg(any(unix))]
#[serde(default = "default_false")]
pub off_command_server: bool,
#[serde(default = "default_false")]
pub log: bool,
pub password: Option<String>,
#[serde(default = "default_false")]
pub simulate_multicast:bool,
}
#[cfg(windows)]
impl ArgsConfig {
pub fn new(start_config: StartConfig) -> ArgsConfig {
let in_ips = start_config.in_ips.iter().map(|(ip, mask, dest)| {
format!("{}/{},{}", Ipv4Addr::from(*ip), subnet_mask_to_integer(*mask), dest)
}).collect::<Vec<String>>();
let out_ips = start_config.out_ips.iter().map(|(ip, mask, dest)| {
format!("{}/{},{}", Ipv4Addr::from(*ip), subnet_mask_to_integer(*mask), dest)
}).collect::<Vec<String>>();
ArgsConfig {
tap: start_config.tap,
version: "1.0.6".to_string(),
token: start_config.token.to_string(),
name: start_config.name.to_string(),
server: start_config.server.to_string(),
nat_test_server: start_config.nat_test_server.iter().map(|v| v.to_string()).collect(),
device_id: start_config.device_id,
in_ips,
out_ips,
log: start_config.log,
#[cfg(any(unix))]
off_command_server: start_config.off_command_server,
password: start_config.password,
simulate_multicast:start_config.simulate_multicast,
}
}
}
#[cfg(windows)]
fn subnet_mask_to_integer(subnet_mask: u32) -> u8 {
let mut mask_bits = subnet_mask;
let mut num_bits = 0;
while mask_bits != 0 {
num_bits += 1;
mask_bits <<= 1;
}
num_bits as u8
}
fn default_false() -> bool {
false
}
fn default_version() -> String {
"1.0.6".to_string()
}
fn default_str() -> String {
"".to_string()
}
fn default_vec() -> Vec<String> {
vec![]
}
fn default_pid() -> u32 {
0
}
// impl ArgsConfig {
// pub fn new(tap: bool, token: String, name: String, server: SocketAddr,
// nat_test_server: &Vec<SocketAddr>, device_id: String,
// in_ips: Vec<(u32, u32, Ipv4Addr)>, out_ips: Vec<(u32, u32, Ipv4Addr)>, ) -> Self {
//
// Self {
// tap,
// version: "1.0".to_string(),
// token,
// name,
// command_port: None,
// server: server.to_string(),
// nat_test_server: nat_test_server.iter().map(|v| v.to_string()).collect::<Vec<String>>(),
// device_id,
// pid: 0,
// }
// }
// }
pub fn lock_file() -> io::Result<File> {
let path = get_home().join(".lock");
let file = File::create(path)?;
file.sync_all()?;
Ok(file)
}
fn save_runtime_data(config: RuntimeData) -> io::Result<()> {
let config_path = get_runtime_data_path();
let str = serde_yaml::to_string(&config).unwrap();
let mut file = File::create(config_path)?;
file.write_all(str.as_bytes())?;
file.sync_all()
}
pub fn update_pid(pid: u32) -> io::Result<()> {
let mut config = read_runtime_data()?;
config.pid = pid;
return save_runtime_data(config);
}
#[cfg(any(unix))]
pub fn read_pid() -> io::Result<u32> {
let config = read_runtime_data()?;
Ok(config.pid)
}
pub fn update_command_port(port: u16) -> io::Result<()> {
let mut config = read_runtime_data()?;
config.command_port = Some(port);
return save_runtime_data(config);
}
pub fn read_command_port() -> io::Result<u16> {
let config = read_runtime_data()?;
if let Some(p) = config.command_port {
Ok(p)
} else {
Err(io::Error::new(io::ErrorKind::Other, "not found config"))
}
}
pub fn get_home() -> PathBuf {
#[cfg(windows)]
{
if let Some(path) = SWITCH_HOME_PATH.lock().as_ref() {
return path.clone();
}
}
let home = dirs::home_dir().unwrap().join(".switch_desktop");
if !home.exists() {
std::fs::create_dir(&home).unwrap();
}
home
}
pub fn get_runtime_data_path() -> PathBuf {
let home = get_home();
home.join(".data")
}
fn read_runtime_data() -> io::Result<RuntimeData> {
let config_path = get_runtime_data_path();
let mut file = if config_path.exists() {
File::open(config_path)?
} else {
OpenOptions::new().read(true).write(true).truncate(false).create(true).open(config_path)?
};
let mut str = String::new();
file.read_to_string(&mut str)?;
match serde_yaml::from_str::<RuntimeData>(&str) {
Ok(config) => Ok(config),
Err(e) => {
log::warn!("{:?}", e);
Err(io::Error::new(io::ErrorKind::Other, "config error"))
}
}
}
fn read_config(config_path: PathBuf) -> io::Result<ArgsConfig> {
let mut file = File::open(config_path)?;
let mut str = String::new();
file.read_to_string(&mut str)?;
match serde_yaml::from_str::<ArgsConfig>(&str) {
Ok(config) => Ok(config),
Err(e) => {
log::warn!("{:?}", e);
Err(io::Error::new(io::ErrorKind::Other, "config error"))
}
}
}
-133
View File
@@ -1,133 +0,0 @@
use console::{style, Style};
use crate::command::entity::{DeviceItem, RouteItem, Status};
pub mod table;
pub fn console_status(status: Status) {
println!("Name: {}", style(status.name).green());
println!("Virtual ip: {}", style(status.virtual_ip).green());
println!("Virtual gateway: {}", style(status.virtual_gateway).green());
println!("Virtual netmask: {}", style(status.virtual_netmask).green());
println!("Connection status: {}", style(status.connect_status).green());
println!("NAT type: {}", style(status.nat_type).green());
println!("Relay server: {}", style(status.relay_server).green());
println!("Public ips: {}", style(status.public_ips).green());
println!("Local ip: {}", style(status.local_ip).green());
}
pub fn console_route_table(mut list: Vec<RouteItem>) {
if list.is_empty() {
println!("No route found");
return;
}
list.sort_by(|t1, t2| t1.destination.cmp(&t2.destination));
let mut out_list = Vec::with_capacity(list.len());
out_list.push(vec![("Destination".to_string(), Style::new()),
("Next Hop".to_string(), Style::new()),
("Metric".to_string(), Style::new()),
("Rt".to_string(), Style::new()),
("Interface".to_string(), Style::new()), ]);
for item in list {
out_list.push(vec![(item.destination, Style::new().green()),
(item.next_hop, Style::new().green()),
(item.metric, Style::new().green()),
(item.rt, Style::new().green()),
(item.interface, Style::new().green())]);
}
table::println_table(out_list)
}
pub fn console_device_list(mut list: Vec<DeviceItem>) {
if list.is_empty() {
println!("No other devices found");
return;
}
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
let mut out_list = Vec::with_capacity(list.len());
//表头
out_list.push(vec![("Name".to_string(), Style::new()),
("Virtual Ip".to_string(), Style::new()),
("Status".to_string(), Style::new()),
("P2P/Relay".to_string(), Style::new()),
("Rt".to_string(), Style::new())]);
for item in list {
if &item.status == "Online" {
if &item.nat_traversal_type == "p2p" {
out_list.push(vec![(item.name, Style::new().green()),
(item.virtual_ip, Style::new().green()),
(item.status, Style::new().green()),
(item.nat_traversal_type, Style::new().green()),
(item.rt, Style::new().green())]);
} else {
out_list.push(vec![(item.name, Style::new().yellow()),
(item.virtual_ip, Style::new().yellow()),
(item.status, Style::new().yellow()),
(item.nat_traversal_type, Style::new().yellow()),
(item.rt, Style::new().yellow())]);
}
} else {
out_list.push(vec![(item.name, Style::new().color256(102)),
(item.virtual_ip, Style::new().color256(102)),
(item.status, Style::new().color256(102)),
("".to_string(), Style::new().color256(102)),
("".to_string(), Style::new().color256(102))]);
}
}
table::println_table(out_list)
}
pub fn console_device_list_all(mut list: Vec<DeviceItem>) {
if list.is_empty() {
println!("No other devices found");
return;
}
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
let mut out_list = Vec::with_capacity(list.len());
//表头
out_list.push(vec![("Name".to_string(), Style::new()),
("Virtual Ip".to_string(), Style::new()),
("Status".to_string(), Style::new()),
("NAT Type".to_string(), Style::new()),
("Public Ips".to_string(), Style::new()),
("Local Ip".to_string(), Style::new()),
("P2P/Relay".to_string(), Style::new()),
("Rt".to_string(), Style::new())]);
for item in list {
if &item.status == "Online" {
if &item.nat_traversal_type == "p2p" {
out_list.push(vec![(item.name, Style::new().green()),
(item.virtual_ip, Style::new().green()),
(item.status, Style::new().green()),
(item.nat_traversal_type, Style::new().green()),
(item.rt, Style::new().green()),
(item.nat_type, Style::new().green()),
(item.public_ips, Style::new().green()),
(item.local_ip, Style::new().green())]);
} else {
out_list.push(vec![(item.name, Style::new().yellow()),
(item.virtual_ip, Style::new().yellow()),
(item.status, Style::new().yellow()),
(item.nat_traversal_type, Style::new().yellow()),
(item.rt, Style::new().yellow()),
(item.nat_type, Style::new().yellow()),
(item.public_ips, Style::new().yellow()),
(item.local_ip, Style::new().yellow()), ]);
}
} else {
out_list.push(vec![(item.name, Style::new().color256(102)),
(item.virtual_ip, Style::new().color256(102)),
(item.status, Style::new().color256(102)),
("".to_string(), Style::new().color256(102)),
("".to_string(), Style::new().color256(102)),
("".to_string(), Style::new().color256(102)),
("".to_string(), Style::new().color256(102)),
("".to_string(), Style::new().color256(102)), ]);
}
}
table::println_table(out_list)
}
-23
View File
@@ -1,23 +0,0 @@
use console::Style;
pub fn println_table(table: Vec<Vec<(String, Style)>>) {
if table.is_empty() {
return;
}
let mut width_list = vec![0; table[0].len()];
for in_list in table.iter() {
for (index, (item, _)) in in_list.iter().enumerate() {
let width = console::measure_text_width(item) + 6;
if width_list[index] < width {
width_list[index] = width;
}
}
}
for in_list in table {
for (col, (item, style)) in in_list.iter().enumerate() {
let str = format!("{:1$}", item, width_list[col]);
print!("{}", style.apply_to(str));
}
println!()
}
}
-217
View File
@@ -1,217 +0,0 @@
#[cfg(target_os = "windows")]
fn get_default_language() -> Option<String> {
use std::process::Command;
use std::str;
let output = Command::new("powershell")
.arg("-Command")
.arg("[System.Globalization.CultureInfo]::CurrentCulture.Name")
.output()
.ok()?;
let language_code = str::from_utf8(&output.stdout)
.ok()?
.trim()
.to_string();
Some(language_code)
}
pub fn init() {
#[cfg(target_os = "windows")]
{
if let Some(l) = get_default_language() {
rust_i18n::set_locale(&l);
}
}
}
pub fn switch_about() -> String {
rust_i18n::t!("switch_about")
}
pub fn switch_usage() -> String {
rust_i18n::t!("switch_usage")
}
pub fn switch_start_about() -> String {
rust_i18n::t!("switch_start_about")
}
pub fn switch_token_help() -> String {
rust_i18n::t!("switch_token_help")
}
pub fn switch_name_help() -> String {
rust_i18n::t!("switch_name_help")
}
pub fn switch_device_id_help() -> String {
rust_i18n::t!("switch_device_id_help")
}
pub fn switch_server_help() -> String {
rust_i18n::t!("switch_server_help")
}
pub fn switch_nat_test_server_help() -> String {
rust_i18n::t!("switch_nat_test_server_help")
}
pub fn switch_log_help() -> String {
rust_i18n::t!("switch_log_help")
}
pub fn switch_tap_help() -> String {
rust_i18n::t!("switch_tap_help")
}
pub fn switch_in_ip_help() -> String {
rust_i18n::t!("switch_in_ip_help")
}
pub fn switch_out_ip_help() -> String {
rust_i18n::t!("switch_out_ip_help")
}
pub fn switch_password_help() -> String {
rust_i18n::t!("switch_password_help")
}
pub fn switch_simulate_multicast_help() -> String {
rust_i18n::t!("switch_simulate_multicast_help")
}
pub fn switch_config_help() -> String {
rust_i18n::t!("switch_config_help")
}
pub fn switch_stop_about() -> String {
rust_i18n::t!("switch_stop_about")
}
pub fn switch_route_about() -> String {
rust_i18n::t!("switch_route_about")
}
pub fn switch_list_about() -> String {
rust_i18n::t!("switch_list_about")
}
pub fn switch_list_all_help() -> String {
rust_i18n::t!("switch_list_all_help")
}
pub fn switch_status_about() -> String {
rust_i18n::t!("switch_status_about")
}
#[cfg(windows)]
pub fn switch_install_about() -> String {
rust_i18n::t!("switch_install_about")
}
#[cfg(windows)]
pub fn switch_path_help() -> String {
rust_i18n::t!("switch_path_help")
}
#[cfg(windows)]
pub fn switch_auto_help() -> String {
rust_i18n::t!("switch_auto_help")
}
#[cfg(windows)]
pub fn switch_uninstall_about() -> String {
rust_i18n::t!("switch_uninstall_about")
}
#[cfg(windows)]
pub fn switch_config_about() -> String {
rust_i18n::t!("switch_config_about")
}
#[cfg(windows)]
pub fn switch_use_root_print() -> String {
rust_i18n::t!("switch_use_admin_print")
}
#[cfg(unix)]
pub fn switch_use_root_print() -> String {
rust_i18n::t!("switch_use_root_print")
}
#[cfg(windows)]
pub fn switch_service_not_start_print() -> String {
rust_i18n::t!("switch_service_not_start_print")
}
pub fn switch_start_successfully_print() -> String {
rust_i18n::t!("switch_start_successfully_print")
}
#[cfg(windows)]
pub fn switch_start_failed_print() -> String {
rust_i18n::t!("switch_start_failed_print")
}
#[cfg(windows)]
pub fn switch_service_not_stopped_print() -> String {
rust_i18n::t!("switch_service_not_stopped_print")
}
#[cfg(windows)]
pub fn switch_server_already_installed_print() -> String {
rust_i18n::t!("switch_server_already_installed_print")
}
pub fn switch_repeated_start_print() -> String {
rust_i18n::t!("switch_repeated_start_print")
}
pub fn switch_stopped_print() -> String {
rust_i18n::t!("switch_stopped_print")
}
pub fn switch_token_not_found_print() -> String {
rust_i18n::t!("switch_token_not_found_print")
}
pub fn switch_token_cannot_be_empty_print() -> String {
rust_i18n::t!("switch_token_cannot_be_empty_print")
}
pub fn switch_token_cannot_exceed_64_print() -> String {
rust_i18n::t!("switch_token_cannot_exceed_64_print")
}
pub fn switch_device_id_is_empty_print() -> String {
rust_i18n::t!("switch_device_id_is_empty_print")
}
pub fn switch_in_ips_example_print() -> String {
rust_i18n::t!("switch_in_ips_example_print")
}
pub fn switch_out_ips_example_print() -> String {
rust_i18n::t!("switch_out_ips_example_print")
}
pub fn switch_relay_server_address_error() -> String {
rust_i18n::t!("switch_relay_server_address_error")
}
pub fn switch_nat_test_server_address_error() -> String {
rust_i18n::t!("switch_nat_test_server_address_error")
}
pub fn switch_press_any_key_to_exit() -> String {
rust_i18n::t!("switch_press_any_key_to_exit")
}
pub fn switch_virtual_ip() -> String {
rust_i18n::t!("switch_virtual_ip")
}
pub fn switch_virtual_gateway() -> String {
rust_i18n::t!("switch_virtual_gateway")
}
pub fn switch_please_enter_the_command() -> String {
rust_i18n::t!("switch_please_enter_the_command")
}
+27
View File
@@ -0,0 +1,27 @@
use std::fs::File;
use std::io;
use std::io::Write;
use std::os::windows::ffi::OsStrExt;
use crate::config::get_home;
const DLL_FILE: &'static [u8] = include_bytes!("../../dll/amd64/wintun.dll");
pub fn load_tun_dll() -> io::Result<()> {
let lib_path = get_home().join("lib");
if !lib_path.exists() {
std::fs::create_dir(&lib_path).unwrap();
}
let dll_path = lib_path.join("wintun.dll");
if !dll_path.exists() {
let mut f = File::create(&dll_path)?;
f.write_all(DLL_FILE)?;
f.sync_data()?;
}
let dll_directory = lib_path.as_os_str();
let dll_directory_wide: Vec<u16> = dll_directory.encode_wide().chain(Some(0)).collect();
unsafe {
winapi::um::winbase::SetDllDirectoryW(dll_directory_wide.as_ptr());
}
Ok(())
}
+206 -249
View File
@@ -1,268 +1,225 @@
use std::thread;
use std::time::Duration;
use clap::{Parser, Subcommand};
use console::style;
use switch::core::Switch;
use crate::config::log_config::log_init;
mod command;
mod config;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(any(unix))]
mod unix;
mod console_out;
mod command_args;
mod i18n;
#[derive(Parser, Debug)]
#[command(
author = "Lu Beilin",
version,
about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信"
)]
pub struct BaseArgs {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// 启动
Start(StartArgs),
/// 停止后台服务
Stop,
/// 安装服务
/// Install service
#[cfg(target_os = "windows")]
Install(InstallArgs),
/// 卸载服务
/// Uninstall service
#[cfg(target_os = "windows")]
Uninstall,
/// 配置
#[cfg(target_os = "windows")]
Config(ConfigArgs),
/// 查看路由
/// View route
Route,
/// 查看设备列表
/// View device list
List {
/// 查看所有
#[arg(short, long)]
all: bool
},
/// 查看设备当前状态
/// View the current status of the device
Status,
}
#[derive(Parser, Debug, Default)]
pub struct StartArgs {
/// 不超过64个字符
/// 相同token的设备之间才能通信。
/// 建议使用uuid保证唯一性。
/// No more than 64 characters
/// Only devices with the same token can communicate with each other.
/// It is recommended to use uuid to ensure uniqueness
#[arg(long)]
token: Option<String>,
/// 给设备一个名称,为空时默认用系统版本信息
/// Give the device a name. If it is blank, the system version information will be used by default
#[arg(long, action)]
name: Option<String>,
/// 设备唯一标识,为空时默认使用MAC地址,不超过64个字符
/// Unique identification of the device. If it is blank, the MAC address is used by default. No more than 64 characters
#[arg(long)]
device_id: Option<String>,
/// 注册和中继服务器地址
/// Register and relay server address
#[arg(long)]
server: Option<String>,
/// NAT检测服务地址,使用逗号分隔
/// NAT detection service address. Use comma to separate
#[arg(long)]
nat_test_server: Option<String>,
/// 关闭命令服务,关闭后不能在其他进程直接使用route、list等命令查看信息
/// Turn off the command service. After turning off, you cannot directly use the route, list and other commands to view information in other processes
#[cfg(any(unix))]
#[arg(long)]
off_command_server: bool,
/// 记录日志,输出在 home/.switch_desktop 目录下,长时间使用时不建议开启
/// Output the log in the "home/.switch_desktop" directory
#[arg(long)]
log: bool,
/// 使用tap网卡
#[arg(long)]
tap: bool,
/// 配置点对网时使用,--in-ip 192.168.10.0/24,10.26.0.3,表示允许接收网段192.168.10.0/24的数据并转发到10.26.0.3
/// Use when configuring peer-to-peer networks
#[arg(long)]
in_ip: Option<Vec<String>>,
/// 配置点对网时使用,--out-ip 192.168.10.0/24,192.168.1.10,表示允许目标为192.168.10.0/24的数据从网卡192.168.1.10转发出去
/// Use when configuring peer-to-peer networks
#[arg(long)]
out_ip: Option<Vec<String>>,
/// 客户端数据加密
#[arg(long)]
password:Option<String>,
/// 模拟组播,默认情况下组播数据会被当作广播发送,兼容性更强,但是会造成流量浪费,开启后会模拟真实组播的数据发送
#[arg(long)]
simulate_multicast:bool,
/// 读取配置文件 --config config_file_path
/// Read configuration file
#[arg(long)]
config: Option<String>,
}
#[cfg(target_os = "windows")]
#[derive(Parser, Debug)]
pub struct InstallArgs {
/// 安装路径
/// Service installation path
#[arg(long)]
path: String,
/// 服务开机自启动
/// Autostart on system startup
#[arg(long)]
auto: bool,
}
#[cfg(target_os = "windows")]
#[derive(Parser, Debug)]
pub struct ConfigArgs {
/// 服务开机自启动
/// Autostart on system startup
#[arg(long)]
auto: bool,
}
#[macro_use]
extern crate rust_i18n;
i18n!("locales", fallback = "en");
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::net::{Ipv4Addr, ToSocketAddrs};
use lazy_static::lazy_static;
use parking_lot::Mutex;
use common::args_parse::ips_parse;
use switch::core::Config;
use switch::core::{Switch, SwitchUtil};
use switch::handle::registration_handler::ReqEnum;
#[cfg(windows)]
mod load_dll;
#[cfg(windows)]
mod config;
lazy_static! {
static ref SWITCH:Mutex<Option<Switch>> = Mutex::new(None);
}
fn main() {
i18n::init();
let args: Vec<_> = std::env::args().collect();
if args.len() == 3 && args[1] == windows::SERVICE_FLAG {
//以服务的方式启动
config::set_win_server_home(std::path::PathBuf::from(&args[2]));
windows::service::start();
return;
} else {
if !command_args::check() {
return;
}
let args = BaseArgs::parse();
if let Commands::Start(start_args) = &args.command {
if start_args.log {
let _ = log_init();
#[cfg(windows)]
{
load_dll::load_tun_dll().unwrap();
}
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![default_value_name,default_value_device_id,connect,close,list])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
fn default_value_name() -> String {
os_info::get().to_string()
}
#[tauri::command]
fn default_value_device_id() -> String {
common::identifier::get_unique_identifier().unwrap_or(String::new())
}
#[tauri::command]
async fn connect(config: ConnectConfig) -> Result<ConnectRegResponse, String> {
let tap = config.tap;
let token = config.token;
let device_id = config.device_id;
let name = config.name;
let server_address_str = config.server_address;
let server_address = match server_address_str.to_socket_addrs() {
Ok(mut addr) => {
if let Some(addr) = addr.next() {
addr
} else {
return Err(String::from("server"));
}
}
windows::main0(args);
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[tokio::main]
async fn main() {
if sudo::RunningAs::Root != sudo::check() {
println!(
"{}",
style("需要使用root权限执行(Need to execute with root permission)...").red()
);
sudo::escalate_if_needed().unwrap();
}
let args = BaseArgs::parse();
if let Commands::Start(start_args) = &args.command {
if start_args.log {
let _ = log_init();
Err(e) => {
return Err(format!("server err:{}", e));
}
}
unix::main0(args).await;
}
};
let nat_test_server = config.nat_test_server.split(" ").flat_map(|a| a.to_socket_addrs()).flatten()
.collect::<Vec<_>>();
let in_ips = config.in_ips.split(" ").into_iter().filter(|e| !e.is_empty()).map(|e| e.to_string()).collect();
let in_ips = match ips_parse(&in_ips) {
Ok(in_ips) => { in_ips }
Err(e) => {
return Err(format!("inIps err:{}", e));
}
};
let out_ips = config.out_ips.split(" ").into_iter().filter(|e| !e.is_empty()).map(|e| e.to_string()).collect();
let out_ips = match ips_parse(&out_ips) {
Ok(out_ips) => { out_ips }
Err(e) => {
return Err(format!("inIps err:{}", e));
}
};
let password = if config.key.is_empty() {
None
} else {
Some(config.key)
};
let simulate_multicast = config.simulate_multicast;
let config = Config::new(tap, token, device_id, name, server_address, server_address_str,
nat_test_server, in_ips, out_ips,
password, simulate_multicast, None);
pub fn console_listen(switch: &Switch) {
use console::Term;
let term = Term::stdout();
println!("{}", style(i18n::switch_start_successfully_print()).green());
let current_device = switch.current_device();
println!("{}: {:?}", i18n::switch_virtual_ip(), style(current_device.virtual_ip()).green());
println!("{}: {:?}", i18n::switch_virtual_gateway(), style(current_device.virtual_gateway()).green());
loop {
println!(
"{}",
style(i18n::switch_please_enter_the_command()).color256(102)
);
match term.read_line() {
Ok(cmd) => {
#[cfg(unix)]
if cmd.is_empty() {
use libc::{STDIN_FILENO, isatty};
if !unsafe { isatty(STDIN_FILENO) != 0 } {
return;
}
}
if command(cmd.trim(), &switch).is_err() {
println!("{}", style("stopping").red());
if let Err(e) = switch.stop() {
println!("stop:{:?}", e);
}
thread::sleep(Duration::from_secs(2));
break;
}
let mut switch_util = SwitchUtil::new(config).await.unwrap();
let mut count = 0;
let response = loop {
match switch_util.connect().await {
Ok(response) => {
break response;
}
Err(e) => {
log::error!("read_line:{:?}", e);
println!("{}", style("stopping...").red());
if let Err(e) = switch.stop() {
log::error!("stop:{:?}", e);
match e {
ReqEnum::TokenError => {
return Err("token error".to_string());
}
ReqEnum::AddressExhausted => {
return Err("address exhausted".to_string());
}
ReqEnum::Timeout => {
count += 1;
if count > 3 {
return Err("connect timeout".to_string());
}
continue;
}
ReqEnum::ServerError(str) => {
return Err(format!("error:{}", str));
}
ReqEnum::Other(str) => {
return Err(format!("error:{}", str));
}
}
thread::sleep(Duration::from_secs(1));
break;
}
}
};
match switch_util.create_iface() {
Ok(_) => {}
Err(e) => {
return Err(format!("create net interface error:{}", e));
}
}
match switch_util.build().await {
Ok(switch) => {
let _ = SWITCH.lock().insert(switch);
}
Err(e) => {
return Err(format!("build switch error:{}", e));
}
}
Ok(ConnectRegResponse {
virtual_ip: response.virtual_ip,
virtual_gateway: response.virtual_gateway,
virtual_netmask: response.virtual_netmask,
})
}
#[tauri::command]
fn list() -> Vec<SwitchPeerItem> {
let mut peer_list = Vec::new();
let mut guard = SWITCH.lock();
match &mut *guard {
None => {}
Some(switch) => {
let mut list = switch.device_list();
let current_device = switch.current_device();
list.sort_unstable_by_key(|v| { (v.status, v.virtual_ip) });
for x in list {
let item = if let Some(route) = switch.route(&x.virtual_ip) {
let connect = if route.is_p2p() {
"p2p".to_string()
} else if route.addr == current_device.connect_server {
"server relay".to_string()
} else {
"client relay".to_string()
};
SwitchPeerItem {
name: x.name,
virtual_ip: x.virtual_ip,
status: format!("{:?}", x.status),
connect,
rt: route.rt.to_string(),
addr: route.addr.to_string(),
}
} else {
SwitchPeerItem {
name: x.name,
virtual_ip: x.virtual_ip,
status: format!("{:?}", x.status),
connect: "".to_string(),
rt: "".to_string(),
addr: "".to_string(),
}
};
peer_list.push(item);
}
}
}
println!("{}", style("stopped").red());
peer_list
}
fn command(cmd: &str, switch: &Switch) -> Result<(), ()> {
match cmd {
"route" => {
let list = command::server::command_route(switch);
console_out::console_route_table(list);
}
"list" => {
let list = command::server::command_list(switch);
console_out::console_device_list(list);
}
"status" => {
let status = command::server::command_status(switch);
console_out::console_status(status);
}
"help" | "h" => {
println!("Options: ");
println!(
"{} , Query the virtual IP of other devices",
style("list").green()
);
println!("{} , View current device status", style("status").green());
println!("{} , Exit the program", style("exit").green());
}
"exit" => {
return Err(());
}
_ => {
println!("command '{}' not found. ", style(cmd).red());
println!("Try to enter: '{}'", style("help").green());
#[tauri::command]
async fn close() {
let switch = SWITCH.lock().take();
match switch {
None => {}
Some(mut switch) => {
switch.stop().unwrap();
switch.wait_stop().await;
}
}
Ok(())
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct ConnectConfig {
pub tap: bool,
pub token: String,
pub device_id: String,
pub name: String,
pub server_address: String,
pub nat_test_server: String,
pub in_ips: String,
pub out_ips: String,
pub key: String,
pub simulate_multicast: bool,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct ConnectRegResponse {
pub virtual_ip: Ipv4Addr,
pub virtual_gateway: Ipv4Addr,
pub virtual_netmask: Ipv4Addr,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct SwitchPeerItem {
pub name: String,
pub virtual_ip: Ipv4Addr,
pub status: String,
pub connect: String,
pub rt: String,
pub addr: String,
}
-132
View File
@@ -1,132 +0,0 @@
use std::sync::Arc;
use console::style;
use fs2::FileExt;
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config};
use crate::command::{command, CommandEnum};
pub async fn main0(base_args: BaseArgs) {
match base_args.command {
Commands::Start(args) => {
let start_config = if let Some(config_path) = &args.config {
match config::read_config_file(config_path.into()) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
} else {
match config::default_config(args) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
};
let off_command_server = start_config.off_command_server;
let config = Config::new(
start_config.tap,
start_config.token.clone(),
start_config.device_id.clone(),
start_config.name.clone(),
start_config.server,
start_config.nat_test_server.clone(),
start_config.in_ips.clone(),
start_config.out_ips.clone(),
start_config.password.clone(),
start_config.simulate_multicast,
);
let lock = match config::lock_file() {
Ok(lock) => {
lock
}
Err(e) => {
log::error!("{:?}",e);
println!("文件锁定失败:{:?}", e);
return;
}
};
if lock.try_lock_exclusive().is_err() {
println!("{}", style("文件被重复打开").red());
return;
}
let switch = match Switch::start(config).await {
Ok(switch) => {
switch
}
Err(e) => {
log::error!("{:?}", e);
println!("启动switch失败:{:?}", e);
lock.unlock().unwrap();
return;
}
};
let switch = Arc::new(switch);
let command_server = crate::command::server::CommandServer::new();
if off_command_server {
crate::console_listen(&switch);
log::info!("前台任务结束");
} else {
if let Err(e) = config::update_pid(std::process::id()) {
log::error!("{:?}", e);
}
let switch1 = switch.clone();
let handle = std::thread::Builder::new().name("cmd-server".into()).spawn(move || {
if let Err(e) = command_server.start(switch1) {
log::error!("{:?}", e);
}
}).unwrap();
crate::console_listen(&switch);
if let Err(e) = handle.join() {
log::error!("后台任务异常{:?}",e);
} else {
log::info!("后台任务结束");
}
}
lock.unlock().unwrap();
}
Commands::Stop => {
command(CommandEnum::Stop);
if let Ok(pid) = config::read_pid() {
if pid != 0 {
let kill_cmd = format!("kill {}", pid);
let kill_out = std::process::Command::new("sh")
.arg("-c")
.arg(&kill_cmd)
.output()
.expect("sh exec error!");
if !kill_out.status.success() {
println!("cmd:{:?},err:{:?}", kill_cmd, kill_out);
return;
}
}
}
println!("stopped")
}
Commands::Route => {
command(CommandEnum::Route);
}
Commands::List { all } => {
if all {
command(CommandEnum::ListAll);
} else {
command(CommandEnum::List);
}
}
Commands::Status => {
command(CommandEnum::Status);
}
}
}
-393
View File
@@ -1,393 +0,0 @@
use std::{io, thread};
use std::ffi::OsString;
use std::net::UdpSocket;
use std::path::PathBuf;
use std::time::Duration;
use console::style;
use fs2::FileExt;
use windows_service::Error;
use windows_service::service::{
ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceState, ServiceType,
};
use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config, i18n};
use crate::command::{command, CommandEnum};
pub mod service;
mod windows_admin_check;
pub const SERVICE_FLAG: &'static str = "start_switch_service_v1_";
pub const SERVICE_NAME: &'static str = "switch-service-v1";
pub const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
fn admin_check() -> bool {
if !windows_admin_check::is_app_elevated() {
println!(
"{}",
style(i18n::switch_use_root_print()).red()
);
true
} else {
false
}
}
fn not_started() -> bool {
match service_state() {
Ok(state) => {
if state == ServiceState::Running {
return false;
} else {
println!("{}", i18n::switch_service_not_start_print())
}
}
Err(e) => {
println!("{:?}", e);
}
}
return true;
}
pub fn main0(base_args: BaseArgs) {
match base_args.command {
Commands::Start(args) => {
if admin_check() {
return;
}
{
// 允许应用通过防火墙
let _udp = UdpSocket::bind("0.0.0.0:0").unwrap();
}
let start_config = if let Some(config_path) = &args.config {
match config::read_config_file(config_path.into()) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
} else {
match config::default_config(args) {
Ok(start_config) => {
start_config
}
Err(e) => {
println!("{}", style(&e).red());
log::error!("{:?}", e);
return;
}
}
};
match service_state() {
Ok(state) => {
if state == ServiceState::Stopped {
match start() {
Ok(_) => {
//需要检查启动状态
thread::sleep(Duration::from_secs(2));
println!("{}", style(i18n::switch_start_successfully_print()).green());
}
Err(e) => {
log::error!("{:?}", e);
println!("{}:{}", style(i18n::switch_start_failed_print()).red(), e);
}
}
} else {
println!("{}", i18n::switch_service_not_stopped_print());
}
}
Err(e) => {
match e {
Error::Winapi(ref e) => {
if let Some(code) = e.raw_os_error() {
if code == 1060 {
//指定的服务未安装。
let config = Config::new(
start_config.tap,
start_config.token,
start_config.device_id,
start_config.name,
start_config.server,
start_config.nat_test_server,
start_config.in_ips,
start_config.out_ips,
start_config.password,
start_config.simulate_multicast,
);
let lock = match config::lock_file() {
Ok(lock) => {
lock
}
Err(e) => {
log::error!("文件锁定失败:{:?}",e);
println!("文件锁定失败:{:?}", e);
return;
}
};
if lock.try_lock_exclusive().is_err() {
println!("{}", style(i18n::switch_repeated_start_print()).red());
return;
}
tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async move {
match Switch::start(config).await {
Ok(switch) => {
crate::console_listen(&switch);
}
Err(e) => {
log::error!("{:?}", e);
println!("启动switch失败:{:?}", e);
}
}
});
lock.unlock().unwrap();
return;
}
}
}
_ => {}
}
println!("{:?}", e);
}
};
pause();
}
Commands::Stop => {
if not_started() {
return;
}
if admin_check() {
return;
}
match stop() {
Ok(_) => {
println!("{}", style(i18n::switch_stopped_print()).green())
}
Err(e) => {
log::error!("{:?}", e);
println!("停止失败:{}", e);
}
}
pause();
}
Commands::Install(args) => {
if admin_check() {
return;
}
if service_state().is_ok() {
println!("{}", i18n::switch_server_already_installed_print());
return;
}
let path: PathBuf = args.path.into();
if !path.exists() {
std::fs::create_dir_all(&path).unwrap();
}
if !path.is_dir() {
println!("参数必须为文件目录(Parameter must be a file directory)");
} else {
if let Err(e) = install(path, args.auto) {
log::error!("{:?}", e);
println!("安装失败:{}", e);
} else {
println!("{}", style("安装成功(Installation succeeded)").green())
}
}
pause();
}
Commands::Uninstall => {
if admin_check() {
return;
}
if service_state().is_err() {
println!("服务未安装");
}
if let Err(e) = uninstall() {
log::error!("{:?}", e);
println!("卸载失败:{}", e);
} else {
println!("{}", style("卸载成功(Uninstall succeeded)").green())
}
pause();
}
Commands::Config(args) => {
if service_state().is_err() {
println!("服务未安装");
}
if let Err(e) = change(args.auto) {
log::error!("{:?}", e);
println!("配置失败:{}", e);
} else {
println!("{}", style("配置成功(Config succeeded)").green())
}
pause();
}
Commands::Route => {
if not_started() {
return;
}
command(CommandEnum::Route);
}
Commands::List { all } => {
if not_started() {
return;
}
if all {
command(CommandEnum::ListAll);
} else {
command(CommandEnum::List);
}
}
Commands::Status => {
if not_started() {
return;
}
command(CommandEnum::Status);
}
}
}
fn pause() {
println!(
"{}",
style(i18n::switch_press_any_key_to_exit()).green()
);
use console::Term;
let term = Term::stdout();
let _ = term.read_char().unwrap();
}
fn install(mut path: PathBuf, auto: bool) -> Result<(), Error> {
if !path.is_absolute() {
path = path.canonicalize().unwrap();
}
let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let current_exe_path = std::env::current_exe().unwrap();
let service_path = path.join("switch-service-v1.exe");
std::fs::copy(current_exe_path, service_path.as_path()).unwrap();
if let Err(e) = std::fs::copy("wintun.dll", path.join("wintun.dll").as_path()) {
if e.kind() == io::ErrorKind::NotFound {
println!("'wintun.dll' not found. Please put 'wintun.dll' in the current directory");
std::process::exit(0);
} else {
panic!("{:?}", e)
}
}
let mut launch_arguments = Vec::new();
launch_arguments.push(OsString::from(SERVICE_FLAG));
launch_arguments.push(OsString::from(
config::get_home().to_str().unwrap(),
));
let start_type = if auto {
ServiceStartType::AutoStart
} else {
ServiceStartType::OnDemand
};
let service_info = ServiceInfo {
name: OsString::from(SERVICE_NAME),
display_name: OsString::from("switch service v1"),
service_type: SERVICE_TYPE,
start_type,
error_control: ServiceErrorControl::Normal,
executable_path: service_path.into(),
launch_arguments,
dependencies: vec![],
account_name: None, // run as System
account_password: None,
};
let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
service.set_description("A VPN")?;
Ok(())
}
fn change(auto: bool) -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_CONFIG | ServiceAccess::CHANGE_CONFIG;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let config = service.query_config()?;
let start_type = if auto {
ServiceStartType::AutoStart
} else {
ServiceStartType::OnDemand
};
let executable_path = config.executable_path.to_string_lossy().to_string();
let executable_path = if executable_path.starts_with('"') && executable_path.ends_with('"') {
&executable_path[1..executable_path.len() - 1]
} else {
&executable_path
};
let mut split = executable_path.split(SERVICE_FLAG);
let executable_path = split.next().unwrap().trim();
let executable_path = if executable_path.starts_with('"') && executable_path.ends_with('"') {
PathBuf::from(&executable_path[1..executable_path.len() - 1])
} else {
PathBuf::from(executable_path)
};
let home_path = split.next().unwrap().trim();
let launch_arguments = vec![OsString::from(SERVICE_FLAG), OsString::from(home_path)];
let service_info = ServiceInfo {
name: OsString::from(SERVICE_NAME),
display_name: config.display_name,
service_type: SERVICE_TYPE,
start_type,
error_control: config.error_control,
executable_path,
launch_arguments,
dependencies: config.dependencies,
account_name: None, // run as System
account_password: None,
};
service.change_config(&service_info)?;
Ok(())
}
fn uninstall() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let service_status = service.query_status()?;
if service_status.current_state != ServiceState::Stopped {
service.stop()?;
// Wait for service to stop
thread::sleep(Duration::from_secs(1));
}
service.delete()?;
Ok(())
}
fn start() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::START)?;
let args: Vec<_> = std::env::args().collect();
service.start(&args[1..])
}
fn service_state() -> Result<ServiceState, Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service_access = ServiceAccess::QUERY_STATUS;
let service = service_manager.open_service(SERVICE_NAME, service_access)?;
let service_status = service.query_status()?;
return Ok(service_status.current_state);
}
fn stop() -> Result<(), Error> {
let manager_access = ServiceManagerAccess::CONNECT;
let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
let service = service_manager.open_service(SERVICE_NAME, ServiceAccess::STOP)?;
service.stop()?;
Ok(())
}
-202
View File
@@ -1,202 +0,0 @@
// #[macro_use]
// extern crate windows_service;
use std::ffi::OsString;
use std::sync::Arc;
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::time::Duration;
use clap::Parser;
use windows_service::{define_windows_service, service_control_handler, service_dispatcher};
use windows_service::service::{
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
};
use windows_service::service_control_handler::ServiceControlHandlerResult;
use switch::core::{Config, Switch};
use crate::{BaseArgs, Commands, config};
use crate::windows::SERVICE_NAME;
define_windows_service!(ffi_service_main, switch_service_main);
pub fn switch_service_main(arguments: Vec<OsString>) {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
match service_main(arguments).await {
Ok(_) => {}
Err(e) => {
log::error!("启动服务失败:{:?}",e);
}
}
})
}
async fn service_main(arguments: Vec<OsString>) -> windows_service::Result<()> {
let parker = crossbeam::sync::Parker::new();
let un_parker = parker.unparker().clone();
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
// Notifies a service to report its current status information to the service
// control manager. Always return NoError even if not implemented.
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
// Handle stop
ServiceControl::Stop => {
un_parker.unpark();
log::info!("handler 服务停止");
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
// Register system service event handler.
// The returned status handle should be used to report service status changes to the system.
let status_handle =
service_control_handler::register(SERVICE_NAME, event_handler)?;
// Tell the system that service is running
status_handle.set_service_status(ServiceStatus {
service_type: crate::windows::SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})?;
match start_switch(arguments).await {
Ok(_) => {
parker.park();
}
Err(e) => {
log::error!("服务启动失败 {:?}",e);
}
}
status_handle.set_service_status(ServiceStatus {
service_type: crate::windows::SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})
}
fn auto_config_path() -> io::Result<PathBuf> {
Ok(config::get_win_server_home().join("auto_config.yaml"))
}
fn save_auto_config(start_config: config::StartConfig) -> io::Result<()> {
let mut file = std::fs::File::create(auto_config_path()?)?;
let config = config::ArgsConfig::new(start_config);
match serde_yaml::to_string(&config) {
Ok(yaml) => {
file.write_all(yaml.as_bytes())
}
Err(e) => {
Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", e)))
}
}
}
async fn start_switch(arguments: Vec<OsString>) -> switch::Result<()> {
let start_config = match BaseArgs::try_parse_from(arguments) {
Ok(args) => {
match args.command {
Commands::Start(args) => {
if args.log {
let _ = config::log_config::log_service_init();
}
if let Some(config_path) = &args.config {
match config::read_config_file(config_path.into()) {
Ok(start_config) => {
if let Err(e) = save_auto_config(start_config.clone()) {
log::warn!("配置文件保存失败:{:?}",e);
}
start_config
}
Err(e) => {
log::error!("{:?}", e);
return Err(switch::error::Error::Stop(e));
}
}
} else {
match config::default_config(args) {
Ok(start_config) => {
if let Err(e) = save_auto_config(start_config.clone()) {
log::warn!("配置文件保存失败:{:?}",e);
}
start_config
}
Err(e) => {
log::error!("{:?}", e);
return Err(switch::error::Error::Stop(e));
}
}
}
}
_ => {
return Err(switch::error::Error::Stop("配置文件错误".to_string()));
}
}
}
Err(_) => {
match config::read_config_file(auto_config_path()?) {
Ok(start_config) => {
if start_config.log {
let _ = config::log_config::log_service_init();
}
start_config
}
Err(e) => {
return Err(switch::error::Error::Stop(e));
}
}
}
};
let config = Config::new(
start_config.tap,
start_config.token,
start_config.device_id,
start_config.name,
start_config.server,
start_config.nat_test_server,
start_config.in_ips,
start_config.out_ips,
start_config.password,
start_config.simulate_multicast,
);
log::info!("switch-service服务启动");
tokio::spawn(async move {
match Switch::start(config).await {
Ok(switch) => {
let switch = Arc::new(switch);
let command_server = crate::command::server::CommandServer::new();
if let Err(e) = config::update_pid(std::process::id()) {
log::error!("{:?}", e);
}
if let Err(e) = command_server.start(switch) {
log::error!("{:?}", e);
}
}
Err(e) => {
log::error!("{:?}", e);
}
};
});
Ok(())
}
pub fn start() {
log::info!("以服务的方式启动");
service_dispatcher::start(SERVICE_NAME, ffi_service_main).unwrap();
}
@@ -1,76 +0,0 @@
/// 使用 https://github.com/spa5k/is_sudo/blob/main/src/window.rs
use std::io::Error;
use std::ptr;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
use winapi::um::securitybaseapi::GetTokenInformation;
use winapi::um::winnt::{TokenElevation, HANDLE, TOKEN_ELEVATION, TOKEN_QUERY};
// Use std::io::Error::last_os_error for errors.
// NOTE: For this example I'm simple passing on the OS error.
// However, customising the error could provide more context
/// Returns true if the current process has admin rights, otherwise false.
pub fn is_app_elevated() -> bool {
_is_app_elevated().unwrap_or(false)
}
/// On success returns a bool indicating if the current process has admin rights.
/// Otherwise returns an OS error.
///
/// This is unlikely to fail but if it does it's even more unlikely that you have admin permissions anyway.
/// Therefore the public function above simply eats the error and returns a bool.
fn _is_app_elevated() -> Result<bool, Error> {
let token = QueryAccessToken::from_current_process()?;
token.is_elevated()
}
/// A safe wrapper around querying Windows access tokens.
pub struct QueryAccessToken(HANDLE);
impl QueryAccessToken {
pub fn from_current_process() -> Result<Self, Error> {
unsafe {
let mut handle: HANDLE = ptr::null_mut();
let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle);
if result != 0 {
Ok(Self(handle))
} else {
Err(Error::last_os_error())
}
}
}
/// On success returns a bool indicating if the access token has elevated privilidges.
/// Otherwise returns an OS error.
pub fn is_elevated(&self) -> Result<bool, Error> {
unsafe {
let mut elevation = TOKEN_ELEVATION::default();
let size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
let mut ret_size = size;
// The weird looking repetition of `as *mut _` is casting the reference to a c_void pointer.
if GetTokenInformation(
self.0,
TokenElevation,
&mut elevation as *mut _ as *mut _,
size,
&mut ret_size,
) != 0
{
Ok(elevation.TokenIsElevated != 0)
} else {
Err(Error::last_os_error())
}
}
}
}
impl Drop for QueryAccessToken {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { CloseHandle(self.0) };
}
}
}