mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	Refactor using Tauri (#278)
This commit is contained in:
		
							
								
								
									
										71
									
								
								crates/openconnect/src/ffi/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								crates/openconnect/src/ffi/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| use crate::Vpn; | ||||
| use log::{debug, info, trace, warn}; | ||||
| use std::ffi::{c_char, c_int, c_void}; | ||||
|  | ||||
| #[repr(C)] | ||||
| #[derive(Debug)] | ||||
| pub(crate) struct ConnectOptions { | ||||
|   pub user_data: *mut c_void, | ||||
|  | ||||
|   pub server: *const c_char, | ||||
|   pub cookie: *const c_char, | ||||
|   pub user_agent: *const c_char, | ||||
|  | ||||
|   pub script: *const c_char, | ||||
|   pub os: *const c_char, | ||||
|   pub certificate: *const c_char, | ||||
|   pub servercert: *const c_char, | ||||
| } | ||||
|  | ||||
| #[link(name = "vpn")] | ||||
| extern "C" { | ||||
|   #[link_name = "vpn_connect"] | ||||
|   fn vpn_connect( | ||||
|     options: *const ConnectOptions, | ||||
|     callback: extern "C" fn(i32, *mut c_void), | ||||
|   ) -> c_int; | ||||
|  | ||||
|   #[link_name = "vpn_disconnect"] | ||||
|   fn vpn_disconnect(); | ||||
| } | ||||
|  | ||||
| pub(crate) fn connect(options: &ConnectOptions) -> i32 { | ||||
|   unsafe { vpn_connect(options, on_vpn_connected) } | ||||
| } | ||||
|  | ||||
| pub(crate) fn disconnect() { | ||||
|   unsafe { vpn_disconnect() } | ||||
| } | ||||
|  | ||||
| #[no_mangle] | ||||
| extern "C" fn on_vpn_connected(pipe_fd: i32, vpn: *mut c_void) { | ||||
|   let vpn = unsafe { &*(vpn as *const Vpn) }; | ||||
|   vpn.on_connected(pipe_fd); | ||||
| } | ||||
|  | ||||
| // Logger used in the C code. | ||||
| // level: 0 = error, 1 = info, 2 = debug, 3 = trace | ||||
| // map the error level log in openconnect to the warning level | ||||
| #[no_mangle] | ||||
| extern "C" fn vpn_log(level: i32, message: *const c_char) { | ||||
|   let message = unsafe { std::ffi::CStr::from_ptr(message) }; | ||||
|   let message = message.to_str().unwrap_or("Invalid log message"); | ||||
|   // Strip the trailing newline | ||||
|   let message = message.trim_end_matches('\n'); | ||||
|  | ||||
|   if level == 0 { | ||||
|     warn!("{}", message); | ||||
|   } else if level == 1 { | ||||
|     info!("{}", message); | ||||
|   } else if level == 2 { | ||||
|     debug!("{}", message); | ||||
|   } else if level == 3 { | ||||
|     trace!("{}", message); | ||||
|   } else { | ||||
|     warn!( | ||||
|       "Unknown log level: {}, enable DEBUG log level to see more details", | ||||
|       level | ||||
|     ); | ||||
|     debug!("{}", message); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										144
									
								
								crates/openconnect/src/ffi/vpn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								crates/openconnect/src/ffi/vpn.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdarg.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/utsname.h> | ||||
| #include <openconnect.h> | ||||
|  | ||||
| #include "vpn.h" | ||||
|  | ||||
| void *g_user_data; | ||||
|  | ||||
| static int g_cmd_pipe_fd; | ||||
| static const char *g_vpnc_script; | ||||
| static vpn_connected_callback on_vpn_connected; | ||||
|  | ||||
| /* Validate the peer certificate */ | ||||
| static int validate_peer_cert(__attribute__((unused)) void *_vpninfo, const char *reason) | ||||
| { | ||||
|     INFO("Validating peer cert: %s", reason); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* Print progress messages */ | ||||
| static void print_progress(__attribute__((unused)) void *_vpninfo, int level, const char *format, ...) | ||||
| { | ||||
|     va_list args; | ||||
|     va_start(args, format); | ||||
|     char *message = format_message(format, args); | ||||
|     va_end(args); | ||||
|  | ||||
|     if (message == NULL) | ||||
|     { | ||||
|         ERROR("Failed to format log message"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         LOG(level, message); | ||||
|         free(message); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void setup_tun_handler(void *_vpninfo) | ||||
| { | ||||
|     int ret = openconnect_setup_tun_device(_vpninfo, g_vpnc_script, NULL); | ||||
|     if (!ret) { | ||||
|         on_vpn_connected(g_cmd_pipe_fd, g_user_data); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Initialize VPN connection */ | ||||
| int vpn_connect(const vpn_options *options, vpn_connected_callback callback) | ||||
| { | ||||
|     INFO("openconnect version: %s", openconnect_get_version()); | ||||
|     struct openconnect_info *vpninfo; | ||||
|     struct utsname utsbuf; | ||||
|  | ||||
|     g_user_data = options->user_data; | ||||
|     g_vpnc_script = options->script; | ||||
|     on_vpn_connected = callback; | ||||
|  | ||||
|     INFO("User agent: %s", options->user_agent); | ||||
|     INFO("VPNC script: %s", options->script); | ||||
|     INFO("OS: %s", options->os); | ||||
|  | ||||
|     vpninfo = openconnect_vpninfo_new(options->user_agent, validate_peer_cert, NULL, NULL, print_progress, NULL); | ||||
|  | ||||
|     if (!vpninfo) | ||||
|     { | ||||
|         ERROR("openconnect_vpninfo_new failed"); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     openconnect_set_loglevel(vpninfo, PRG_TRACE); | ||||
|     openconnect_init_ssl(); | ||||
|     openconnect_set_protocol(vpninfo, "gp"); | ||||
|     openconnect_set_hostname(vpninfo, options->server); | ||||
|     openconnect_set_cookie(vpninfo, options->cookie); | ||||
|  | ||||
|     if (options->os) { | ||||
|         openconnect_set_reported_os(vpninfo, options->os); | ||||
|     } | ||||
|  | ||||
|     if (options->certificate) | ||||
|     { | ||||
|         INFO("Setting client certificate: %s", options->certificate); | ||||
|         openconnect_set_client_cert(vpninfo, options->certificate, NULL); | ||||
|     } | ||||
|  | ||||
|     if (options->servercert) { | ||||
|         INFO("Setting server certificate: %s", options->servercert); | ||||
|         openconnect_set_system_trust(vpninfo, 0); | ||||
|     } | ||||
|  | ||||
|     g_cmd_pipe_fd = openconnect_setup_cmd_pipe(vpninfo); | ||||
|     if (g_cmd_pipe_fd < 0) | ||||
|     { | ||||
|         ERROR("openconnect_setup_cmd_pipe failed"); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     if (!uname(&utsbuf)) | ||||
|     { | ||||
|         openconnect_set_localname(vpninfo, utsbuf.nodename); | ||||
|     } | ||||
|  | ||||
|     // Essential step | ||||
|     if (openconnect_make_cstp_connection(vpninfo) != 0) | ||||
|     { | ||||
|         ERROR("openconnect_make_cstp_connection failed"); | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     if (openconnect_setup_dtls(vpninfo, 60) != 0) | ||||
|     { | ||||
|         openconnect_disable_dtls(vpninfo); | ||||
|     } | ||||
|  | ||||
|     // Essential step | ||||
|     openconnect_set_setup_tun_handler(vpninfo, setup_tun_handler); | ||||
|  | ||||
|     while (1) | ||||
|     { | ||||
|         int ret = openconnect_mainloop(vpninfo, 300, 10); | ||||
|  | ||||
|         if (ret) | ||||
|         { | ||||
|             INFO("openconnect_mainloop returned %d, exiting", ret); | ||||
|             openconnect_vpninfo_free(vpninfo); | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         INFO("openconnect_mainloop returned 0, reconnecting"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Stop the VPN connection */ | ||||
| void vpn_disconnect() | ||||
| { | ||||
|     char cmd = OC_CMD_CANCEL; | ||||
|     if (write(g_cmd_pipe_fd, &cmd, 1) < 0) | ||||
|     { | ||||
|         ERROR("Failed to write to command pipe, VPN connection may not be stopped"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										68
									
								
								crates/openconnect/src/ffi/vpn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								crates/openconnect/src/ffi/vpn.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdarg.h> | ||||
| #include <openconnect.h> | ||||
|  | ||||
| typedef void (*vpn_connected_callback)(int cmd_pipe_fd, void *user_data); | ||||
|  | ||||
| typedef struct vpn_options | ||||
| { | ||||
|     void *user_data; | ||||
|     const char *server; | ||||
|     const char *cookie; | ||||
|     const char *user_agent; | ||||
|  | ||||
|     const char *script; | ||||
|     const char *os; | ||||
|     const char *certificate; | ||||
|     const char *servercert; | ||||
| } vpn_options; | ||||
|  | ||||
| int vpn_connect(const vpn_options *options, vpn_connected_callback callback); | ||||
| void vpn_disconnect(); | ||||
|  | ||||
| extern void vpn_log(int level, const char *msg); | ||||
|  | ||||
| static char *format_message(const char *format, va_list args) | ||||
| { | ||||
|     va_list args_copy; | ||||
|     va_copy(args_copy, args); | ||||
|     int len = vsnprintf(NULL, 0, format, args_copy); | ||||
|     va_end(args_copy); | ||||
|  | ||||
|     char *buffer = malloc(len + 1); | ||||
|     if (buffer == NULL) | ||||
|     { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     vsnprintf(buffer, len + 1, format, args); | ||||
|     return buffer; | ||||
| } | ||||
|  | ||||
| static void _log(int level, ...) | ||||
| { | ||||
|     va_list args; | ||||
|     va_start(args, level); | ||||
|  | ||||
|     char *format = va_arg(args, char *); | ||||
|     char *message = format_message(format, args); | ||||
|  | ||||
|     va_end(args); | ||||
|  | ||||
|     if (message == NULL) | ||||
|     { | ||||
|         vpn_log(PRG_ERR, "Failed to format log message"); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         vpn_log(level, message); | ||||
|         free(message); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #define LOG(level, ...) _log(level, __VA_ARGS__) | ||||
| #define ERROR(...) LOG(PRG_ERR, __VA_ARGS__) | ||||
| #define INFO(...) LOG(PRG_INFO, __VA_ARGS__) | ||||
| #define DEBUG(...) LOG(PRG_DEBUG, __VA_ARGS__) | ||||
| #define TRACE(...) LOG(PRG_TRACE, __VA_ARGS__) | ||||
		Reference in New Issue
	
	Block a user