use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::mem::size_of; use std::io::Error as IoError; use libc::{ c_int, sockaddr_in, sockaddr_in6, in_addr, in6_addr, socket, bind, listen, accept, connect, close, }; use crate::runtime2::poll::{AsFileDescriptor, FileDescriptor}; #[derive(Debug)] pub enum SocketError { Opening, Modifying, Binding, Listening, Connecting, Accepted, Accepting, } enum SocketState { Opened, Listening, } const SOCKET_BLOCKING: bool = false; /// TCP (client) connection pub struct SocketTcpClient { socket_handle: libc::c_int, is_blocking: bool, } impl SocketTcpClient { pub fn new(ip: IpAddr, port: u16) -> Result { let socket_handle = create_and_connect_socket( libc::SOCK_STREAM, libc::IPPROTO_TCP, ip, port )?; if !set_socket_blocking(socket_handle, SOCKET_BLOCKING) { unsafe{ libc::close(socket_handle); } return Err(SocketError::Modifying); } println!(" CREATE [{:04}] client", socket_handle); return Ok(SocketTcpClient{ socket_handle, is_blocking: SOCKET_BLOCKING, }) } pub(crate) fn new_from_handle(socket_handle: libc::c_int) -> Result { if !set_socket_blocking(socket_handle, SOCKET_BLOCKING) { unsafe{ libc::close(socket_handle); } return Err(SocketError::Modifying); } return Ok(SocketTcpClient{ socket_handle, is_blocking: SOCKET_BLOCKING, }) } pub fn send(&self, message: &[u8]) -> Result { let result = unsafe{ let message_pointer = message.as_ptr().cast(); libc::send(self.socket_handle, message_pointer, message.len() as libc::size_t, 0) }; if result < 0 { return Err(IoError::last_os_error()); } return Ok(result as usize); } /// Receives data from the TCP socket. Returns the number of bytes received. /// More bytes may be present even thought `used < buffer.len()`. pub fn receive(&self, buffer: &mut [u8]) -> Result { let result = unsafe { let message_pointer = buffer.as_mut_ptr().cast(); libc::recv(self.socket_handle, message_pointer, buffer.len(), 0) }; if result < 0 { return Err(IoError::last_os_error()); } return Ok(result as usize); } } impl Drop for SocketTcpClient { fn drop(&mut self) { println!("DESTRUCT [{:04}] client", self.socket_handle); debug_assert!(self.socket_handle >= 0); unsafe{ close(self.socket_handle) }; } } impl AsFileDescriptor for SocketTcpClient { fn as_file_descriptor(&self) -> FileDescriptor { return self.socket_handle; } } /// TCP listener. Yielding new connections pub struct SocketTcpListener { socket_handle: libc::c_int, is_blocking: bool, } impl SocketTcpListener { pub fn new(ip: IpAddr, port: u16) -> Result { // Create and bind let socket_handle = create_and_bind_socket( libc::SOCK_STREAM, libc::IPPROTO_TCP, ip, port )?; if !set_socket_blocking(socket_handle, SOCKET_BLOCKING) { unsafe{ libc::close(socket_handle); } return Err(SocketError::Modifying); } // Listen unsafe { let result = listen(socket_handle, libc::SOMAXCONN); if result < 0 { unsafe{ libc::close(socket_handle); } return Err(SocketError::Listening); } } println!(" CREATE [{:04}] listener", socket_handle); return Ok(SocketTcpListener{ socket_handle, is_blocking: SOCKET_BLOCKING, }); } pub fn accept(&self) -> Result { let (mut address, mut address_size) = create_sockaddr_in_empty(); let address_pointer = &mut address as *mut sockaddr_in; let socket_handle = unsafe { accept(self.socket_handle, address_pointer.cast(), &mut address_size) }; if socket_handle < 0 { return Err(IoError::last_os_error()); } println!(" CREATE [{:04}] client (from listener)", socket_handle); return Ok(socket_handle); } } impl Drop for SocketTcpListener { fn drop(&mut self) { println!("DESTRUCT [{:04}] listener", self.socket_handle); debug_assert!(self.socket_handle >= 0); unsafe{ close(self.socket_handle) }; } } impl AsFileDescriptor for SocketTcpListener { fn as_file_descriptor(&self) -> FileDescriptor { return self.socket_handle; } } /// Raw socket receiver. Essentially a listener that accepts a single connection struct SocketRawRx { listen_handle: c_int, accepted_handle: c_int, } impl SocketRawRx { pub fn new(ip: Option, port: u16) -> Result { let ip = ip.unwrap_or(Ipv4Addr::UNSPECIFIED); // unspecified is the same as INADDR_ANY let address = unsafe{ in_addr{ s_addr: std::mem::transmute(ip.octets()), }}; let socket_address = sockaddr_in{ sin_family: libc::AF_INET as libc::sa_family_t, sin_port: htons(port), sin_addr: address, sin_zero: [0; 8], }; unsafe { let socket_handle = create_and_bind_socket(libc::SOCK_RAW, 0, IpAddr::V4(ip), port)?; let result = listen(socket_handle, 3); if result < 0 { return Err(SocketError::Listening); } return Ok(SocketRawRx{ listen_handle: socket_handle, accepted_handle: -1, }); } } // pub fn try_accept(&mut self, timeout_ms: u32) -> Result<(), SocketError> { // if self.accepted_handle >= 0 { // // Already accepted a connection // return Err(SocketError::Accepted); // } // // let mut socket_address = sockaddr_in{ // sin_family: 0, // sin_port: 0, // sin_addr: in_addr{ s_addr: 0 }, // sin_zero: [0; 8] // }; // let mut size = size_of::() as u32; // unsafe { // let result = accept(self.listen_handle, &mut socket_address as *mut _, &mut size as *mut _); // if result < 0 { // return Err(SocketError::Accepting); // } // } // // return Ok(()); // } } impl Drop for SocketRawRx { fn drop(&mut self) { if self.accepted_handle >= 0 { unsafe { close(self.accepted_handle); } } if self.listen_handle >= 0 { unsafe { close(self.listen_handle); } } } } // The following is essentially stolen from `mio`'s io_source.rs file. #[cfg(unix)] trait AsRawFileDescriptor { fn as_raw_file_descriptor(&self) -> c_int; } impl AsRawFileDescriptor for SocketTcpClient { fn as_raw_file_descriptor(&self) -> c_int { return self.socket_handle; } } /// Performs the `socket` and `bind` calls. fn create_and_bind_socket(socket_type: libc::c_int, protocol: libc::c_int, ip: IpAddr, port: u16) -> Result { let family = socket_family_from_ip(ip); unsafe { let socket_handle = socket(family, socket_type, protocol); if socket_handle < 0 { return Err(SocketError::Opening); } let result = match ip { IpAddr::V4(ip) => { let (socket_address, address_size) = create_sockaddr_in_v4(ip, port); let socket_pointer = &socket_address as *const sockaddr_in; bind(socket_handle, socket_pointer.cast(), address_size) }, IpAddr::V6(ip) => { let (socket_address, address_size) = create_sockaddr_in_v6(ip, port); let socket_pointer= &socket_address as *const sockaddr_in6; bind(socket_handle, socket_pointer.cast(), address_size) } }; if result < 0 { close(socket_handle); return Err(SocketError::Binding); } return Ok(socket_handle); } } /// Performs the `socket` and `connect` calls fn create_and_connect_socket(socket_type: libc::c_int, protocol: libc::c_int, ip: IpAddr, port: u16) -> Result { let family = socket_family_from_ip(ip); unsafe { let socket_handle = socket(family, socket_type, protocol); if socket_handle < 0 { return Err(SocketError::Opening); } let result = match ip { IpAddr::V4(ip) => { let (socket_address, address_size) = create_sockaddr_in_v4(ip, port); let socket_pointer = &socket_address as *const sockaddr_in; connect(socket_handle, socket_pointer.cast(), address_size) }, IpAddr::V6(ip) => { let (socket_address, address_size) = create_sockaddr_in_v6(ip, port); let socket_pointer= &socket_address as *const sockaddr_in6; connect(socket_handle, socket_pointer.cast(), address_size) } }; if result < 0 { close(socket_handle); return Err(SocketError::Connecting); } return Ok(socket_handle); } } #[inline] fn create_sockaddr_in_empty() -> (sockaddr_in, libc::socklen_t) { let socket_address = sockaddr_in{ sin_family: 0, sin_port: 0, sin_addr: in_addr { s_addr: 0 }, sin_zero: [0; 8], }; let address_size = size_of::(); return (socket_address, address_size as _); } #[inline] fn create_sockaddr_in_v4(ip: Ipv4Addr, port: u16) -> (sockaddr_in, libc::socklen_t) { let address = unsafe{ in_addr{ s_addr: std::mem::transmute(ip.octets()) } }; let socket_address = sockaddr_in{ sin_family: libc::AF_INET as libc::sa_family_t, sin_port: htons(port), sin_addr: address, sin_zero: [0; 8] }; let address_size = size_of::(); return (socket_address, address_size as _); } #[inline] fn create_sockaddr_in_v6(ip: Ipv6Addr, port: u16) -> (sockaddr_in6, libc::socklen_t) { // flow label is advised to be, according to RFC6437 a (somewhat // secure) random number taken from a uniform distribution let flow_info = rand::random(); let address = unsafe{ in6_addr{ s6_addr: ip.octets() } }; let socket_address = sockaddr_in6{ sin6_family: libc::AF_INET6 as libc::sa_family_t, sin6_port: htons(port), sin6_flowinfo: flow_info, sin6_addr: address, sin6_scope_id: 0, // incorrect in case of loopback address }; let address_size = size_of::(); return (socket_address, address_size as _); } #[inline] fn set_socket_blocking(handle: libc::c_int, blocking: bool) -> bool { if handle < 0 { return false; } unsafe{ let mut flags = libc::fcntl(handle, libc::F_GETFL, 0); if flags < 0 { return false; } if blocking { flags &= !libc::O_NONBLOCK; } else { flags |= libc::O_NONBLOCK; } let result = libc::fcntl(handle, libc::F_SETFL, flags); if result < 0 { return false; } } return true; } #[inline] fn socket_family_from_ip(ip: IpAddr) -> libc::c_int { return match ip { IpAddr::V4(_) => libc::AF_INET, IpAddr::V6(_) => libc::AF_INET6, }; } #[inline] fn htons(port: u16) -> u16 { return port.to_be(); }