///|
type Ipv4addr Bytes
///|
type Ipv6addr Bytes
///|
type Ipaddr Bytes
///|
#external
type AddrInfoResults
///|
pub struct AddrInfo {
family : Int
socktype : Int
protocol : Int
addr : Ipaddr
canonname : String?
}
///|
pub(open) trait ToIpaddr {
ip_name(self : Self) -> String raise NetError
to_socket_addr(self : Self) -> SocketAddr raise NetError
to_ipaddr(self : Self) -> Ipaddr raise NetError
}
///|
trait RawPointer {
is_null(self : Self) -> Bool
}
///|
pub(open) trait ToCString {
to_cstring(self : Self) -> Bytes
}
///|
pub impl ToIpaddr for Ipaddr with to_ipaddr(self : Ipaddr) -> Ipaddr = "%identity"
///|
pub impl ToIpaddr for Ipv4addr with to_ipaddr(self : Ipv4addr) -> Ipaddr = "%identity"
///|
pub impl ToIpaddr for Ipv6addr with to_ipaddr(self : Ipv6addr) -> Ipaddr = "%identity"
///|
extern "C" fn addrinfo_is_null_pointer_ffi(ptr : AddrInfoResults) -> Bool = "moonbit_is_null_pointer"
///|
extern "C" fn addr_is_null_pointer_ffi(addr : Ipaddr) -> Bool = "moonbit_is_null_pointer"
///|
extern "C" fn bytes_is_null_pointer_ffi(bytes : Bytes) -> Bool = "moonbit_is_null_pointer"
///|
extern "C" fn ipv4_addrlen_ffi() -> Int = "moonbit_ipv4_addrlen_ffi"
///|
extern "C" fn ipv6_addrlen_ffi() -> Int = "moonbit_ipv6_addrlen_ffi"
///|
extern "C" fn get_ip_name_ffi(
addr : Ipaddr,
cb : (Int, Int, Int, Int, @c.Pointer[Byte]) -> Bool
) -> Int = "moonbit_get_ip_name_ffi"
///|
extern "C" fn get_ipv4_name_ffi(addr : Ipv4addr, bytes : Bytes) -> Int = "moonbit_get_ipv4_name_ffi"
///|
extern "C" fn get_ipv6_name_ffi(addr : Ipv6addr, bytes : Bytes) -> Int = "moonbit_get_ipv6_name_ffi"
///|
extern "C" fn create_sockaddr_storage_ffi() -> Ipaddr = "moonbit_create_sockaddr_storage_ffi"
///|
let _IP_V4_ADDRLEN : Int = ipv4_addrlen_ffi()
///|
let _IP_V6_ADDRLEN : Int = ipv6_addrlen_ffi()
///|
impl RawPointer for AddrInfoResults with is_null(self : AddrInfoResults) -> Bool {
return addrinfo_is_null_pointer_ffi(self)
}
///|
impl RawPointer for Ipaddr with is_null(self : Ipaddr) -> Bool {
return addr_is_null_pointer_ffi(self)
}
///|
impl RawPointer for Bytes with is_null(self : Bytes) -> Bool {
return bytes_is_null_pointer_ffi(self)
}
///|
pub impl ToCString for String with to_cstring(self : String) -> Bytes {
let buf = @buffer.new()
@encoding.encode_to(self, buf, encoding=UTF8)
buf.write_byte((0).to_byte()) // Null-terminate the string
return buf.contents()
}
///|
pub impl ToIpaddr for Ipaddr with to_socket_addr(self : Ipaddr) -> SocketAddr raise NetError {
let buffer = @buffer.new()
let mut family : Int = -1
let mut port : Int = 0
let mut flowinfo : Int = 0
let mut scope_id : Int = 0
let status = get_ip_name_ffi(self, fn(
family_val : Int,
port_val : Int,
flowinfo_val : Int,
scope_id_val : Int,
ip_str : @c.Pointer[Byte]
) {
if ip_str.is_null() {
return false
}
for i = 0; ip_str[i] != 0; i = i + 1 {
buffer.write_byte(ip_str[i])
}
family = family_val
port = port_val
flowinfo = flowinfo_val
scope_id = scope_id_val
return true
})
guard status >= 0 else {
raise NetError("Failed to get IP address name and port from ffi")
}
let decoder = @encoding.decoder(UTF8)
let host = decoder.lossy_consume(buffer.contents())
if family == _AF_INET6 {
return SocketAddr::V6(
host,
port.to_uint16(),
flowinfo=flowinfo.reinterpret_as_uint(),
scope_id=scope_id.reinterpret_as_uint(),
)
} else if family == _AF_INET {
return SocketAddr::V4(host, port.to_uint16())
} else {
raise NetError("Unsupported address family: \{family}")
}
}
///|
pub impl ToIpaddr for Ipaddr with ip_name(self : Ipaddr) -> String raise NetError {
match self.to_socket_addr() {
V4(host, _) => host
V6(host, _, ..) => host
}
}
///|
pub impl ToIpaddr for Ipv4addr with to_socket_addr(self : Ipv4addr) -> SocketAddr raise NetError {
self.to_ipaddr().to_socket_addr()
}
///|
pub impl ToIpaddr for Ipv4addr with ip_name(self : Ipv4addr) -> String raise NetError {
self.to_ipaddr().ip_name()
}
///|
pub impl ToIpaddr for Ipv6addr with to_socket_addr(self : Ipv6addr) -> SocketAddr raise NetError {
self.to_ipaddr().to_socket_addr()
}
///|
pub impl ToIpaddr for Ipv6addr with ip_name(self : Ipv6addr) -> String raise NetError {
let bytes = Bytes::make(_IP_V6_ADDRLEN, 0)
let status = get_ipv6_name_ffi(self, bytes)
guard status >= 0 else {
raise NetError("Failed to get IPv6 address name from ffi")
}
let buffer = @buffer.new()
for i = 0; bytes[i] != 0; i = i + 1 {
buffer.write_byte(bytes[i])
}
let decoder = @encoding.decoder(UTF8)
return decoder.lossy_consume(buffer.contents())
}
///|
extern "C" fn getaddrinfo_make_ffi(
hostname : Bytes,
service : Bytes,
ai_flags : Int,
ai_family : Int,
ai_socktype : Int,
ai_protocol : Int
) -> AddrInfoResults = "moonbit_getaddrinfo_make_ffi"
///|
extern "C" fn addrinfo_results_iterate_ffi(
addr_info : AddrInfoResults,
cb : (Int, Int, Int, Int, Ipaddr, @c.Pointer[Byte]) -> Bool
) -> Bool = "moonbit_addrinfo_results_iterate_ffi"
///|
pub fn AddrInfoResults::iter(self : AddrInfoResults) -> Iter[AddrInfo] {
Iter::new(fn(yield_ : (AddrInfo) -> IterResult) {
if addrinfo_results_iterate_ffi(self, fn(
_,
family : Int,
socktype : Int,
protocol : Int,
addr : Ipaddr,
canonname : @c.Pointer[Byte]
) {
let canonname = if canonname.is_null() {
None
} else {
let buffer = @buffer.new()
for i = 0; canonname[i] != 0; i = i + 1 {
buffer.write_byte(canonname[i])
}
let decoder = @encoding.decoder(UTF8)
Some(decoder.lossy_consume(buffer.contents()))
}
match yield_({ family, socktype, protocol, addr, canonname }) {
IterEnd => true
IterContinue => false
}
}) {
return IterEnd
} else {
return IterContinue
}
})
}
///|
pub fn[H : ToCString, S : ToCString, TDO : ToDomain, ToT : ToType, ToP : ToProtocol] getaddrinfo(
hostname : H,
service : S,
ai_flags : Int,
ai_family : TDO,
ai_socktype : ToT,
ai_protocol : ToP
) -> AddrInfoResults raise NetError {
let service = service.to_cstring()
let addr_info = getaddrinfo_make_ffi(
hostname.to_cstring(),
service,
ai_flags,
ai_family.to_domain().inner(),
ai_socktype.to_type().inner(),
ai_protocol.to_protocol().inner(),
)
if addr_info.is_null() {
raise NetError("Failed to get address info")
}
return addr_info
}