summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2023-06-20 21:49:53 +0300
committerdefanor <defanor@uberspace.net>2023-06-20 21:49:53 +0300
commitc3cb19dec32ffae9e4f93c269b4e1d3504321643 (patch)
treed5c5bc4fc9582b01b68a4056a52a00692d77f5da
parentc12a24ef337889ed1f980cce2baf78ac6bd0ee93 (diff)
Add Rust versions of the TCP module and of a few structures
-rw-r--r--src/Cargo.toml1
-rw-r--r--src/Makefile.am8
-rw-r--r--src/rexmpp.rs255
-rw-r--r--src/rexmpp_dns.c2
-rw-r--r--src/rexmpp_dns.rs62
-rw-r--r--src/rexmpp_jid.rs17
-rw-r--r--src/rexmpp_rust.rs6
-rw-r--r--src/rexmpp_tcp.rs473
8 files changed, 820 insertions, 4 deletions
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::<sockaddr>::uninit();
+ let mut sa_len : socklen_t = mem::size_of::<sockaddr>() 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::<c_int>() 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::<sockaddr_in>::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::<sockaddr_in>() 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::<sockaddr_in6>::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::<sockaddr_in6>() 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::<c_int>() 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::<timespec>::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::<in6_addr>() 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::<sockaddr_in6>() as u32;
+ } else {
+ let mut addr_v4: sockaddr_in = mem::zeroed();
+ (*conn).addr_cur_v4 += 1;
+ let len = (mem::size_of::<in_addr>() 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::<sockaddr_in>() 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
+}