//! # Networking library //! //! The `lb:net` library provides an asynchronous network API for creating TCP or UDP servers and //! clients. //! //! ## Exports //! //! See [`lb_netlib`] for items exported by this library. use derive_more::{From, FromStr}; use luaffi::{cdef, marker::OneOf, metatype}; use std::{ net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, time::Duration, }; use thiserror::Error; use tokio::net::{TcpListener, TcpSocket, TcpStream}; /// Errors that can be thrown by this library. /// /// Functions which return this error will **throw** in Lua. The error message can be caught by /// using [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall). #[derive(Debug, Error)] pub enum Error { /// I/O error. #[error("{0}")] Io(#[from] std::io::Error), /// IP or socket address syntax error. #[error("{0}")] InvalidAddr(#[from] AddrParseError), /// Socket was already converted and cannot be used anymore. #[error("socket was already converted")] SocketConsumed, } type Result = std::result::Result; /// Items exported by the `lb:net` library. /// /// This library can be acquired by calling /// [`require("lb:net")`](https://www.lua.org/manual/5.1/manual.html#pdf-require) in Lua. /// /// ```lua /// local net = require("lb:net"); /// ``` #[cdef(module = "lb:net")] pub struct lb_netlib; #[metatype] impl lb_netlib { #[new] extern "Lua-C" fn new() -> Self { Self } /// An IPv4 address representing localhost: `127.0.0.1` pub extern "Lua-C" fn localhost_v4() -> lb_ipaddr { lb_ipaddr(Ipv4Addr::LOCALHOST.into()) } /// An IPv6 address representing localhost: `::1` pub extern "Lua-C" fn localhost_v6() -> lb_ipaddr { lb_ipaddr(Ipv6Addr::LOCALHOST.into()) } /// An IPv4 address representing an unspecified address: `0.0.0.0` pub extern "Lua-C" fn unspecified_v4() -> lb_ipaddr { lb_ipaddr(Ipv4Addr::UNSPECIFIED.into()) } /// An IPv6 address representing an unspecified address: `::` pub extern "Lua-C" fn unspecified_v6() -> lb_ipaddr { lb_ipaddr(Ipv6Addr::UNSPECIFIED.into()) } /// An IPv4 address representing the broadcast address: `255.255.255.255` pub extern "Lua-C" fn broadcast_v4() -> lb_ipaddr { lb_ipaddr(Ipv4Addr::BROADCAST.into()) } /// Creates an [`lb_ipaddr`] from the given input. /// /// If `s` is an [`lb_ipaddr`], a copy of that value is returned. If `s` is an /// [`lb_socketaddr`], the IP address part of the socket address is returned. Otherwise, parses /// `s` as an IP address string. Both IPv4 or IPv6 addresses are supported. pub extern "Lua" fn ipaddr( addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, ) -> Result { if __istype(__ct.lb_ipaddr, addr) { __new(__ct.lb_ipaddr, addr) // copy constructor } else if __istype(__ct.lb_socketaddr, addr) { s.ip() } else { Self::__parse_ipaddr(addr) } } extern "Lua-C" fn __parse_ipaddr(addr: &str) -> Result { Ok(addr.parse()?) } /// Creates an [`lb_socketaddr`] from the given input. /// /// A socket address is an IP address with a port number. /// /// If `s` is an [`lb_socketaddr`], a copy of that value is returned. If `s` is an /// [`lb_ipaddr`], a socket address with that IP address is returned. Otherwise, parses `s` as a /// socket address string. Both IPv4 and IPv6 addresses are supported. /// /// If `port` is not specified, `0` is used as the default. pub extern "Lua" fn socketaddr( addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, port: Option, ) -> Result { if port != () { Self::__new_skaddr(Self::ipaddr(addr), port) } else { if __istype(__ct.lb_socketaddr, addr) { __new(__ct.lb_socketaddr, addr) // copy constructor } else if __istype(__ct.lb_ipaddr, addr) { Self::__new_skaddr(addr, 0) // default port 0 } else { Self::__parse_skaddr(addr) } } } extern "Lua-C" fn __new_skaddr(ip: &lb_ipaddr, port: u16) -> lb_socketaddr { SocketAddr::new(ip.0, port).into() } extern "Lua-C" fn __parse_skaddr(addr: &str) -> Result { Ok(if let Ok(addr) = addr.parse() { SocketAddr::new(addr, 0).into() // default port 0 } else { addr.parse::()?.into() }) } /// Creates a new TCP socket configured for IPv4. /// /// See [`TcpSocket::new_v4`]. pub extern "Lua-C" fn tcp() -> Result { Ok(Some(TcpSocket::new_v4()?).into()) } /// Creates a new TCP socket configured for IPv6. /// /// See [`TcpSocket::new_v6`]. pub extern "Lua-C" fn tcp6() -> Result { Ok(Some(TcpSocket::new_v6()?).into()) } pub async extern "Lua" fn bind_tcp( addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, port: Option, ) -> Result { let addr = Self::socketaddr(addr, port); let socket; if addr.ip().is_v6() { socket = Self::tcp6(); } else { socket = Self::tcp(); } socket.bind(addr); socket } pub async extern "Lua" fn connect_tcp( addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, port: Option, ) -> Result { let addr = Self::socketaddr(addr, port); let socket; if addr.ip().is_v6() { socket = Self::tcp6(); } else { socket = Self::tcp(); } socket.connect(addr) } pub async extern "Lua" fn listen_tcp( addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, port: Option, ) -> Result { Self::bind_tcp(addr, port).listen(1024) } } /// IP address, either IPv4 or IPv6. /// /// # Example /// /// This example creates an [`lb_ipaddr`] by parsing an IP address string. /// /// ```lua /// local net = require("lb:net"); /// local addr = net.ipaddr("127.0.0.1"); -- ipv4 loopback address /// /// assert(addr:is_v4()); /// assert(addr:is_loopback()); /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, From, FromStr)] #[cdef] pub struct lb_ipaddr(#[opaque] IpAddr); #[metatype] impl lb_ipaddr { /// See [`IpAddr::is_unspecified`]. pub extern "Lua-C" fn is_unspecified(&self) -> bool { self.0.is_unspecified() } /// See [`IpAddr::is_loopback`]. pub extern "Lua-C" fn is_loopback(&self) -> bool { self.0.is_loopback() } /// See [`IpAddr::is_multicast`]. pub extern "Lua-C" fn is_multicast(&self) -> bool { self.0.is_multicast() } /// Returns the string `"v4"` if this is an IPv4 address or `"v6"` if this is an IPv6 address. pub extern "Lua" fn family(&self) -> String { if self.is_v6() { "v6" } else { "v4" } } /// Returns `true` if this is an IPv4 address. pub extern "Lua-C" fn is_v4(&self) -> bool { self.0.is_ipv4() } /// See [`Ipv4Addr::is_private`]. pub extern "Lua-C" fn is_v4_private(&self) -> bool { match self.0 { IpAddr::V4(v4) => v4.is_private(), IpAddr::V6(_) => false, } } /// See [`Ipv4Addr::is_link_local`]. pub extern "Lua-C" fn is_v4_link_local(&self) -> bool { match self.0 { IpAddr::V4(v4) => v4.is_link_local(), IpAddr::V6(_) => false, } } /// See [`Ipv4Addr::is_broadcast`]. pub extern "Lua-C" fn is_v4_broadcast(&self) -> bool { match self.0 { IpAddr::V4(v4) => v4.is_broadcast(), IpAddr::V6(_) => false, } } /// See [`Ipv4Addr::is_documentation`]. pub extern "Lua-C" fn is_v4_documentation(&self) -> bool { match self.0 { IpAddr::V4(v4) => v4.is_documentation(), IpAddr::V6(_) => false, } } /// Returns `true` if this is an IPv6 address. pub extern "Lua-C" fn is_v6(&self) -> bool { self.0.is_ipv6() } /// See [`Ipv6Addr::is_unique_local`]. pub extern "Lua-C" fn is_v6_unique_local(&self) -> bool { match self.0 { IpAddr::V4(_) => false, IpAddr::V6(v6) => v6.is_unique_local(), } } /// See [`Ipv6Addr::is_unicast_link_local`]. pub extern "Lua-C" fn is_v6_unicast_link_local(&self) -> bool { match self.0 { IpAddr::V4(_) => false, IpAddr::V6(v6) => v6.is_unicast_link_local(), } } /// See [`Ipv4Addr::to_ipv6_compatible`]. pub extern "Lua-C" fn to_v6_compat(&self) -> Self { match self.0 { IpAddr::V4(v4) => Self(v4.to_ipv6_compatible().into()), IpAddr::V6(_) => *self, } } /// See [`Ipv4Addr::to_ipv6_mapped`]. pub extern "Lua-C" fn to_v6_mapped(&self) -> Self { match self.0 { IpAddr::V4(v4) => Self(v4.to_ipv6_mapped().into()), IpAddr::V6(_) => *self, } } /// See [`IpAddr::to_canonical`]. pub extern "Lua-C" fn canonical(&self) -> Self { self.0.to_canonical().into() } /// Returns the string representation of this address. #[tostring] pub extern "Lua-C" fn tostring(&self) -> String { self.0.to_string() } } /// Socket address, which is an IP address with a port number. #[derive(Debug, Clone, Copy, PartialEq, Eq, From, FromStr)] #[cdef] pub struct lb_socketaddr(#[opaque] SocketAddr); #[metatype] impl lb_socketaddr { /// Returns the IP part of this address. pub extern "Lua-C" fn ip(&self) -> lb_ipaddr { self.0.ip().into() } /// Sets the IP part of this address. /// /// This function accepts the same arguments as [`ipaddr`](lb_netlib::ipaddr). pub extern "Lua" fn set_ip( &mut self, addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, ) -> &mut Self { if __istype(__ct.lb_ipaddr, addr) { self.__set_ip(addr); } else if __istype(__ct.lb_socketaddr, addr) { self.__set_ip(addr.ip()); } else { self.__set_ip_parse(addr); } self } extern "Lua-C" fn __set_ip(&mut self, ip: &lb_ipaddr) { self.0.set_ip(ip.0); } extern "Lua-C" fn __set_ip_parse(&mut self, addr: &str) -> Result<()> { Ok(self.0.set_ip(addr.parse()?)) } /// Returns the port part of this address. pub extern "Lua-C" fn port(&self) -> u16 { self.0.port() } /// Sets the port part of this address. pub extern "Lua" fn set_port(&mut self, port: u16) -> &mut Self { self.__set_port(port); self } extern "Lua-C" fn __set_port(&mut self, port: u16) { self.0.set_port(port) } /// Returns the string representation of this address. #[tostring] pub extern "Lua-C" fn tostring(&self) -> String { self.0.to_string() } } /// TCP socket which has not yet been converted to an [`lb_tcpstream`] or [`lb_tcplistener`]. #[derive(Debug, From)] #[cdef] pub struct lb_tcpsocket(#[opaque] Option); #[metatype] impl lb_tcpsocket { fn socket(&self) -> Result<&TcpSocket> { self.0.as_ref().ok_or(Error::SocketConsumed) } /// See [`TcpSocket::keepalive`]. pub extern "Lua-C" fn keepalive(&self) -> Result { Ok(self.socket()?.keepalive()?) } /// See [`TcpSocket::set_keepalive`]. pub extern "Lua-C" fn set_keepalive(&self, enabled: bool) -> Result<()> { Ok(self.socket()?.set_keepalive(enabled)?) } /// See [`TcpSocket::reuseaddr`]. pub extern "Lua-C" fn reuseaddr(&self) -> Result { Ok(self.socket()?.reuseaddr()?) } /// See [`TcpSocket::set_reuseaddr`]. pub extern "Lua-C" fn set_reuseaddr(&self, enabled: bool) -> Result<()> { Ok(self.socket()?.set_reuseaddr(enabled)?) } /// See [`TcpSocket::reuseport`]. pub extern "Lua-C" fn reuseport(&self) -> Result { Ok(self.socket()?.reuseport()?) } /// See [`TcpSocket::set_reuseport`]. pub extern "Lua-C" fn set_reuseport(&self, enabled: bool) -> Result<()> { Ok(self.socket()?.set_reuseport(enabled)?) } /// See [`TcpSocket::send_buffer_size`]. pub extern "Lua-C" fn sendbuf(&self) -> Result { Ok(self.socket()?.send_buffer_size()?) } /// See [`TcpSocket::set_send_buffer_size`]. pub extern "Lua-C" fn set_sendbuf(&self, size: u32) -> Result<()> { Ok(self.socket()?.set_send_buffer_size(size)?) } /// See [`TcpSocket::recv_buffer_size`]. pub extern "Lua-C" fn recvbuf(&self) -> Result { Ok(self.socket()?.recv_buffer_size()?) } /// See [`TcpSocket::set_recv_buffer_size`]. pub extern "Lua-C" fn set_recvbuf(&self, size: u32) -> Result<()> { Ok(self.socket()?.set_recv_buffer_size(size)?) } /// See [`TcpSocket::linger`]. pub extern "Lua-C" fn linger(&self) -> Result { Ok(self .socket()? .linger()? .map(|n| n.as_secs_f64()) .unwrap_or(0.)) } /// See [`TcpSocket::set_linger`]. pub extern "Lua-C" fn set_linger(&self, secs: f64) -> Result<()> { Ok(self .socket()? .set_linger((secs != 0.).then_some(Duration::from_secs_f64(secs)))?) } /// See [`TcpSocket::nodelay`]. pub extern "Lua-C" fn nodelay(&self) -> Result { Ok(self.socket()?.nodelay()?) } /// See [`TcpSocket::set_nodelay`]. pub extern "Lua-C" fn set_nodelay(&self, enabled: bool) -> Result<()> { Ok(self.socket()?.set_nodelay(enabled)?) } /// See [`TcpSocket::tos`]. pub extern "Lua-C" fn tos(&self) -> Result { Ok(self.socket()?.tos()?) } /// See [`TcpSocket::set_tos`]. pub extern "Lua-C" fn set_tos(&self, tos: u32) -> Result<()> { Ok(self.socket()?.set_tos(tos)?) } /// See [`TcpSocket::local_addr`]. pub extern "Lua-C" fn local_addr(&self) -> Result { Ok(self.socket()?.local_addr()?.into()) } /// See [`TcpSocket::bind`]. pub extern "Lua-C" fn bind(&self, addr: &lb_socketaddr) -> Result<()> { Ok(self.socket()?.bind(addr.0)?) } /// See [`TcpSocket::connect`]. pub async extern "Lua-C" fn connect(&mut self, addr: &lb_socketaddr) -> Result { let socket = self.0.take().ok_or(Error::SocketConsumed)?; Ok(socket.connect(addr.0).await?.into()) } /// See [`TcpSocket::listen`]. pub extern "Lua-C" fn listen(&mut self, backlog: u32) -> Result { let socket = self.0.take().ok_or(Error::SocketConsumed)?; Ok(socket.listen(backlog)?.into()) } } /// TCP connection between a local and a remote socket. #[derive(Debug, From)] #[cdef] pub struct lb_tcpstream(#[opaque] TcpStream); #[metatype] impl lb_tcpstream {} /// TCP socket server, listening for connections. #[derive(Debug, From)] #[cdef] pub struct lb_tcplistener(#[opaque] TcpListener); #[metatype] impl lb_tcplistener {}