Support specify csd-wrapper

This commit is contained in:
Kevin Yue 2024-02-04 07:47:11 +08:00
parent 662e4d0b8a
commit 1cf31449d6
10 changed files with 124 additions and 10 deletions

1
Cargo.lock generated
View File

@ -1438,6 +1438,7 @@ dependencies = [
"roxmltree",
"serde",
"serde_json",
"serde_urlencoded",
"specta",
"specta-macros",
"tauri",

View File

@ -43,6 +43,7 @@ thiserror = "1"
redact-engine = "0.1"
dotenvy_macro = "0.15"
compile-time = "0.2"
serde_urlencoded = "0.7"
[profile.release]
opt-level = 'z' # Optimize for size

View File

@ -10,7 +10,7 @@ use tokio::sync::{mpsc, oneshot, watch, RwLock};
use tokio_util::sync::CancellationToken;
pub(crate) struct VpnTaskContext {
vpn_handle: Arc<RwLock<Option<Vpn>>>,
vpn_handle: Arc<std::sync::RwLock<Option<Vpn>>>,
vpn_state_tx: Arc<watch::Sender<VpnState>>,
disconnect_rx: RwLock<Option<oneshot::Receiver<()>>>,
}
@ -32,16 +32,18 @@ impl VpnTaskContext {
}
let info = req.info().clone();
let vpn_handle = self.vpn_handle.clone();
let vpn_handle = Arc::clone(&self.vpn_handle);
let args = req.args();
let vpn = Vpn::builder(req.gateway().server(), args.cookie())
.user_agent(args.user_agent())
.script(args.vpnc_script())
.csd_uid(args.csd_uid())
.csd_wrapper(args.csd_wrapper())
.os(args.openconnect_os())
.build();
// Save the VPN handle
vpn_handle.write().await.replace(vpn);
vpn_handle.write().unwrap().replace(vpn);
let vpn_state_tx = self.vpn_state_tx.clone();
let connect_info = Box::new(info.clone());
@ -55,17 +57,30 @@ impl VpnTaskContext {
thread::spawn(move || {
let vpn_state_tx_clone = vpn_state_tx.clone();
vpn_handle.blocking_read().as_ref().map(|vpn| {
vpn.connect(move || {
let connect_info = Box::new(info.clone());
vpn_state_tx.send(VpnState::Connected(connect_info)).ok();
if let Err(err) = vpn_handle.read().map(|vpn| {
vpn.as_ref().map(|vpn| {
vpn.connect(move || {
let connect_info = Box::new(info.clone());
vpn_state_tx.send(VpnState::Connected(connect_info)).ok();
})
})
});
}) {
info!("VPN connect failed: {:?}", err);
}
// .as_ref().map(|vpn| {
// vpn.connect(move || {
// let connect_info = Box::new(info.clone());
// vpn_state_tx.send(VpnState::Connected(connect_info)).ok();
// })
// });
// println!("VPN connect result: {:?}", ret);
// Notify the VPN is disconnected
vpn_state_tx_clone.send(VpnState::Disconnected).ok();
// Remove the VPN handle
vpn_handle.blocking_write().take();
// vpn_handle.blocking_write().take();
vpn_handle.write().unwrap().take();
disconnect_tx.send(()).ok();
});
@ -73,7 +88,9 @@ impl VpnTaskContext {
pub async fn disconnect(&self) {
if let Some(disconnect_rx) = self.disconnect_rx.write().await.take() {
if let Some(vpn) = self.vpn_handle.read().await.as_ref() {
info!("Disconnecting VPN...");
if let Some(vpn) = self.vpn_handle.read().unwrap().as_ref() {
info!("VPN is connected, start disconnecting...");
self.vpn_state_tx.send(VpnState::Disconnecting).ok();
vpn.disconnect()
}

View File

@ -25,6 +25,7 @@ url.workspace = true
regex.workspace = true
dotenvy_macro.workspace = true
uzers.workspace = true
serde_urlencoded.workspace = true
tauri = { workspace = true, optional = true }
clap = { workspace = true, optional = true }

View File

@ -0,0 +1,51 @@
use std::collections::HashMap;
use anyhow::bail;
use reqwest::Client;
use crate::{gp_params::GpParams, utils::normalize_server};
async fn retrieve_config(gateway: &str, cookie: &str, gp_params: &GpParams) -> anyhow::Result<()> {
let url = normalize_server(gateway)?;
let config_url = format!("{}/ssl-vpn/getconfig.esp", url);
let client = Client::builder()
.danger_accept_invalid_certs(gp_params.ignore_tls_errors())
.user_agent(gp_params.user_agent())
.build()?;
let mut params = serde_urlencoded::from_str::<HashMap<&str, &str>>(cookie)?;
println!("{:?}", params);
params.insert("client-type", "1");
params.insert("protocol-version", "p1");
params.insert("internal", "no");
params.insert("ipv6-support", "yes");
params.insert("clientos", gp_params.client_os());
params.insert("hmac-algo", "sha1,md5,sha256");
params.insert("enc-algo", "aes-128-cbc,aes-256-cbc");
if let Some(os_version) = gp_params.os_version() {
params.insert("os-version", os_version);
}
if let Some(client_version) = gp_params.client_version() {
params.insert("app-version", client_version);
}
let res = client.post(&config_url).form(&params).send().await?;
let status = res.status();
if status.is_client_error() || status.is_server_error() {
bail!("Retrieve config error: {}", status)
}
let res_xml = res.text().await?;
println!("{}", res_xml);
Ok(())
}
pub async fn hip_report(gateway: &str, cookie: &str, gp_params: &GpParams) -> anyhow::Result<()> {
retrieve_config(gateway, cookie, gp_params).await
}

View File

@ -1,5 +1,6 @@
mod login;
mod parse_gateways;
pub mod hip;
pub use login::*;
pub(crate) use parse_gateways::*;

View File

@ -83,6 +83,18 @@ impl GpParams {
self.prefer_default_browser
}
pub fn client_os(&self) -> &str {
self.client_os.as_str()
}
pub fn os_version(&self) -> Option<&str> {
self.os_version.as_deref()
}
pub fn client_version(&self) -> Option<&str> {
self.client_version.as_deref()
}
pub(crate) fn to_params(&self) -> HashMap<&str, &str> {
let mut params: HashMap<&str, &str> = HashMap::new();
let client_os = self.client_os.as_str();

View File

@ -23,6 +23,11 @@ pub fn get_non_root_user() -> anyhow::Result<User> {
Ok(user)
}
pub fn get_current_user() -> anyhow::Result<User> {
let current_user = whoami::username();
get_user_by_name(&current_user)
}
fn get_real_user() -> anyhow::Result<User> {
// Read the UID from SUDO_UID or PKEXEC_UID environment variable if available.
let uid = match env::var("SUDO_UID") {

View File

@ -32,6 +32,8 @@ pub struct ConnectArgs {
cookie: String,
vpnc_script: Option<String>,
user_agent: Option<String>,
csd_uid: u32,
csd_wrapper: Option<String>,
os: Option<ClientOs>,
}
@ -42,6 +44,8 @@ impl ConnectArgs {
vpnc_script: None,
user_agent: None,
os: None,
csd_uid: 0,
csd_wrapper: None,
}
}
@ -60,6 +64,14 @@ impl ConnectArgs {
pub fn openconnect_os(&self) -> Option<String> {
self.os.as_ref().map(|os| os.to_openconnect_os().to_string())
}
pub fn csd_uid(&self) -> u32 {
self.csd_uid
}
pub fn csd_wrapper(&self) -> Option<String> {
self.csd_wrapper.clone()
}
}
#[derive(Debug, Deserialize, Serialize, Type)]
@ -81,6 +93,16 @@ impl ConnectRequest {
self
}
pub fn with_csd_uid(mut self, csd_uid: u32) -> Self {
self.args.csd_uid = csd_uid;
self
}
pub fn with_csd_wrapper<T: Into<Option<String>>>(mut self, csd_wrapper: T) -> Self {
self.args.csd_wrapper = csd_wrapper.into();
self
}
pub fn with_user_agent<T: Into<Option<String>>>(mut self, user_agent: T) -> Self {
self.args.user_agent = user_agent.into();
self

View File

@ -143,6 +143,9 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
void vpn_disconnect()
{
char cmd = OC_CMD_CANCEL;
INFO("Stopping VPN connection: %d", g_cmd_pipe_fd);
if (write(g_cmd_pipe_fd, &cmd, 1) < 0)
{
ERROR("Failed to write to command pipe, VPN connection may not be stopped");