diff --git a/proxmox-protocol/Cargo.toml b/proxmox-protocol/Cargo.toml index fe3d6ab8..7f67554f 100644 --- a/proxmox-protocol/Cargo.toml +++ b/proxmox-protocol/Cargo.toml @@ -13,6 +13,7 @@ crate-type = ['lib', 'cdylib'] [dependencies] chrono = "0.4" endian_trait = "0.6" +errno = "0.2" failure = "0.1" libc = "0.2" openssl = "0.10" diff --git a/proxmox-protocol/src/c_connector.rs b/proxmox-protocol/src/c_connector.rs new file mode 100644 index 00000000..bb82d0f8 --- /dev/null +++ b/proxmox-protocol/src/c_connector.rs @@ -0,0 +1,145 @@ +//! C API for the `Connector`. + +use std::ffi::CStr; +use std::os::raw::{c_char, c_int}; + +use crate::Connector; + +#[inline(always)] +fn with_errno(err: c_int, value: T) -> T { + errno::set_errno(errno::Errno(err)); + value +} + +#[inline] +fn checkstr(ptr: *const c_char) -> Option { + if ptr.is_null() { + return None; + } + + let cstr = unsafe { CStr::from_ptr(ptr) }; + match cstr.to_str() { + Ok(s) => Some(s.to_string()), + Err(_) => None, + } +} + +#[inline(always)] +fn wrap_buildcall(me: *mut Connector, func: F) +where + F: FnOnce(Connector) -> Connector, +{ + let me = unsafe { &mut *me }; + let moved_me = std::mem::replace(me, unsafe { std::mem::uninitialized() }); + std::mem::forget(std::mem::replace(me, func(moved_me))); +} + +/// Create a connector object. +/// +/// Returns a valid pointer or `NULL` on error, with `errno` set. +/// +/// Errors: +/// * `EINVAL`: a required parameter was `NULL` or contained invalid bytes. +#[no_mangle] +pub extern "C" fn proxmox_connector_new( + user: *const c_char, + server: *const c_char, + store: *const c_char, +) -> *mut Connector { + let (user, server, store) = match (checkstr(user), checkstr(server), checkstr(store)) { + (Some(user), Some(server), Some(store)) => (user, server, store), + _ => return with_errno(libc::EINVAL, std::ptr::null_mut()), + }; + + Box::leak(Box::new(Connector::new(user, server, store))) +} + +/// If a connector is not required anymore and has not been used up via a call to +/// `proxmox_connector_connect`, this can be used to free the associated resources. +#[no_mangle] +pub extern "C" fn proxmox_connector_drop(me: *mut Connector) { + unsafe { Box::from_raw(me) }; +} + +/// Use a password +/// +/// Returns `0` on success, a negative `errno` value on error. +/// +/// Errors: +/// * `EINVAL`: a required parameter was `NULL` or contained invalid bytes. +#[no_mangle] +pub extern "C" fn proxmox_connector_set_password( + me: *mut Connector, + password: *const c_char, +) -> c_int { + let password = match checkstr(password) { + Some(pw) => pw, + _ => return -libc::EINVAL, + }; + + wrap_buildcall(me, move |me| me.password(password)); + + 0 +} + +/// Use an existing ticket. +/// +/// Returns `0` on success, a negative `errno` value on error. +/// +/// Errors: +/// * `EINVAL`: a required parameter was `NULL` or contained invalid bytes. +#[no_mangle] +pub extern "C" fn proxmox_connector_set_ticket( + me: *mut Connector, + ticket: *const c_char, + token: *const c_char, +) -> c_int { + let (ticket, token) = match (checkstr(ticket), checkstr(token)) { + (Some(ticket), Some(token)) => (ticket, token), + _ => return -libc::EINVAL, + }; + + wrap_buildcall(me, move |me| me.ticket(ticket, token)); + + 0 +} + +/// Change whether certificate validation should be used on the connector. +#[no_mangle] +pub extern "C" fn proxmox_connector_set_certificate_validation(me: *mut Connector, on: bool) { + let me = unsafe { &mut *me }; + + wrap_buildcall(me, move |me| me.certificate_validation(on)); +} + +/// Initiate the connection. This consumes the Connector, invalidating the pointer to it! +/// +/// Returns a `ProxmoxBackup*`, or `NULL` on error. +#[no_mangle] +pub extern "C" fn proxmox_connector_connect( + me: *mut Connector, +) -> *mut crate::c_client::CClient { + let boxed = unsafe { Box::from_raw(me) }; + let me = *boxed; + match me.do_connect() { + Ok(stream) => { + let mut client = crate::c_client::make_c_compatible_client(stream); + match client.wait_for_handshake() { + Ok(true) => crate::c_client::make_c_client(client), + Ok(false) => { + // This is a synchronous blocking connection, so this should be impossible: + eprintln!("proxmox backup protocol error handshake did not complete?"); + std::ptr::null_mut() + } + Err(err) => { + eprintln!("error during handshake with backup server: {}", err); + std::ptr::null_mut() + } + } + } + Err(err) => { + eprintln!("error connecting to backup server: {}", err); + std::ptr::null_mut() + } + } +} diff --git a/proxmox-protocol/src/lib.rs b/proxmox-protocol/src/lib.rs index 4664a5ce..d08e94c9 100644 --- a/proxmox-protocol/src/lib.rs +++ b/proxmox-protocol/src/lib.rs @@ -21,3 +21,4 @@ pub use types::*; pub mod c_chunker; pub mod c_client; +pub mod c_connector;