diff --git a/crates/lb/src/net.rs b/crates/lb/src/net.rs index 2899945..0194685 100644 --- a/crates/lb/src/net.rs +++ b/crates/lb/src/net.rs @@ -53,7 +53,7 @@ impl lb_netlib { } /// An IPv4 address representing localhost: `127.0.0.1` - pub extern "Lua-C" fn localhost_v4() -> lb_ipaddr { + pub extern "Lua-C" fn localhost() -> lb_ipaddr { lb_ipaddr(Ipv4Addr::LOCALHOST.into()) } @@ -63,7 +63,7 @@ impl lb_netlib { } /// An IPv4 address representing an unspecified address: `0.0.0.0` - pub extern "Lua-C" fn unspecified_v4() -> lb_ipaddr { + pub extern "Lua-C" fn unspecified() -> lb_ipaddr { lb_ipaddr(Ipv4Addr::UNSPECIFIED.into()) } @@ -73,17 +73,35 @@ impl lb_netlib { } /// An IPv4 address representing the broadcast address: `255.255.255.255` - pub extern "Lua-C" fn broadcast_v4() -> lb_ipaddr { + pub extern "Lua-C" fn broadcast() -> lb_ipaddr { lb_ipaddr(Ipv4Addr::BROADCAST.into()) } - /// Creates an [`lb_ipaddr`] from the given input. + /// Creates an IP address 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. + /// If `addr` is an [`lb_ipaddr`], a copy of that value is returned. If `addr` is an + /// [`lb_socketaddr`], the IP part of the socket address is returned. Otherwise, parses `addr` + /// as an IP address string. Both IPv4 or IPv6 addresses are accepted. + /// + /// An address string may be something like `127.0.0.1`. + /// + /// The type of the parsed address can be checked using [`is_v4`](lb_ipaddr::is_v4) or + /// [`is_v6`](lb_ipaddr::is_v6) functions on the returned [`lb_ipaddr`](lb_ipaddr) value. + /// + /// # Errors + /// + /// This function will throw an error if the input syntax is invalid. + /// + /// # Example + /// + /// ```lua + /// local net = require("lb:net") + /// local addr = net.ipaddr("192.168.1.1") + /// + /// assert(addr:is_v4()) + /// ``` pub extern "Lua" fn ipaddr( - addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + addr: OneOf<(&str, &lb_ipaddr, &lb_socketaddr)>, ) -> Result { if __istype(__ct.lb_ipaddr, addr) { __new(__ct.lb_ipaddr, addr) // copy constructor @@ -98,17 +116,35 @@ impl lb_netlib { Ok(addr.parse()?) } - /// Creates an [`lb_socketaddr`] from the given input. + /// Creates a socket address from the given input. /// - /// A socket address is an IP address with a port number. + /// A socket address is an IP address with a prescribed 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 `addr` is an [`lb_socketaddr`], a copy of that value is returned. If `addr` is an + /// [`lb_ipaddr`], a socket address with that IP part is returned. Otherwise, parses `addr` as a + /// socket address string. Both IPv4 and IPv6 addresses are accepted. /// - /// If `port` is not specified, `0` is used as the default. + /// If `port` is specified, it is always used as the port part of the returned socket address. + /// Otherwise, `0` is used as the default. + /// + /// An address string may be something like `127.0.0.1:3000`. + /// + /// # Errors + /// + /// This function will throw an error if the input syntax is invalid. + /// + /// # Example + /// + /// ```lua + /// local net = require("lb:net") + /// local addr = net.socketaddr("::1", 8080) + /// + /// assert(addr:ip():is_v6()) + /// assert(addr:port() == 8080) + /// assert(addr:is_loopback()) + /// ``` pub extern "Lua" fn socketaddr( - addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + addr: OneOf<(&str, &lb_ipaddr, &lb_socketaddr)>, port: Option, ) -> Result { if port != () { @@ -138,26 +174,50 @@ impl lb_netlib { /// Creates a new TCP socket configured for IPv4. /// - /// See [`TcpSocket::new_v4`]. + /// # Errors + /// + /// This function may throw an error if the socket could not be created. 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 { + /// # Errors + /// + /// This function may throw an error if the socket could not be created. + pub extern "Lua-C" fn tcp_v6() -> Result { Ok(Some(TcpSocket::new_v6()?).into()) } + /// Creates a new TCP socket bound to the given address and port. + /// + /// This function accepts the same arguments as [`socketaddr`](Self::socketaddr). It creates a + /// new TCP socket configured to use either IPv4 or IPv6 depending on the type of the given + /// address, and binds it to that address. + /// + /// # Errors + /// + /// This function may throw an error if the socket could not be created or bound to the + /// specified address. + /// + /// # Example + /// + /// ```lua + /// local net = require("lb:net") + /// local socket = net.bind_tcp("127.0.0.1", 8080) + /// + /// assert(socket:local_addr():ip():is_loopback()) + /// assert(socket:local_addr():port() == 8080) + /// ``` pub async extern "Lua" fn bind_tcp( - addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + addr: OneOf<(&str, &lb_ipaddr, &lb_socketaddr)>, port: Option, ) -> Result { let addr = Self::socketaddr(addr, port); let socket; if addr.ip().is_v6() { - socket = Self::tcp6(); + socket = Self::tcp_v6(); } else { socket = Self::tcp(); } @@ -165,26 +225,45 @@ impl lb_netlib { socket } + /// Creates a new TCP socket listening on the given address and port. + /// + /// This is a convenience function that combines [`bind_tcp`](Self::bind_tcp) and + /// [`listen`](lb_tcpsocket::listen). It accepts the same arguments as + /// [`socketaddr`](Self::socketaddr). + /// + /// # Errors + /// + /// This function may throw an error if the socket could not be created, bound to the specified + /// address, or could not transition to the listening state. + pub async extern "Lua" fn listen_tcp( + addr: OneOf<(&str, &lb_ipaddr, &lb_socketaddr)>, + port: Option, + ) -> Result { + Self::bind_tcp(addr, port).listen(1024) + } + + /// Establishes a new TCP connection to the server at the given address and port. + /// + /// This function accepts the same arguments as [`socketaddr`](Self::socketaddr). It creates a + /// new TCP socket and connects it to the specified address. + /// + /// # Errors + /// + /// This function may throw an error if the socket could not be created or connected to the + /// specified address. pub async extern "Lua" fn connect_tcp( - addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + addr: OneOf<(&str, &lb_ipaddr, &lb_socketaddr)>, port: Option, ) -> Result { let addr = Self::socketaddr(addr, port); let socket; if addr.ip().is_v6() { - socket = Self::tcp6(); + socket = Self::tcp_v6(); } 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. @@ -206,69 +285,109 @@ 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`]. + /// 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 a loopback address. + /// + /// For IPv4, this is any address in the `127.0.0.0/8` range. + /// + /// For IPv6, this is the address `::1`. + pub extern "Lua-C" fn is_loopback(&self) -> bool { + self.0.is_loopback() + } + + /// Returns `true` if this address is unspecified. + /// + /// For IPv4, this is the address `0.0.0.0`. + /// + /// For IPv6, this is the address `::`. + pub extern "Lua-C" fn is_unspecified(&self) -> bool { + self.0.is_unspecified() + } + + /// Returns `true` if this address is a multicast address. + /// + /// For IPv4, this is any address in the `224.0.0.0/4` range. + /// + /// For IPv6, this is any address in the `ff00::/8` range. + pub extern "Lua-C" fn is_multicast(&self) -> bool { + self.0.is_multicast() + } + + /// Returns `true` if this is an IPv4 private address. + /// + /// For IPv4, this is any address in one of these ranges: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// For IPv6, this always returns `false`. + pub extern "Lua-C" fn is_v4_private(&self) -> bool { + match self.0 { + IpAddr::V4(v4) => v4.is_private(), + IpAddr::V6(_) => false, + } + } + + /// Returns `true` if this is an IPv4 link-local address. + /// + /// For IPv4, this is any address in the `169.254.0.0/16` range. + /// + /// For IPv6, this always returns `false`. + pub extern "Lua-C" fn is_v4_link_local(&self) -> bool { + match self.0 { + IpAddr::V4(v4) => v4.is_link_local(), + IpAddr::V6(_) => false, + } + } + + /// Returns `true` if this is an IPv4 broadcast address. + /// + /// For IPv4, this is the address `255.255.255.255`. + /// + /// For IPv6, this always returns `false`. + pub extern "Lua-C" fn is_v4_broadcast(&self) -> bool { + match self.0 { + IpAddr::V4(v4) => v4.is_broadcast(), + IpAddr::V6(_) => false, + } + } + + /// Returns `true` if this is an IPv4 documentation address. + /// + /// For IPv4, this is any address in one of these ranges: + /// + /// - `192.0.2.0/24` (TEST-NET-1) + /// - `198.51.100.0/24` (TEST-NET-2) + /// - `203.0.113.0/24` (TEST-NET-3) + /// + /// For IPv6, this always returns `false`. + 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 unique local address. + /// + /// For IPv4, this always returns `false`. + /// + /// For IPv6, this is any address in the `fc00::/7` range. pub extern "Lua-C" fn is_v6_unique_local(&self) -> bool { match self.0 { IpAddr::V4(_) => false, @@ -276,7 +395,11 @@ impl lb_ipaddr { } } - /// See [`Ipv6Addr::is_unicast_link_local`]. + /// Returns `true` if this is an IPv6 unicast address with link-local scope. + /// + /// For IPv4, this always returns `false`. + /// + /// For IPv6, this is any address in the `fe80::/10` range. pub extern "Lua-C" fn is_v6_unicast_link_local(&self) -> bool { match self.0 { IpAddr::V4(_) => false, @@ -284,25 +407,68 @@ impl lb_ipaddr { } } - /// See [`Ipv4Addr::to_ipv6_compatible`]. - pub extern "Lua-C" fn to_v6_compat(&self) -> Self { + /// Converts this address to IPv4. + /// + /// For IPv4, this returns the address unchanged. + /// + /// For IPv6, this returns the original IPv4 address if it is an IPv4-mapped or IPv4-compatible + /// IPv6 address. Otherwise, this returns `nil`. + pub extern "Lua-C" fn to_v4(&self) -> Option { match self.0 { - IpAddr::V4(v4) => Self(v4.to_ipv6_compatible().into()), - IpAddr::V6(_) => *self, + IpAddr::V4(_) => Some(*self), + IpAddr::V6(v6) => v6.to_ipv4().map(|v| Self(v.into())), } } - /// See [`Ipv4Addr::to_ipv6_mapped`]. - pub extern "Lua-C" fn to_v6_mapped(&self) -> Self { + /// Converts this address to IPv6. + /// + /// For IPv4, this returns the IPv4-mapped IPv6 address as defined by + /// [`Ipv4Addr::to_ipv6_mapped`]. + /// + /// For IPv6, this returns the address unchanged. + pub extern "Lua-C" fn to_v6(&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 canonical form of this address. + /// + /// For IPv4, this returns the address unchanged. + /// + /// For IPv6, this returns the original IPv4 address if it is an IPv4-mapped or IPv4-compatible + /// IPv6 address. Otherwise, this returns the address unchanged. + pub extern "Lua-C" fn to_canonical(&self) -> Self { + match self.0 { + IpAddr::V4(_) => *self, + IpAddr::V6(v6) => v6.to_ipv4().map_or(*self, |v| Self(v.into())), + } + } + + /// Returns `true` if the given addresses are equal. + /// + /// Two addresses are considered equal if they are of the same family (IPv4 or IPv6) and + /// represent the same address in octets. + #[eq] + pub extern "Lua-C" fn equals(left: &Self, right: &Self) -> bool { + left.0 == right.0 + } + + /// Returns `true` if the left address is less than the right address. + /// + /// IPv4 addresses are always less than IPv6 addresses. + #[lt] + pub extern "Lua-C" fn less_than(left: &Self, right: &Self) -> bool { + left.0 < right.0 + } + + /// Returns `true` if the left address is less than or equal to the right address. + /// + /// IPv4 addresses are always less than IPv6 addresses. + #[le] + pub extern "Lua-C" fn less_than_or_equals(left: &Self, right: &Self) -> bool { + left.0 <= right.0 } /// Returns the string representation of this address. @@ -329,7 +495,7 @@ impl lb_socketaddr { /// 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)>, + addr: OneOf<(&str, &lb_ipaddr, &lb_socketaddr)>, ) -> &mut Self { if __istype(__ct.lb_ipaddr, addr) { self.__set_ip(addr); @@ -478,17 +644,17 @@ impl lb_tcpsocket { 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()) } + + /// 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()) + } } /// TCP connection between a local and a remote socket.