mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			v2.3.4
			...
			a1c63f8498
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | a1c63f8498 | ||
|  | 9460d498fc | ||
|  | c2a6a436a5 | ||
|  | c578e35178 | 
							
								
								
									
										1
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -11,6 +11,7 @@ | ||||
|         "distro", | ||||
|         "dotenv", | ||||
|         "dotenvy", | ||||
|         "dtls", | ||||
|         "getconfig", | ||||
|         "globalprotect", | ||||
|         "globalprotectcallback", | ||||
|   | ||||
							
								
								
									
										44
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										44
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -1459,6 +1459,7 @@ dependencies = [ | ||||
|  "url", | ||||
|  "urlencoding", | ||||
|  "uzers", | ||||
|  "which", | ||||
|  "whoami", | ||||
| ] | ||||
|  | ||||
| @@ -1676,6 +1677,15 @@ version = "0.4.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" | ||||
|  | ||||
| [[package]] | ||||
| name = "home" | ||||
| version = "0.5.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" | ||||
| dependencies = [ | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "html-escape" | ||||
| version = "0.2.13" | ||||
| @@ -2153,9 +2163,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||||
|  | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.151" | ||||
| version = "0.2.155" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" | ||||
| checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" | ||||
|  | ||||
| [[package]] | ||||
| name = "libredox" | ||||
| @@ -2548,9 +2558,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "openssl" | ||||
| version = "0.10.62" | ||||
| version = "0.10.66" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" | ||||
| checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" | ||||
| dependencies = [ | ||||
|  "bitflags 2.4.1", | ||||
|  "cfg-if", | ||||
| @@ -2589,9 +2599,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "openssl-sys" | ||||
| version = "0.9.98" | ||||
| version = "0.9.103" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" | ||||
| checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" | ||||
| dependencies = [ | ||||
|  "cc", | ||||
|  "libc", | ||||
| @@ -3234,9 +3244,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "rustix" | ||||
| version = "0.38.28" | ||||
| version = "0.38.34" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" | ||||
| checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" | ||||
| dependencies = [ | ||||
|  "bitflags 2.4.1", | ||||
|  "errno", | ||||
| @@ -4798,6 +4808,18 @@ dependencies = [ | ||||
|  "windows-metadata", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "which" | ||||
| version = "6.0.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075" | ||||
| dependencies = [ | ||||
|  "either", | ||||
|  "home", | ||||
|  "rustix", | ||||
|  "winsafe", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "whoami" | ||||
| version = "1.5.1" | ||||
| @@ -5104,6 +5126,12 @@ dependencies = [ | ||||
|  "windows-sys 0.48.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "winsafe" | ||||
| version = "0.0.19" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" | ||||
|  | ||||
| [[package]] | ||||
| name = "wry" | ||||
| version = "0.24.7" | ||||
|   | ||||
| @@ -46,6 +46,7 @@ compile-time = "0.2" | ||||
| serde_urlencoded = "0.7" | ||||
| md5="0.7" | ||||
| sha256="1" | ||||
| which="6" | ||||
|  | ||||
| # Tauri dependencies | ||||
| tauri = { version = "1.5" } | ||||
|   | ||||
| @@ -40,6 +40,8 @@ struct Cli { | ||||
|   clean: bool, | ||||
|   #[arg(long)] | ||||
|   default_browser: bool, | ||||
|   #[arg(long)] | ||||
|   external_browser: Option<String>, | ||||
| } | ||||
|  | ||||
| impl Cli { | ||||
| @@ -59,8 +61,15 @@ impl Cli { | ||||
|       None => portal_prelogin(&self.server, &gp_params).await?, | ||||
|     }; | ||||
|  | ||||
|     if self.default_browser { | ||||
|       let browser_auth = BrowserAuthenticator::new(&saml_request); | ||||
|     let browser_auth = if let Some(external_browser) = &self.external_browser { | ||||
|       Some(BrowserAuthenticator::new_with_browser(&saml_request, external_browser)) | ||||
|     } else if self.default_browser { | ||||
|       Some(BrowserAuthenticator::new(&saml_request)) | ||||
|     } else { | ||||
|       None | ||||
|     }; | ||||
|  | ||||
|     if let Some(browser_auth) = browser_auth { | ||||
|       browser_auth.authenticate()?; | ||||
|  | ||||
|       info!("Please continue the authentication process in the default browser"); | ||||
|   | ||||
| @@ -86,6 +86,9 @@ pub(crate) struct ConnectArgs { | ||||
|   #[arg(long)] | ||||
|   os_version: Option<String>, | ||||
|  | ||||
|   #[arg(long, help = "Disable DTLS and ESP")] | ||||
|   no_dtls: bool, | ||||
|  | ||||
|   #[arg(long, help = "The HiDPI mode, useful for high resolution screens")] | ||||
|   hidpi: bool, | ||||
|  | ||||
| @@ -94,6 +97,12 @@ pub(crate) struct ConnectArgs { | ||||
|  | ||||
|   #[arg(long, help = "Use the default browser to authenticate")] | ||||
|   default_browser: bool, | ||||
|  | ||||
|   #[arg( | ||||
|     long, | ||||
|     help = "Use the specified browser to authenticate, e.g., firefox, chromium, chrome, or the path to the browser" | ||||
|   )] | ||||
|   external_browser: Option<String>, | ||||
| } | ||||
|  | ||||
| impl ConnectArgs { | ||||
| @@ -281,9 +290,11 @@ impl<'a> ConnectHandler<'a> { | ||||
|       None | ||||
|     }; | ||||
|  | ||||
|     let os = ClientOs::from(&self.args.os).to_openconnect_os().to_string(); | ||||
|     let vpn = Vpn::builder(gateway, cookie) | ||||
|       .script(self.args.script.clone()) | ||||
|       .user_agent(self.args.user_agent.clone()) | ||||
|       .os(Some(os)) | ||||
|       .certificate(self.args.certificate.clone()) | ||||
|       .sslkey(self.args.sslkey.clone()) | ||||
|       .key_password(self.latest_key_password.borrow().clone()) | ||||
| @@ -292,6 +303,7 @@ impl<'a> ConnectHandler<'a> { | ||||
|       .reconnect_timeout(self.args.reconnect_timeout) | ||||
|       .mtu(mtu) | ||||
|       .disable_ipv6(self.args.disable_ipv6) | ||||
|       .no_dtls(self.args.no_dtls) | ||||
|       .build()?; | ||||
|  | ||||
|     let vpn = Arc::new(vpn); | ||||
| @@ -320,6 +332,11 @@ impl<'a> ConnectHandler<'a> { | ||||
|     match prelogin { | ||||
|       Prelogin::Saml(prelogin) => { | ||||
|         let use_default_browser = prelogin.support_default_browser() && self.args.default_browser; | ||||
|         let external_browser = if prelogin.support_default_browser() { | ||||
|           self.args.external_browser.as_deref() | ||||
|         } else { | ||||
|           None | ||||
|         }; | ||||
|  | ||||
|         let cred = SamlAuthLauncher::new(&self.args.server) | ||||
|           .gateway(is_gateway) | ||||
| @@ -332,6 +349,7 @@ impl<'a> ConnectHandler<'a> { | ||||
|           .ignore_tls_errors(self.shared_args.ignore_tls_errors) | ||||
|           .clean(self.args.clean) | ||||
|           .default_browser(use_default_browser) | ||||
|           .external_browser(external_browser) | ||||
|           .launch() | ||||
|           .await?; | ||||
|  | ||||
|   | ||||
| @@ -9,28 +9,29 @@ | ||||
|     "tauri": "tauri" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@emotion/react": "^11.11.1", | ||||
|     "@emotion/styled": "^11.11.0", | ||||
|     "@mui/icons-material": "^5.14.18", | ||||
|     "@mui/material": "^5.14.18", | ||||
|     "@tauri-apps/api": "^1.5.0", | ||||
|     "react": "^18.2.0", | ||||
|     "react-dom": "^18.2.0" | ||||
|     "@emotion/react": "^11.13.0", | ||||
|     "@emotion/styled": "^11.13.0", | ||||
|     "@mui/icons-material": "^5.16.7", | ||||
|     "@mui/material": "^5.16.7", | ||||
|     "@tauri-apps/api": "^1.6.0", | ||||
|     "react": "^18.3.1", | ||||
|     "react-dom": "^18.3.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@tauri-apps/cli": "^1.5.6", | ||||
|     "@types/node": "^20.8.10", | ||||
|     "@types/react": "^18.2.15", | ||||
|     "@types/react-dom": "^18.2.7", | ||||
|     "@typescript-eslint/eslint-plugin": "^6.12.0", | ||||
|     "@typescript-eslint/parser": "^6.12.0", | ||||
|     "@vitejs/plugin-react": "^4.0.3", | ||||
|     "eslint": "^8.54.0", | ||||
|     "eslint-config-prettier": "^9.0.0", | ||||
|     "eslint-plugin-react": "^7.33.2", | ||||
|     "eslint-plugin-react-hooks": "^4.6.0", | ||||
|     "@tauri-apps/cli": "^1.6.0", | ||||
|     "@types/node": "^20.14.15", | ||||
|     "@types/react": "^18.3.3", | ||||
|     "@types/react-dom": "^18.3.0", | ||||
|     "@typescript-eslint/eslint-plugin": "^6.21.0", | ||||
|     "@typescript-eslint/parser": "^6.21.0", | ||||
|     "@vitejs/plugin-react": "^4.3.1", | ||||
|     "eslint": "^8.57.0", | ||||
|     "eslint-config-prettier": "^9.1.0", | ||||
|     "eslint-plugin-react": "^7.35.0", | ||||
|     "eslint-plugin-react-hooks": "^4.6.2", | ||||
|     "prettier": "3.1.0", | ||||
|     "typescript": "^5.0.2", | ||||
|     "typescript": "^5.5.4", | ||||
|     "vite": "^4.5.3" | ||||
|   } | ||||
|   }, | ||||
|   "packageManager": "pnpm@8.15.7" | ||||
| } | ||||
|   | ||||
							
								
								
									
										2010
									
								
								apps/gpgui-helper/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2010
									
								
								apps/gpgui-helper/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -47,6 +47,7 @@ impl VpnTaskContext { | ||||
|       .reconnect_timeout(args.reconnect_timeout()) | ||||
|       .mtu(args.mtu()) | ||||
|       .disable_ipv6(args.disable_ipv6()) | ||||
|       .no_dtls(args.no_dtls()) | ||||
|       .build() | ||||
|     { | ||||
|       Ok(vpn) => vpn, | ||||
|   | ||||
| @@ -30,6 +30,7 @@ uzers.workspace = true | ||||
| serde_urlencoded.workspace = true | ||||
| md5.workspace = true | ||||
| sha256.workspace = true | ||||
| which.workspace = true | ||||
|  | ||||
| tauri = { workspace = true, optional = true } | ||||
| clap = { workspace = true, optional = true } | ||||
|   | ||||
| @@ -19,6 +19,7 @@ pub struct SamlAuthLauncher<'a> { | ||||
|   ignore_tls_errors: bool, | ||||
|   clean: bool, | ||||
|   default_browser: bool, | ||||
|   external_browser: Option<&'a str>, | ||||
| } | ||||
|  | ||||
| impl<'a> SamlAuthLauncher<'a> { | ||||
| @@ -35,6 +36,7 @@ impl<'a> SamlAuthLauncher<'a> { | ||||
|       ignore_tls_errors: false, | ||||
|       clean: false, | ||||
|       default_browser: false, | ||||
|       external_browser: None, | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -88,6 +90,11 @@ impl<'a> SamlAuthLauncher<'a> { | ||||
|     self | ||||
|   } | ||||
|  | ||||
|   pub fn external_browser(mut self, external_browser: Option<&'a str>) -> Self { | ||||
|     self.external_browser = external_browser; | ||||
|     self | ||||
|   } | ||||
|  | ||||
|   /// Launch the authenticator binary as the current user or SUDO_USER if available. | ||||
|   pub async fn launch(self) -> anyhow::Result<Option<Credential>> { | ||||
|     let mut auth_cmd = Command::new(GP_AUTH_BINARY); | ||||
| @@ -133,6 +140,10 @@ impl<'a> SamlAuthLauncher<'a> { | ||||
|       auth_cmd.arg("--default-browser"); | ||||
|     } | ||||
|  | ||||
|     if let Some(external_browser) = self.external_browser { | ||||
|       auth_cmd.arg("--external-browser").arg(external_browser); | ||||
|     } | ||||
|  | ||||
|     let mut non_root_cmd = auth_cmd.into_non_root()?; | ||||
|     let output = non_root_cmd | ||||
|       .kill_on_drop(true) | ||||
|   | ||||
| @@ -1,20 +1,31 @@ | ||||
| use std::{env::temp_dir, fs, io::Write, os::unix::fs::PermissionsExt}; | ||||
| use std::{borrow::Cow, env::temp_dir, fs, io::Write, os::unix::fs::PermissionsExt}; | ||||
|  | ||||
| use anyhow::bail; | ||||
| use log::warn; | ||||
| use log::{info, warn}; | ||||
|  | ||||
| pub struct BrowserAuthenticator<'a> { | ||||
|   auth_request: &'a str, | ||||
|   browser: Option<&'a str>, | ||||
| } | ||||
|  | ||||
| impl BrowserAuthenticator<'_> { | ||||
|   pub fn new(auth_request: &str) -> BrowserAuthenticator { | ||||
|     BrowserAuthenticator { auth_request } | ||||
|     BrowserAuthenticator { | ||||
|       auth_request, | ||||
|       browser: None, | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   pub fn new_with_browser<'a>(auth_request: &'a str, browser: &'a str) -> BrowserAuthenticator<'a> { | ||||
|     BrowserAuthenticator { | ||||
|       auth_request, | ||||
|       browser: Some(browser), | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   pub fn authenticate(&self) -> anyhow::Result<()> { | ||||
|     if self.auth_request.starts_with("http") { | ||||
|       open::that_detached(self.auth_request)?; | ||||
|     let path = if self.auth_request.starts_with("http") { | ||||
|       Cow::Borrowed(self.auth_request) | ||||
|     } else { | ||||
|       let html_file = temp_dir().join("gpauth.html"); | ||||
|  | ||||
| @@ -31,9 +42,31 @@ impl BrowserAuthenticator<'_> { | ||||
|       file.set_permissions(fs::Permissions::from_mode(0o600))?; | ||||
|       file.write_all(self.auth_request.as_bytes())?; | ||||
|  | ||||
|       open::that_detached(html_file)?; | ||||
|       Cow::Owned(html_file.to_string_lossy().to_string()) | ||||
|     }; | ||||
|  | ||||
|     if let Some(browser) = self.browser { | ||||
|       let app = find_browser_path(browser); | ||||
|  | ||||
|       info!("Launching browser: {}", app); | ||||
|       open::with_detached(path.as_ref(), app)?; | ||||
|     } else { | ||||
|       info!("Launching the default browser..."); | ||||
|       open::that_detached(path.as_ref())?; | ||||
|     } | ||||
|  | ||||
|     Ok(()) | ||||
|   } | ||||
| } | ||||
|  | ||||
| fn find_browser_path(browser: &str) -> String { | ||||
|   if browser == "chrome" { | ||||
|     which::which("google-chrome-stable") | ||||
|       .or_else(|_| which::which("google-chrome")) | ||||
|       .or_else(|_| which::which("chromium")) | ||||
|       .map(|path| path.to_string_lossy().to_string()) | ||||
|       .unwrap_or_else(|_| browser.to_string()) | ||||
|   } else { | ||||
|     browser.into() | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -41,6 +41,7 @@ pub struct ConnectArgs { | ||||
|   reconnect_timeout: u32, | ||||
|   mtu: u32, | ||||
|   disable_ipv6: bool, | ||||
|   no_dtls: bool, | ||||
| } | ||||
|  | ||||
| impl ConnectArgs { | ||||
| @@ -58,6 +59,7 @@ impl ConnectArgs { | ||||
|       reconnect_timeout: 300, | ||||
|       mtu: 0, | ||||
|       disable_ipv6: false, | ||||
|       no_dtls: false, | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -108,6 +110,10 @@ impl ConnectArgs { | ||||
|   pub fn disable_ipv6(&self) -> bool { | ||||
|     self.disable_ipv6 | ||||
|   } | ||||
|  | ||||
|   pub fn no_dtls(&self) -> bool { | ||||
|     self.no_dtls | ||||
|   } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize, Serialize, Type)] | ||||
| @@ -179,6 +185,11 @@ impl ConnectRequest { | ||||
|     self | ||||
|   } | ||||
|  | ||||
|   pub fn with_no_dtls(mut self, no_dtls: bool) -> Self { | ||||
|     self.args.no_dtls = no_dtls; | ||||
|     self | ||||
|   } | ||||
|  | ||||
|   pub fn gateway(&self) -> &Gateway { | ||||
|     self.info.gateway() | ||||
|   } | ||||
|   | ||||
| @@ -24,6 +24,7 @@ pub(crate) struct ConnectOptions { | ||||
|   pub reconnect_timeout: u32, | ||||
|   pub mtu: u32, | ||||
|   pub disable_ipv6: u32, | ||||
|   pub no_dtls: u32, | ||||
| } | ||||
|  | ||||
| #[link(name = "vpn")] | ||||
|   | ||||
| @@ -63,6 +63,7 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback) | ||||
|     INFO("RECONNECT_TIMEOUT: %d", options->reconnect_timeout); | ||||
|     INFO("MTU: %d", options->mtu); | ||||
|     INFO("DISABLE_IPV6: %d", options->disable_ipv6); | ||||
|     INFO("NO_DTLS: %d", options->no_dtls); | ||||
|  | ||||
|     vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL); | ||||
|  | ||||
| @@ -119,7 +120,7 @@ int vpn_connect(const vpn_options *options, vpn_connected_callback callback) | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     if (openconnect_setup_dtls(vpninfo, 60) != 0) { | ||||
|     if (options->no_dtls || openconnect_setup_dtls(vpninfo, 60) != 0) { | ||||
|         openconnect_disable_dtls(vpninfo); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,7 @@ typedef struct vpn_options | ||||
|     const int reconnect_timeout; | ||||
|     const int mtu; | ||||
|     const int disable_ipv6; | ||||
|     const int no_dtls; | ||||
| } vpn_options; | ||||
|  | ||||
| int vpn_connect(const vpn_options *options, vpn_connected_callback callback); | ||||
|   | ||||
| @@ -28,6 +28,7 @@ pub struct Vpn { | ||||
|   reconnect_timeout: u32, | ||||
|   mtu: u32, | ||||
|   disable_ipv6: bool, | ||||
|   no_dtls: bool, | ||||
|  | ||||
|   callback: OnConnectedCallback, | ||||
| } | ||||
| @@ -77,6 +78,7 @@ impl Vpn { | ||||
|       reconnect_timeout: self.reconnect_timeout, | ||||
|       mtu: self.mtu, | ||||
|       disable_ipv6: self.disable_ipv6 as u32, | ||||
|       no_dtls: self.no_dtls as u32, | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -125,6 +127,7 @@ pub struct VpnBuilder { | ||||
|   reconnect_timeout: u32, | ||||
|   mtu: u32, | ||||
|   disable_ipv6: bool, | ||||
|   no_dtls: bool, | ||||
| } | ||||
|  | ||||
| impl VpnBuilder { | ||||
| @@ -147,6 +150,7 @@ impl VpnBuilder { | ||||
|       reconnect_timeout: 300, | ||||
|       mtu: 0, | ||||
|       disable_ipv6: false, | ||||
|       no_dtls: false, | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -205,6 +209,11 @@ impl VpnBuilder { | ||||
|     self | ||||
|   } | ||||
|  | ||||
|   pub fn no_dtls(mut self, no_dtls: bool) -> Self { | ||||
|     self.no_dtls = no_dtls; | ||||
|     self | ||||
|   } | ||||
|  | ||||
|   pub fn build(self) -> Result<Vpn, VpnError> { | ||||
|     let script = match self.script { | ||||
|       Some(script) => { | ||||
| @@ -239,6 +248,7 @@ impl VpnBuilder { | ||||
|       reconnect_timeout: self.reconnect_timeout, | ||||
|       mtu: self.mtu, | ||||
|       disable_ipv6: self.disable_ipv6, | ||||
|       no_dtls: self.no_dtls, | ||||
|  | ||||
|       callback: Default::default(), | ||||
|     }) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user