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