509 lines
15 KiB
Rust
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 {}
|