use libc::{
__errno_location,
__u32,
AF_INET,
AF_INET6,
EINPROGRESS,
SOCK_STREAM,
SOL_TCP,
TCP_QUEUE_SEQ,
TCP_REPAIR,
TCP_REPAIR_QUEUE,
c_int,
c_void,
close as libc_close,
connect as libc_connect,
in_addr,
in6_addr,
setsockopt,
sockaddr,
sockaddr_in,
sockaddr_in6,
socket,
};
static TCP_SEND_QUEUE: __u32 = 2;
use std::io::{Error, ErrorKind, Result};
use std::mem::size_of;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::os::unix::io::FromRawFd;
use log::debug;
#[cfg(feature = "async")]
use async_io;
#[cfg(feature = "async")]
use async_net;
#[derive(Clone, Copy, PartialEq)]
pub enum Family {
V4,
V6,
}
fn family_matches(socket_addr: &SocketAddr, family: Option<Family>) -> bool {
if let Some(f) = family {
if f == Family::V4 && !socket_addr.is_ipv4() {
return false;
}
if f == Family::V6 && !socket_addr.is_ipv6() {
return false;
}
}
true
}
pub fn connect<A: ToSocketAddrs>(
sequence_no: u32,
addr: A,
force_family: Option<Family>,
) -> Result<std::net::TcpStream> {
unsafe {
let socket_addrs = addr.to_socket_addrs()?;
let mut maybe_err = None;
for socket_addr in socket_addrs {
if !family_matches(&socket_addr, force_family) {
debug!("skipping {}, not of requested family", socket_addr);
continue;
}
debug!("Trying to connect to {}", socket_addr);
let sock = create_socket(family_of(&socket_addr), sequence_no)?;
match connect_socket(sock, socket_addr, true) {
Ok(()) => {
debug!("Connected to {}", socket_addr);
return Ok(std::net::TcpStream::from_raw_fd(sock));
}
Err(e) => maybe_err = Some(e),
}
libc_close(sock);
}
if let Some(e) = maybe_err {
return Err(e);
}
}
Err(Error::new(
ErrorKind::AddrNotAvailable,
"No address entries for hostname",
))
}
#[cfg(feature = "async")]
pub async fn connect_async<A>(
sequence_no: u32,
socket_addr: A,
force_family: Option<Family>,
) -> Result<async_net::TcpStream>
where
A: async_net::AsyncToSocketAddrs,
{
let socket_addrs = async_net::resolve(socket_addr).await?;
unsafe {
let mut maybe_err = None;
for socket_addr in socket_addrs {
if !family_matches(&socket_addr, force_family) {
debug!("skipping {}, not of requested family", socket_addr);
continue;
}
debug!("Trying to connect to {}", socket_addr);
let sock = create_socket(family_of(&socket_addr), sequence_no)?;
match connect_socket(sock, socket_addr, true) {
Ok(()) => {
let stream = match async_io::Async::new(std::net::TcpStream::from_raw_fd(sock))
{
Ok(s) => s,
Err(e) => {
maybe_err = Some(e);
continue;
}
};
match stream.writable().await {
Ok(_) => match stream.get_ref().take_error()? {
None => {
debug!("Connected to {}", socket_addr);
return Ok(stream.into());
}
Some(e) => {
maybe_err = Some(e);
continue;
}
},
Err(e) => maybe_err = Some(e),
}
}
Err(e) => maybe_err = Some(e),
}
libc_close(sock);
}
if let Some(e) = maybe_err {
return Err(e);
}
}
Err(Error::new(
ErrorKind::AddrNotAvailable,
"No address entries for hostname",
))
}
unsafe fn connect_socket(
sock: c_int,
socket_addr: std::net::SocketAddr,
blocking: bool,
) -> Result<()> {
unsafe {
match socket_addr {
SocketAddr::V4(v4addr) => {
let octets = v4addr.ip().octets();
let u32_addr: u32 = (octets[0] as u32)
| (octets[1] as u32) << 8
| (octets[2] as u32) << 16
| (octets[3] as u32) << 24;
let saddr = sockaddr_in {
sin_family: AF_INET as u16,
sin_port: v4addr.port().to_be(),
sin_addr: in_addr { s_addr: u32_addr },
sin_zero: [0; 8],
};
let result = libc_connect(
sock,
&saddr as *const sockaddr_in as *const sockaddr,
size_of::<sockaddr_in>() as u32,
);
if result < 0 && (blocking || (*__errno_location()) != EINPROGRESS) {
return Err(Error::last_os_error());
}
}
SocketAddr::V6(v6addr) => {
let saddr = sockaddr_in6 {
sin6_family: AF_INET6 as u16,
sin6_port: v6addr.port().to_be(),
sin6_flowinfo: 0,
sin6_addr: in6_addr {
s6_addr: v6addr.ip().octets(),
},
sin6_scope_id: 0,
};
let result = libc_connect(
sock,
&saddr as *const sockaddr_in6 as *const sockaddr,
size_of::<sockaddr_in6>() as u32,
);
if result < 0 && (blocking || (*__errno_location()) != EINPROGRESS) {
return Err(Error::last_os_error());
}
}
}
Ok(())
}
}
unsafe fn sso_tcp_wrapper(sock: c_int, cmd: c_int, data: u32) -> Result<()> {
unsafe {
let dataptr = &data as *const __u32 as *const c_void;
if setsockopt(sock, SOL_TCP, cmd, dataptr, 4) < 0 {
return Err(Error::last_os_error());
}
Ok(())
}
}
unsafe fn create_socket(family: c_int, sequence_no: u32) -> Result<c_int> {
unsafe {
let sock: c_int = socket(family, SOCK_STREAM, 0);
if sock < 0 {
return Err(Error::last_os_error());
}
sso_tcp_wrapper(sock, TCP_REPAIR, 1)?;
sso_tcp_wrapper(sock, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE)?;
sso_tcp_wrapper(sock, TCP_QUEUE_SEQ, sequence_no)?;
sso_tcp_wrapper(sock, TCP_REPAIR, 0)?;
Ok(sock)
}
}
fn family_of(socket_addr: &std::net::SocketAddr) -> c_int {
match socket_addr {
SocketAddr::V4(_) => AF_INET,
SocketAddr::V6(_) => AF_INET6,
}
}