Compare commits

...

3 Commits

Author SHA1 Message Date
Kevin Yue
90a8c11acb feat: add disable_ipv6 option (related #364) 2024-05-19 09:04:45 +08:00
Kevin Yue
92b858884c fix: check executable for file 2024-05-10 10:26:45 -04:00
Kevin Yue
159673652c Refactor prelogin.rs to use default labels for username and password 2024-05-09 01:48:02 -04:00
9 changed files with 86 additions and 42 deletions

View File

@@ -50,6 +50,8 @@ pub(crate) struct ConnectArgs {
#[arg(short, long, help = "Request MTU from server (legacy servers only)")]
mtu: Option<u32>,
#[arg(long, help = "Do not ask for IPv6 connectivity")]
disable_ipv6: bool,
#[arg(long, default_value = GP_USER_AGENT, help = "The user agent to use")]
user_agent: String,
@@ -216,6 +218,7 @@ impl<'a> ConnectHandler<'a> {
.csd_uid(csd_uid)
.csd_wrapper(csd_wrapper)
.mtu(mtu)
.disable_ipv6(self.args.disable_ipv6)
.build()?;
let vpn = Arc::new(vpn);

View File

@@ -42,6 +42,7 @@ impl VpnTaskContext {
.csd_wrapper(args.csd_wrapper())
.mtu(args.mtu())
.os(args.openconnect_os())
.disable_ipv6(args.disable_ipv6())
.build()
{
Ok(vpn) => vpn,

View File

@@ -1,7 +1,6 @@
use is_executable::IsExecutable;
use std::path::Path;
use std::{io, path::Path};
pub use is_executable::is_executable;
use is_executable::IsExecutable;
const VPNC_SCRIPT_LOCATIONS: [&str; 6] = [
"/usr/local/share/vpnc-scripts/vpnc-script",
@@ -39,3 +38,17 @@ pub fn find_vpnc_script() -> Option<String> {
pub fn find_csd_wrapper() -> Option<String> {
find_executable(&CSD_WRAPPER_LOCATIONS)
}
/// If file exists, check if it is executable
pub fn check_executable(file: &str) -> Result<(), io::Error> {
let path = Path::new(file);
if path.exists() && !path.is_executable() {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
format!("{} is not executable", file),
));
}
Ok(())
}

View File

@@ -181,22 +181,24 @@ fn parse_res_xml(res_xml: &str, is_gateway: bool) -> anyhow::Result<Prelogin> {
return Ok(Prelogin::Saml(saml_prelogin));
}
let label_username = xml::get_child_text(&doc, "username-label");
let label_password = xml::get_child_text(&doc, "password-label");
// Check if the prelogin response is standard login
if label_username.is_some() && label_password.is_some() {
let label_username = xml::get_child_text(&doc, "username-label").unwrap_or_else(|| {
info!("Username label has no value, using default");
String::from("Username")
});
let label_password = xml::get_child_text(&doc, "password-label").unwrap_or_else(|| {
info!("Password label has no value, using default");
String::from("Password")
});
let auth_message =
xml::get_child_text(&doc, "authentication-message").unwrap_or(String::from("Please enter the login credentials"));
let standard_prelogin = StandardPrelogin {
region,
is_gateway,
auth_message,
label_username: label_username.unwrap(),
label_password: label_password.unwrap(),
label_username,
label_password,
};
Ok(Prelogin::Standard(standard_prelogin))
} else {
Err(anyhow!("Invalid prelogin response"))
}
}

View File

@@ -36,6 +36,7 @@ pub struct ConnectArgs {
csd_wrapper: Option<String>,
mtu: u32,
os: Option<ClientOs>,
disable_ipv6: bool,
}
impl ConnectArgs {
@@ -48,6 +49,7 @@ impl ConnectArgs {
csd_uid: 0,
csd_wrapper: None,
mtu: 0,
disable_ipv6: false,
}
}
@@ -78,6 +80,10 @@ impl ConnectArgs {
pub fn mtu(&self) -> u32 {
self.mtu
}
pub fn disable_ipv6(&self) -> bool {
self.disable_ipv6
}
}
#[derive(Debug, Deserialize, Serialize, Type)]
@@ -109,11 +115,6 @@ impl ConnectRequest {
self
}
pub fn with_mtu(mut self, mtu: u32) -> Self {
self.args.mtu = mtu;
self
}
pub fn with_user_agent<T: Into<Option<String>>>(mut self, user_agent: T) -> Self {
self.args.user_agent = user_agent.into();
self
@@ -124,6 +125,16 @@ impl ConnectRequest {
self
}
pub fn with_mtu(mut self, mtu: u32) -> Self {
self.args.mtu = mtu;
self
}
pub fn with_disable_ipv6(mut self, disable_ipv6: bool) -> Self {
self.args.disable_ipv6 = disable_ipv6;
self
}
pub fn gateway(&self) -> &Gateway {
self.info.gateway()
}

View File

@@ -20,6 +20,7 @@ pub(crate) struct ConnectOptions {
pub csd_wrapper: *const c_char,
pub mtu: u32,
pub disable_ipv6: u32,
}
#[link(name = "vpn")]

View File

@@ -64,6 +64,7 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
INFO("CSD_USER: %d", options->csd_uid);
INFO("CSD_WRAPPER: %s", options->csd_wrapper);
INFO("MTU: %d", options->mtu);
INFO("DISABLE_IPV6: %d", options->disable_ipv6);
vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL);
@@ -103,6 +104,10 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback)
openconnect_set_reqmtu(vpninfo, mtu);
}
if (options->disable_ipv6) {
openconnect_disable_ipv6(vpninfo);
}
g_cmd_pipe_fd = openconnect_setup_cmd_pipe(vpninfo);
if (g_cmd_pipe_fd < 0)
{

View File

@@ -21,6 +21,8 @@ typedef struct vpn_options
const char *csd_wrapper;
const int mtu;
const int disable_ipv6;
} vpn_options;
int vpn_connect(const vpn_options *options, vpn_connected_callback callback);
@@ -35,7 +37,7 @@ static char *format_message(const char *format, va_list args)
int len = vsnprintf(NULL, 0, format, args_copy);
va_end(args_copy);
char *buffer = malloc(len + 1);
char *buffer = (char*)malloc(len + 1);
if (buffer == NULL)
{
return NULL;

View File

@@ -4,7 +4,7 @@ use std::{
sync::{Arc, RwLock},
};
use common::vpn_utils::{find_vpnc_script, is_executable};
use common::vpn_utils::{check_executable, find_vpnc_script};
use log::info;
use crate::ffi;
@@ -24,6 +24,7 @@ pub struct Vpn {
csd_wrapper: Option<CString>,
mtu: u32,
disable_ipv6: bool,
callback: OnConnectedCallback,
}
@@ -68,6 +69,7 @@ impl Vpn {
csd_wrapper: Self::option_to_ptr(&self.csd_wrapper),
mtu: self.mtu,
disable_ipv6: self.disable_ipv6 as u32,
}
}
@@ -80,23 +82,23 @@ impl Vpn {
}
#[derive(Debug)]
pub struct VpnError<'a> {
message: &'a str,
pub struct VpnError {
message: String,
}
impl<'a> VpnError<'a> {
fn new(message: &'a str) -> Self {
impl VpnError {
fn new(message: String) -> Self {
Self { message }
}
}
impl fmt::Display for VpnError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl fmt::Display for VpnError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for VpnError<'_> {}
impl std::error::Error for VpnError {}
pub struct VpnBuilder {
server: String,
@@ -110,6 +112,7 @@ pub struct VpnBuilder {
csd_wrapper: Option<String>,
mtu: u32,
disable_ipv6: bool,
}
impl VpnBuilder {
@@ -126,6 +129,7 @@ impl VpnBuilder {
csd_wrapper: None,
mtu: 0,
disable_ipv6: false,
}
}
@@ -159,21 +163,22 @@ impl VpnBuilder {
self
}
pub fn build(self) -> Result<Vpn, VpnError<'static>> {
pub fn disable_ipv6(mut self, disable_ipv6: bool) -> Self {
self.disable_ipv6 = disable_ipv6;
self
}
pub fn build(self) -> Result<Vpn, VpnError> {
let script = match self.script {
Some(script) => {
if !is_executable(&script) {
return Err(VpnError::new("vpnc script is not executable"));
}
check_executable(&script).map_err(|e| VpnError::new(e.to_string()))?;
script
}
None => find_vpnc_script().ok_or_else(|| VpnError::new("Failed to find vpnc-script"))?,
None => find_vpnc_script().ok_or_else(|| VpnError::new(String::from("Failed to find vpnc-script")))?,
};
if let Some(csd_wrapper) = &self.csd_wrapper {
if !is_executable(csd_wrapper) {
return Err(VpnError::new("CSD wrapper is not executable"));
}
check_executable(csd_wrapper).map_err(|e| VpnError::new(e.to_string()))?;
}
let user_agent = self.user_agent.unwrap_or_default();
@@ -192,6 +197,7 @@ impl VpnBuilder {
csd_wrapper: self.csd_wrapper.as_deref().map(Self::to_cstring),
mtu: self.mtu,
disable_ipv6: self.disable_ipv6,
callback: Default::default(),
})