From c3cb19dec32ffae9e4f93c269b4e1d3504321643 Mon Sep 17 00:00:00 2001 From: defanor Date: Tue, 20 Jun 2023 21:49:53 +0300 Subject: Add Rust versions of the TCP module and of a few structures --- src/Cargo.toml | 1 + src/Makefile.am | 8 +- src/rexmpp.rs | 255 +++++++++++++++++++++++++++++ src/rexmpp_dns.c | 2 + src/rexmpp_dns.rs | 62 +++++++ src/rexmpp_jid.rs | 17 ++ src/rexmpp_rust.rs | 6 +- src/rexmpp_tcp.rs | 473 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 820 insertions(+), 4 deletions(-) create mode 100644 src/rexmpp.rs create mode 100644 src/rexmpp_dns.rs create mode 100644 src/rexmpp_jid.rs create mode 100644 src/rexmpp_tcp.rs diff --git a/src/Cargo.toml b/src/Cargo.toml index fc59bfc..6ce67b4 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -13,3 +13,4 @@ path = "rexmpp_rust.rs" [dependencies] libc = "0.2" +errno = "0.3" diff --git a/src/Makefile.am b/src/Makefile.am index 36365ea..9e38c41 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,6 @@ AM_CFLAGS = -Werror -Wall -Wextra -pedantic -std=gnu99 \ lib_LTLIBRARIES = librexmpp.la librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \ - rexmpp_tcp.h rexmpp_tcp.c \ rexmpp.h rexmpp.c \ rexmpp_dns.h rexmpp_dns.c \ rexmpp_tls.h rexmpp_tls.c \ @@ -40,7 +39,9 @@ librexmpp_la_LIBADD = $(LIBXML_LIBS) \ librexmpp_la_LDFLAGS = [] if USE_RUST -target_debug_librexmpp_rust_a_SOURCES = rexmpp_rust.rs rexmpp_socks.rs rexmpp_xml.rs +target_debug_librexmpp_rust_a_SOURCES = \ + rexmpp_rust.rs rexmpp.rs rexmpp_jid.rs rexmpp_dns.rs rexmpp_tcp.rs \ + rexmpp_socks.rs rexmpp_xml.rs noinst_LIBRARIES = target/debug/librexmpp_rust.a librexmpp_la_LIBADD += target/debug/librexmpp_rust.a librexmpp_la_LDFLAGS += -L. -lpthread -ldl @@ -49,5 +50,6 @@ target/debug/librexmpp_rust.a: $(target_debug_librexmpp_rust_a_SOURCES) $(CARGO) build else -librexmpp_la_SOURCES += rexmpp_socks.h rexmpp_socks.c +librexmpp_la_SOURCES += rexmpp_tcp.h rexmpp_tcp.c \ + rexmpp_socks.h rexmpp_socks.c endif diff --git a/src/rexmpp.rs b/src/rexmpp.rs new file mode 100644 index 0000000..bf09183 --- /dev/null +++ b/src/rexmpp.rs @@ -0,0 +1,255 @@ +extern crate libc; +use std::os::raw::{c_char, c_int, c_void, c_uint}; +use libc::{time_t, timespec}; + +use super::{rexmpp_jid, rexmpp_xml, rexmpp_dns, rexmpp_tcp, rexmpp_socks}; + +#[derive(PartialEq)] +#[repr(C)] +pub enum ResolverState { + Ready, + SRV, + SRV2, + Failure +} + +#[derive(PartialEq)] +#[repr(C)] +pub enum TCPState { + None, + Connecting, + SOCKS, + Connected, + Closed, + ConnectionFailure, + Error +} + +#[derive(PartialEq)] +#[repr(C)] +pub enum StreamState { + None, + Opening, + StartTLS, + SASL, + Bind, + SMFull, + SMAcks, + SMResume, + Ready, + CloseRequested, + Closing, + Closed, + Error, + ErrorReconnect +} + +#[derive(PartialEq)] +#[repr(C)] +pub enum TLSState { + Inactive, + AwaitingDirect, + Handshake, + Active, + Closing, + Closed, + Error +} + +#[derive(PartialEq)] +#[repr(C)] +pub enum SASLState { + Inactive, + Negotiation, + Active, + Error +} + +#[derive(PartialEq)] +#[repr(C)] +pub enum SMState { + Inactive, + Negotiation, + Active +} + +#[derive(PartialEq)] +#[repr(C)] +pub enum CarbonsState { + Inactive, + Negotiation, + Active +} + +#[derive(PartialEq)] +#[repr(C)] +pub enum TLSPolicy { + Require, + Prefer, + Avoid +} + +type IQCallback = unsafe extern "C" +fn (s: *mut Rexmpp, cb_data: *mut c_void, + request: *mut rexmpp_xml::RexmppXML, response: *mut rexmpp_xml::RexmppXML, + success: c_int) -> (); + +#[repr(C)] +pub struct RexmppIQ { + pub requset: *mut rexmpp_xml::RexmppXML, + pub cb: IQCallback, + pub cb_data: *const c_void, + pub next: *mut RexmppIQ +} + +#[repr(C)] +pub struct Rexmpp { + pub resolver_state: ResolverState, + pub tcp_state: TCPState, + pub stream_state: StreamState, + pub tls_state: TLSState, + pub sasl_state: SASLState, + pub sm_state: SMState, + pub carbons_state: CarbonsState, + + // Basic configuration + pub initial_jid: rexmpp_jid::RexmppJID, + + // Manual host/port configuration + pub manual_host: *const c_char, + pub manual_port: u16, + pub manual_direct_tls: c_int, + + // Miscellaneous settings + pub disco_node: *const c_char, + + // SOCKS settings + pub socks_host: *const c_char, + pub socks_port: u16, + + // Various knobs (these are used instead of loadable modules) + pub enable_carbons: c_int, // XEP-0280 + pub manage_roster: c_int, + pub roster_cache_file: *const c_char, + pub track_roster_presence: c_int, + pub track_roster_events: c_int, // XEP-0163 + pub nick_notifications: c_int, // XEP-0172 + pub retrieve_openpgp_keys: c_int, // XEP-0373 + pub autojoin_bookmarked_mucs: c_int, // XEP-0402 + pub tls_policy: TLSPolicy, + pub enable_jingle: c_int, + pub client_name: *const c_char, // XEP-0030, XEP-0092 + pub client_type: *const c_char, // XEP-0030 + pub client_version: *const c_char, // XEP-0092 + pub local_address: *const c_char, // For ICE, XEP-0176 + pub jingle_prefer_rtcp_mux: c_int, + // An IP_MTU_DISCOVER parameter for TCP sockets, or -1 to not set + // it + pub path_mtu_discovery: c_int, + // Resource limits + pub stanza_queue_size: u32, + pub send_queue_size: u32, + pub iq_queue_size: u32, + pub iq_cache_size: u32, + pub max_jingle_sessions: u32, + + // Callbacks + + // c_variadic is experimental and cannot be used on the stable + // release channel, so skipping the log function callback. + pub log_function: *const c_void, + // Actually skipping proper definitions of others for now as well + // (TODO: add them). + pub sasl_property_cb: *const c_void, + pub xml_in_cb: *const c_void, + pub xml_out_cb: *const c_void, + pub roster_modify_cb: *const c_void, + pub console_print_cb: *const c_void, + + // Stream-related state + pub assigned_jid: rexmpp_jid::RexmppJID, + pub stream_features: *mut rexmpp_xml::RexmppXML, + pub roster_items: *mut rexmpp_xml::RexmppXML, + pub roster_ver: *mut c_char, + pub roster_presence: *mut rexmpp_xml::RexmppXML, + pub roster_events: *mut rexmpp_xml::RexmppXML, + + // Other dynamic data + pub disco_info: *mut rexmpp_xml::RexmppXML, + // Includes Jingle RTP session candidates; rexmpp prioritizes the + // ones listed earlier on incoming calls + pub jingle_rtp_description: *mut rexmpp_xml::RexmppXML, + + // IQs we're waiting for responses to + pub active_iq: *mut RexmppIQ, + pub iq_cache: *mut rexmpp_xml::RexmppXML, + + // Jingle context + pub jingle: *const c_void, // TODO + + // Connection and stream management + pub reconnect_number: c_uint, + pub reconnect_seconds: time_t, + pub next_reconnect_time: timespec, + pub stanza_queue: *mut rexmpp_xml::RexmppXML, + pub stanzas_out_count: u32, + pub stanzas_out_acknowledged: u32, + pub stanzas_in_count: u32, + pub stream_id: *mut c_char, + + // Server ping configuration and state + pub ping_delay: c_int, + pub ping_requested: c_int, + pub last_network_activity: time_t, + + // DNS-related structures + pub resolver: *mut c_void, + pub server_srv: *mut rexmpp_dns::RexmppDNSResult, + pub server_srv_cur: c_int, + pub server_srv_tls: *mut rexmpp_dns::RexmppDNSResult, + pub server_srv_tls_cur: c_int, + pub server_active_srv: *mut rexmpp_dns::RexmppDNSSRV, + + // The XMPP server we are connecting to + pub server_host: *const c_char, + pub server_port: u16, + + // The primary socket used for communication with the server + pub server_socket: c_int, + // Whether the address it's connected to was verified with DNSSEC + pub server_socket_dns_secure: c_int, + + // A structure used to establish a TCP connection + pub server_connection: rexmpp_tcp::RexmppTCPConnection, + pub server_socks_conn: rexmpp_socks::RexmppSocks, + + // Send buffer. NULL if there is nothing to send (and must not be + // NULL if there is anything in the send queue). Not appending + // data to it, see send_queue for queuing. + pub send_buffer: *mut c_char, + pub send_buffer_len: isize, + pub send_buffer_sent: isize, + + // A queue of XML elements to send + pub send_queue: *mut rexmpp_xml::RexmppXML, + + // XML parser context, and current element pointer for building + // XML nodes with a SAX2 parser interface + pub xml_parser: *mut c_void, + pub current_element_root: *mut c_void, + pub current_element: *mut c_void, + pub input_queue: *mut c_void, + pub input_queue_last: *mut c_void, + + // TLS structures + pub tls: *mut c_void, + + // SASL structures + pub sasl: *mut c_void, + + // OpenPGP structures + pub pgp_ctx: *mut c_void, + + // curl structures + pub curl_multi: *mut c_void +} diff --git a/src/rexmpp_dns.c b/src/rexmpp_dns.c index 8c40c0a..f793e88 100644 --- a/src/rexmpp_dns.c +++ b/src/rexmpp_dns.c @@ -64,6 +64,7 @@ int rexmpp_parse_srv (char *in, int in_len, struct rexmpp_dns_srv *out) { } +#ifndef USE_RUST void rexmpp_dns_result_free (rexmpp_dns_result_t *result) { if (result->data != NULL) { int i; @@ -79,6 +80,7 @@ void rexmpp_dns_result_free (rexmpp_dns_result_t *result) { } free(result); } +#endif rexmpp_dns_result_t *result_from_hostent (struct hostent *hostinfo) { rexmpp_dns_result_t *r = malloc(sizeof(rexmpp_dns_result_t)); diff --git a/src/rexmpp_dns.rs b/src/rexmpp_dns.rs new file mode 100644 index 0000000..8ea8f31 --- /dev/null +++ b/src/rexmpp_dns.rs @@ -0,0 +1,62 @@ +use std::os::raw::{c_int, c_void, c_char}; +use libc::*; +use std::ptr; + +use super::rexmpp; + +type DNSQueryCB = unsafe extern "C" +fn (s: *mut rexmpp::Rexmpp, ptr: *mut c_void, result: *mut RexmppDNSResult) -> (); + +extern { + pub fn rexmpp_dns_resolve (s: *mut rexmpp::Rexmpp, + query: *const c_char, + rrtype: c_int, + rrclass: c_int, + ptr: *mut c_void, + callback: DNSQueryCB) -> c_int; + pub fn rexmpp_dns_process (s: *mut rexmpp::Rexmpp, + read_fds: *mut fd_set, + write_fds: *mut fd_set) -> c_int; + pub fn rexmpp_dns_fds (s: *mut rexmpp::Rexmpp, + read_fds: *mut fd_set, + write_fds: *mut fd_set) -> c_int; + pub fn rexmpp_dns_timeout (s: *mut rexmpp::Rexmpp, + max_tv: *mut timespec, + tv: *mut timespec) -> *mut timespec; +} + +#[repr(C)] +pub struct RexmppDNSResult { + pub data: *mut *mut c_void, + pub len: *mut c_int, + pub secure: c_int +} + +#[repr(C)] +pub struct RexmppDNSSRV { + pub priority: u16, + pub weight: u16, + pub port: u16, + pub target: [c_char; 256] +} + + +#[no_mangle] +pub unsafe extern "C" +fn rexmpp_dns_result_free (result: *mut RexmppDNSResult) { + if (*result).data != ptr::null_mut() { + let mut i = 0; + let data_ptr: *mut *mut c_void = (*result).data; + while *(data_ptr.offset(i)) != ptr::null_mut() { + free(*(data_ptr.offset(i))); + i += 1; + } + free((*result).data as *mut c_void); + (*result).data = ptr::null_mut(); + } + if (*result).len != ptr::null_mut() { + free((*result).len as *mut c_void); + (*result).len = ptr::null_mut(); + } + free(result as *mut c_void); +} diff --git a/src/rexmpp_jid.rs b/src/rexmpp_jid.rs new file mode 100644 index 0000000..7730b2f --- /dev/null +++ b/src/rexmpp_jid.rs @@ -0,0 +1,17 @@ +use std::os::raw::{c_char}; + +#[repr(C)] +pub struct RexmppJID { + local: [c_char; 1024], + domain: [c_char; 1024], + resource: [c_char; 1024], + bare: [c_char; 2048], + full: [c_char; 3072] +} + +// #[no_mangle] +// extern "C" +// fn rexmpp_jid_parse (str: *const c_char, jid : &mut RexmppJID) -> c_int +// { + +// } diff --git a/src/rexmpp_rust.rs b/src/rexmpp_rust.rs index 9fa3ae4..fd19bd4 100644 --- a/src/rexmpp_rust.rs +++ b/src/rexmpp_rust.rs @@ -1,2 +1,6 @@ -mod rexmpp_socks; +mod rexmpp_jid; mod rexmpp_xml; +mod rexmpp_dns; +mod rexmpp_tcp; +mod rexmpp_socks; +mod rexmpp; diff --git a/src/rexmpp_tcp.rs b/src/rexmpp_tcp.rs new file mode 100644 index 0000000..d895004 --- /dev/null +++ b/src/rexmpp_tcp.rs @@ -0,0 +1,473 @@ +use std::os::raw::{c_int, c_char}; +use libc::*; +use std::ptr::null_mut; +use std::mem; +use errno::{errno}; + +use super::{rexmpp_dns, rexmpp}; + + +#[link(name = "libc")] +extern { + fn inet_pton (af: c_int, src: *const c_char, dst: *mut c_void) -> c_int; +} + + +const REXMPP_TCP_MAX_CONNECTION_ATTEMPTS: usize = 20; +const REXMPP_TCP_IPV6_DELAY_MS: i64 = 50; +const REXMPP_TCP_CONN_DELAY_MS: i64 = 250; + +#[derive(PartialEq, Copy, Clone)] +#[repr(C)] +pub enum ResolutionStatus { + Inactive, + Waiting, + Success, + Failure +} + +#[derive(PartialEq, Copy, Clone)] +#[repr(C)] +pub enum ConnectionError { + Done, + ResolverError, + InProgress, + Failure, + Error +} + +#[repr(C)] +pub struct RexmppTCPConnection { + pub host: *const c_char, + pub port: u16, + pub resolution_v4: ResolutionStatus, + pub resolver_status_v4: c_int, + pub resolved_v4: *mut rexmpp_dns::RexmppDNSResult, + pub addr_cur_v4: c_int, + pub resolution_v6: ResolutionStatus, + pub resolver_status_v6: c_int, + pub resolved_v6: *mut rexmpp_dns::RexmppDNSResult, + pub addr_cur_v6: c_int, + pub sockets: [c_int; REXMPP_TCP_MAX_CONNECTION_ATTEMPTS], + pub connection_attempts: c_int, + pub next_connection_time: timespec, + pub fd: c_int, + pub dns_secure: c_int +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_dns_aaaa_cb (_s: *mut rexmpp::Rexmpp, + ptr: *mut c_void, + result: *mut rexmpp_dns::RexmppDNSResult) + -> () { + let conn = ptr as *mut RexmppTCPConnection; + (*conn).resolved_v6 = result; + if result != null_mut() { + (*conn).resolution_v6 = ResolutionStatus::Success; + (*conn).addr_cur_v6 = -1; + } else { + (*conn).resolution_v6 = ResolutionStatus::Failure; + } +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_dns_a_cb (_s: *mut rexmpp::Rexmpp, + ptr: *mut c_void, + result: *mut rexmpp_dns::RexmppDNSResult) + -> () { + let conn = ptr as *mut RexmppTCPConnection; + (*conn).resolved_v4 = result; + if result != null_mut() { + (*conn).resolution_v4 = ResolutionStatus::Success; + (*conn).addr_cur_v4 = -1; + if (*conn).resolution_v6 == ResolutionStatus::Waiting { + // Wait a bit (usually 50 ms) for IPv6 + clock_gettime(CLOCK_MONOTONIC, &mut (*conn).next_connection_time); + (*conn).next_connection_time.tv_nsec += REXMPP_TCP_IPV6_DELAY_MS * 1000000; + if (*conn).next_connection_time.tv_nsec >= 1000000000 { + (*conn).next_connection_time.tv_nsec -= 1000000000; + (*conn).next_connection_time.tv_sec += 1; + } + } + } else { + (*conn).resolution_v4 = ResolutionStatus::Failure; + } +} + + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_cleanup (conn: *mut RexmppTCPConnection) -> () { + for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) { + if (*conn).sockets[i] != -1 && (*conn).sockets[i] != (*conn).fd { + close((*conn).sockets[i]); + (*conn).sockets[i] = -1; + } + } + if (*conn).resolution_v4 != ResolutionStatus::Inactive { + (*conn).resolution_v4 = ResolutionStatus::Inactive; + (*conn).resolution_v6 = ResolutionStatus::Inactive; + } + if (*conn).resolved_v4 != null_mut() { + rexmpp_dns::rexmpp_dns_result_free((*conn).resolved_v4); + (*conn).resolved_v4 = null_mut(); + } + if (*conn).resolved_v6 != null_mut() { + rexmpp_dns::rexmpp_dns_result_free((*conn).resolved_v6); + (*conn).resolved_v6 = null_mut(); + } +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_connected (conn: *mut RexmppTCPConnection, fd: c_int) + -> ConnectionError { + let mut sa_ptr = mem::MaybeUninit::::uninit(); + let mut sa_len : socklen_t = mem::size_of::() as u32; + getsockname(fd, sa_ptr.as_mut_ptr(), &mut sa_len); + let sa = sa_ptr.assume_init(); + if sa.sa_family == (AF_INET as u16) + && (*conn).resolved_v4 != null_mut() { + (*conn).dns_secure = (*(*conn).resolved_v4).secure; + } + else if sa.sa_family == (AF_INET6 as u16) + && (*conn).resolved_v6 != null_mut() { + (*conn).dns_secure = (*(*conn).resolved_v6).secure; + } + (*conn).fd = fd; + rexmpp_tcp_cleanup(conn); + return ConnectionError::Done; +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_socket (s: *mut rexmpp::Rexmpp, domain: c_int) -> c_int { + let sock: c_int = socket(domain, SOCK_STREAM, 0); + + // Make it non-blocking + let flags: c_int = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); + + // Set path MTU discovery, if provided + if (*s).path_mtu_discovery != -1 { + setsockopt(sock, SOL_IP, IP_MTU_DISCOVER, + &mut (*s).path_mtu_discovery as *mut i32 as *mut c_void, + mem::size_of::() as u32); + } + + return sock; +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_conn_init (s: *mut rexmpp::Rexmpp, + conn: *mut RexmppTCPConnection, + host: *const c_char, + port: u16) + -> ConnectionError { + for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) { + (*conn).sockets[i] = -1; + } + (*conn).connection_attempts = 0; + (*conn).port = port; + (*conn).resolved_v4 = null_mut(); + (*conn).resolved_v6 = null_mut(); + (*conn).fd = -1; + (*conn).dns_secure = 0; + (*conn).next_connection_time.tv_sec = 0; + (*conn).next_connection_time.tv_nsec = 0; + + (*conn).resolution_v4 = ResolutionStatus::Inactive; + (*conn).resolution_v6 = ResolutionStatus::Inactive; + + let mut addr_v4 = mem::MaybeUninit::::uninit(); + if inet_pton(AF_INET, host, + &mut ((*addr_v4.as_mut_ptr()).sin_addr) + as *mut in_addr as *mut c_void) == 1 { + (*addr_v4.as_mut_ptr()).sin_family = AF_INET as u16; + (*addr_v4.as_mut_ptr()).sin_port = port.to_be(); + (*conn).sockets[(*conn).connection_attempts as usize] = + rexmpp_tcp_socket(s, AF_INET); + if connect((*conn).sockets[(*conn).connection_attempts as usize], + addr_v4.as_mut_ptr() as *mut sockaddr, + mem::size_of::() as u32) != 0 { + if errno().0 != EINPROGRESS { + return ConnectionError::Error; + } + } else { + return rexmpp_tcp_connected(conn, + (*conn).sockets[(*conn).connection_attempts as usize]); + } + (*conn).connection_attempts += 1; + return ConnectionError::InProgress; + } + + let mut addr_v6 = mem::MaybeUninit::::uninit(); + if inet_pton(AF_INET6, host, + &mut ((*addr_v6.as_mut_ptr()).sin6_addr) + as *mut in6_addr as *mut c_void) == 1 { + (*addr_v6.as_mut_ptr()).sin6_family = AF_INET as u16; + (*addr_v6.as_mut_ptr()).sin6_port = port.to_be(); + (*addr_v6.as_mut_ptr()).sin6_flowinfo = 0; + (*addr_v6.as_mut_ptr()).sin6_scope_id = 0; + (*conn).sockets[(*conn).connection_attempts as usize] = + rexmpp_tcp_socket(s, AF_INET6); + if connect((*conn).sockets[(*conn).connection_attempts as usize], + addr_v6.as_mut_ptr() as *mut sockaddr, + mem::size_of::() as u32) != 0 { + if errno().0 != EINPROGRESS { + return ConnectionError::Error; + } + } else { + return rexmpp_tcp_connected(conn, + (*conn).sockets[(*conn).connection_attempts as usize]); + } + (*conn).connection_attempts += 1; + return ConnectionError::InProgress; + } + (*conn).resolution_v4 = ResolutionStatus::Waiting; + (*conn).resolution_v6 = ResolutionStatus::Waiting; + + rexmpp_dns::rexmpp_dns_resolve(s, host, 28, 1, + conn as *mut c_void, + rexmpp_tcp_dns_aaaa_cb); + rexmpp_dns::rexmpp_dns_resolve(s, host, 1, 1, + conn as *mut c_void, + rexmpp_tcp_dns_a_cb); + return ConnectionError::InProgress; +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_conn_finish (conn: *mut RexmppTCPConnection) -> c_int { + rexmpp_tcp_cleanup(conn); + return (*conn).fd; +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_conn_ipv4_available (conn: *mut RexmppTCPConnection) -> bool { + (*conn).resolution_v4 == ResolutionStatus::Success + && (*conn).resolved_v4 != null_mut() + && *(*(*conn).resolved_v4).data + .offset(((*conn).addr_cur_v4 + 1) as isize) != null_mut() +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_conn_ipv6_available (conn: *mut RexmppTCPConnection) -> bool { + (*conn).resolution_v6 == ResolutionStatus::Success + && (*conn).resolved_v6 != null_mut() + && *(*(*conn).resolved_v6).data + .offset(((*conn).addr_cur_v6 + 1) as isize) != null_mut() +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_conn_proceed (s: *mut rexmpp::Rexmpp, + conn: *mut RexmppTCPConnection, + read_fds: *mut fd_set, + write_fds: *mut fd_set) -> ConnectionError { + // Check for successful connections. + for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) { + if (*conn).sockets[i] != -1 && FD_ISSET((*conn).sockets[i], write_fds) { + let mut err: c_int = 0; + let mut err_len: socklen_t = mem::size_of::() as u32; + if getsockopt((*conn).sockets[i], SOL_SOCKET, SO_ERROR, + &mut err as *mut c_int as *mut c_void, + &mut err_len) < 0 { + return ConnectionError::Error; + } else { + if err == 0 { + return rexmpp_tcp_connected(conn, (*conn).sockets[i]); + } else if err != EINPROGRESS { + close((*conn).sockets[i]); + (*conn).sockets[i] = -1; + } + } + } + } + + // Name resolution + if (*conn).resolution_v4 == ResolutionStatus::Waiting + || (*conn).resolution_v6 == ResolutionStatus::Waiting { + rexmpp_dns::rexmpp_dns_process(s, read_fds, write_fds); + } + if (*conn).resolution_v4 == ResolutionStatus::Failure + && (*conn).resolution_v6 == ResolutionStatus::Failure { + // Failed to resolve anything + return ConnectionError::Failure; + } + + // New connections + let mut repeat: bool; + let mut now = mem::MaybeUninit::::uninit(); + let now_ptr = now.as_mut_ptr(); + loop { + repeat = false; + if (*conn).connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS as i32 + && (rexmpp_tcp_conn_ipv4_available(conn) + || rexmpp_tcp_conn_ipv6_available(conn)) { + clock_gettime(CLOCK_MONOTONIC, now_ptr); + if (*now_ptr).tv_sec > (*conn).next_connection_time.tv_sec + || ((*now_ptr).tv_sec == (*conn).next_connection_time.tv_sec + && (*now_ptr).tv_nsec >= (*conn).next_connection_time.tv_nsec) { + // Time to attempt a new connection + let mut use_ipv6 = false; + if rexmpp_tcp_conn_ipv4_available(conn) && + rexmpp_tcp_conn_ipv6_available(conn) { + if (*conn).addr_cur_v4 >= (*conn).addr_cur_v6 { + use_ipv6 = true; + } + } else if rexmpp_tcp_conn_ipv6_available(conn) { + use_ipv6 = true; + } + + let addr: *mut sockaddr; + let addrlen: socklen_t; + let domain: c_int; + if use_ipv6 { + let mut addr_v6: sockaddr_in6 = mem::zeroed(); + (*conn).addr_cur_v6 += 1; + let len = (mem::size_of::() as i32) + .min(*(*(*conn).resolved_v6).len.offset((*conn).addr_cur_v6 as isize)); + memcpy(&mut addr_v6.sin6_addr as *mut in6_addr as *mut c_void, + *(*(*conn).resolved_v6).data.offset((*conn).addr_cur_v6 as isize), + len as usize); + addr_v6.sin6_family = AF_INET6 as u16; + addr_v6.sin6_port = (*conn).port.to_be(); + addr_v6.sin6_flowinfo = 0; + addr_v6.sin6_scope_id = 0; + domain = AF_INET6; + addr = &mut addr_v6 as *mut sockaddr_in6 as *mut sockaddr; + addrlen = mem::size_of::() as u32; + } else { + let mut addr_v4: sockaddr_in = mem::zeroed(); + (*conn).addr_cur_v4 += 1; + let len = (mem::size_of::() as i32) + .min(*(*(*conn).resolved_v4).len.offset((*conn).addr_cur_v4 as isize)); + memcpy(&mut addr_v4.sin_addr as *mut in_addr as *mut c_void, + *(*(*conn).resolved_v4).data.offset((*conn).addr_cur_v4 as isize), + len as usize); + addr_v4.sin_family = AF_INET as u16; + addr_v4.sin_port = (*conn).port.to_be(); + domain = AF_INET; + addr = &mut addr_v4 as *mut sockaddr_in as *mut sockaddr; + addrlen = mem::size_of::() as u32; + } + (*conn).sockets[(*conn).connection_attempts as usize] = + rexmpp_tcp_socket(s, domain); + if connect((*conn).sockets[(*conn).connection_attempts as usize], + addr, addrlen) != 0 { + if errno().0 == EINPROGRESS { + clock_gettime(CLOCK_MONOTONIC, &mut (*conn).next_connection_time); + (*conn).next_connection_time.tv_nsec += + REXMPP_TCP_CONN_DELAY_MS * 1000000; + if (*conn).next_connection_time.tv_nsec >= 1000000000 { + (*conn).next_connection_time.tv_nsec -= 1000000000; + (*conn).next_connection_time.tv_sec += 1; + } + (*conn).connection_attempts += 1; + } else { + close((*conn).sockets[(*conn).connection_attempts as usize]); + (*conn).sockets[(*conn).connection_attempts as usize] = -1; + if (*conn).connection_attempts < REXMPP_TCP_MAX_CONNECTION_ATTEMPTS as i32 + && (rexmpp_tcp_conn_ipv4_available(conn) || + rexmpp_tcp_conn_ipv6_available(conn)) { + repeat = true; + } + } + } else { + return rexmpp_tcp_connected(conn, + (*conn).sockets[(*conn).connection_attempts as usize]); + } + } + } + if ! repeat { + break; + } + } + + let mut active_connections = false; + for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) { + if (*conn).sockets[i] != -1 { + active_connections = true; + break; + } + } + + clock_gettime(CLOCK_MONOTONIC, now_ptr); + + if active_connections + || (*conn).resolution_v4 == ResolutionStatus::Waiting + || (*conn).resolution_v6 == ResolutionStatus::Waiting + || ((*conn).next_connection_time.tv_sec > (*now_ptr).tv_sec + || ((*conn).next_connection_time.tv_sec == (*now_ptr).tv_sec + && (*conn).next_connection_time.tv_nsec > (*now_ptr).tv_nsec)) { + ConnectionError::InProgress + } else { + ConnectionError::Failure + } +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_conn_fds (s: *mut rexmpp::Rexmpp, + conn: *mut RexmppTCPConnection, + read_fds: *mut fd_set, + write_fds: *mut fd_set) -> c_int { + let mut max_fd: c_int = 0; + if (*conn).resolution_v4 == ResolutionStatus::Waiting + || (*conn).resolution_v6 == ResolutionStatus::Waiting { + max_fd = rexmpp_dns::rexmpp_dns_fds(s, read_fds, write_fds); + } + for i in 0..=(REXMPP_TCP_MAX_CONNECTION_ATTEMPTS - 1) { + if (*conn).sockets[i] != -1 { + FD_SET((*conn).sockets[i], write_fds); + if max_fd < (*conn).sockets[i] + 1 { + max_fd = (*conn).sockets[i] + 1; + } + } + } + max_fd +} + +#[no_mangle] +unsafe extern "C" +fn rexmpp_tcp_conn_timeout (s: *mut rexmpp::Rexmpp, + conn: *mut RexmppTCPConnection, + max_tv: *mut timespec, + tv: *mut timespec) -> *mut timespec { + let mut now: timespec = mem::zeroed(); + let mut ret: *mut timespec = max_tv; + if (*conn).resolution_v4 == ResolutionStatus::Waiting + || (*conn).resolution_v6 == ResolutionStatus::Waiting { + ret = rexmpp_dns::rexmpp_dns_timeout(s, max_tv, tv); + } + if (*conn).resolution_v4 == ResolutionStatus::Success + || (*conn).resolution_v6 == ResolutionStatus::Success + || ((*conn).resolution_v4 == ResolutionStatus::Inactive + && (*conn).resolution_v4 == ResolutionStatus::Inactive) { + clock_gettime(CLOCK_MONOTONIC, &mut now); + if now.tv_sec < (*conn).next_connection_time.tv_sec + || (now.tv_sec == (*conn).next_connection_time.tv_sec + && now.tv_nsec <= (*conn).next_connection_time.tv_nsec) { + if ret == null_mut() + || (*ret).tv_sec > (*conn).next_connection_time.tv_sec - now.tv_sec + || ((*ret).tv_sec == (*conn).next_connection_time.tv_sec - now.tv_sec + && (*ret).tv_nsec > (*conn).next_connection_time.tv_nsec - now.tv_sec) { + ret = tv; + (*tv).tv_sec = (*conn).next_connection_time.tv_sec - now.tv_sec; + if (*conn).next_connection_time.tv_nsec > now.tv_nsec { + (*tv).tv_nsec = (*conn).next_connection_time.tv_nsec - now.tv_nsec; + } else { + (*tv).tv_nsec = (*conn).next_connection_time.tv_nsec + 1000000000 - now.tv_nsec; + (*tv).tv_sec -= 1; + } + } + } + } + ret +} -- cgit v1.2.3