diff --git a/Cargo.toml b/Cargo.toml index 65c9bd2d0799e751f87ad4f44e6c59e403ee6a1f..339f8f5edd7837d35b1115453a8590aac363d184 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,10 @@ authors = [ ] edition = "2021" +[features] +default=["internet"] +internet=["libc"] + [dependencies] # ffi diff --git a/src/runtime2/stdlib/internet.rs b/src/runtime2/stdlib/internet.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9fe43a02c06d383d2e5c3296b3bda3bdff52f136 100644 --- a/src/runtime2/stdlib/internet.rs +++ b/src/runtime2/stdlib/internet.rs @@ -0,0 +1,277 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::mem::size_of; + +use libc::{ + c_int, + sockaddr_in, sockaddr_in6, in_addr, in6_addr, + socket, bind, listen, accept, connect, close, +}; + +#[derive(Debug)] +pub enum SocketError { + Opening, + Binding, + Listening, + Connecting, + Accepted, + Accepting, +} + +enum SocketState { + Opened, + Listening, +} + +/// TCP connection +pub struct SocketTcpClient { + socket_handle: libc::c_int +} + +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 + )?; + + return Ok(SocketTcpClient{ + socket_handle, + }) + } + + 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(()) + } + + return Ok(result as usize); + } + + 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() as libc::size_t, 0) + }; + if result < 0 { + return Err(()) + } + + return Ok(result as usize); + } +} + +impl Drop for SocketTcpClient { + fn drop(&mut self) { + debug_assert!(self.socket_handle >= 0); + unsafe{ close(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); + } + } + } +} + + + +/// 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_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 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(); +} + +mod tests { + use std::net::*; + use super::*; + + #[test] + fn test_inet_thingo() { + let s = SocketTcpClient::new(IpAddr::V4(Ipv4Addr::new(142, 250, 179, 163)), 80).expect("connect"); + s.send(b"GET / HTTP/1.1\r\n\r\n").expect("sending"); + let mut buffer = [0;65000]; + s.receive(&mut buffer).expect("receiving"); + let as_str = String::from_utf8_lossy(&buffer); + println!("Yay! Got:\n{}", as_str); + } +} \ No newline at end of file diff --git a/src/runtime2/stdlib/mod.rs b/src/runtime2/stdlib/mod.rs index 272d4fc7f5e5478a1ef3477d6c6a311ef6d1f513..ee9cc7dbb4dce4e1c088c53c5f86c68ec97c23d3 100644 --- a/src/runtime2/stdlib/mod.rs +++ b/src/runtime2/stdlib/mod.rs @@ -1 +1 @@ -mod internet; \ No newline at end of file +#[cfg(feature="internet")] mod internet; \ No newline at end of file