luby/crates/lb/src/net.rs

509 lines
15 KiB
Rust

//! # 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<T> = std::result::Result<T, Error>;
/// 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<lb_ipaddr> {
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<lb_ipaddr> {
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<u16>,
) -> Result<lb_socketaddr> {
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<lb_socketaddr> {
Ok(if let Ok(addr) = addr.parse() {
SocketAddr::new(addr, 0).into() // default port 0
} else {
addr.parse::<SocketAddr>()?.into()
})
}
/// Creates a new TCP socket configured for IPv4.
///
/// See [`TcpSocket::new_v4`].
pub extern "Lua-C" fn tcp() -> Result<lb_tcpsocket> {
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<lb_tcpsocket> {
Ok(Some(TcpSocket::new_v6()?).into())
}
pub async extern "Lua" fn bind_tcp(
addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>,
port: Option<u16>,
) -> Result<lb_tcpsocket> {
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<u16>,
) -> Result<lb_tcpstream> {
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<u16>,
) -> Result<lb_tcplistener> {
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<TcpSocket>);
#[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<bool> {
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<bool> {
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<bool> {
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<u32> {
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<u32> {
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<f64> {
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<bool> {
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<u32> {
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<lb_socketaddr> {
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<lb_tcpstream> {
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<lb_tcplistener> {
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 {}