///|
pub(all) enum AddrFamily {
// Only IPv4 is supported for now
AF_INET
// AF_INET6 = 10
}
///|
fn AddrFamily::from_u16(value : UInt16) -> AddrFamily raise Failure {
match value {
2 => AddrFamily::AF_INET
// 10 => AddrFamily::AF_INET6
_ => fail("Unsupported address family: \{value}")
}
}
///|
fn AddrFamily::to_u16(self : AddrFamily) -> UInt16 {
match self {
AddrFamily::AF_INET => 2
// AddrFamily::AF_INET6 => 10
}
}
///|
pub(all) enum SocketType {
SOCK_STREAM
SOCK_DGRAM
// other types are not supported
}
///|
/// TODO: comment this code to ignore warning checking
// fn SocketType::from_u16(value : UInt16) -> SocketType raise Failure {
// match value {
// 1 => SocketType::SOCK_STREAM
// 2 => SocketType::SOCK_DGRAM
// _ => fail("Unsupported socket type: \{value}")
// }
// }
///|
fn SocketType::to_u16(self : SocketType) -> UInt16 {
match self {
SocketType::SOCK_STREAM => 1
SocketType::SOCK_DGRAM => 2
}
}
///|
pub(all) enum IpProtocol {
IPPROTO_IP
IPPROTO_TCP
IPPROTO_UDP
// other protocols are not supported
}
///|
// TODO: comment this code to ignore warning checking
// fn IpProtocol::from_u16(value : UInt16) -> IpProtocol raise Failure {
// match value {
// 0 => IpProtocol::IPPROTO_IP
// 6 => IpProtocol::IPPROTO_TCP
// 17 => IpProtocol::IPPROTO_UDP
// _ => fail("Unsupported IP protocol: \{value}")
// }
// }
///|
fn IpProtocol::to_u16(self : IpProtocol) -> UInt16 {
match self {
IpProtocol::IPPROTO_IP => 0
IpProtocol::IPPROTO_TCP => 6
IpProtocol::IPPROTO_UDP => 17
}
}
// TODO: Protocol families
///|
struct Hostent {
inner : @internal.FFI_hostent
}
///|
pub fn Hostent::get_addr_str(self : Hostent) -> String raise Failure {
let addr = @internal._ffi_hostent_get_addr_str(self.inner)
@ctypes.CString::from_bytes(addr).to_string()
}
///|
pub fn get_host_by_name(hostname : String) -> Hostent raise Failure {
let cstr = @ctypes.CString::from_string(hostname)
let hostent = @internal._ffi_gethostbyname(cstr)
if hostent.is_null() {
fail("gethostbyname failed, hostent is null")
}
Hostent::{ inner: hostent }
}
///|
pub fn Hostent::get_addr(self : Hostent) -> UInt {
@internal._ffi_hostent_get_addr(self.inner)
}
///|
struct Socket {
domain : AddrFamily
inner : Int
}
///|
pub fn new_socket(
domain : AddrFamily,
type_ : SocketType,
protocol~ : IpProtocol? = None
) -> Socket raise Failure {
let protocol = match (protocol, type_) {
(Some(p), _) => p
(None, SocketType::SOCK_STREAM) => IpProtocol::IPPROTO_TCP
(None, SocketType::SOCK_DGRAM) => IpProtocol::IPPROTO_UDP
}
let fd = @internal._ffi_socket(
domain.to_u16().to_int(),
type_.to_u16().to_int(),
protocol.to_u16().to_int(),
)
if fd < 0 {
if fd == -1 {
let err = @internal._ffi_get_errno()
let err_str = @internal._ffi_stderror(err)
fail("socket failed: \{err_str}")
}
fail("socket failed: \{fd}")
}
Socket::{ domain, inner: fd }
}
///|
pub fn Socket::close(self : Socket) -> Unit raise Failure {
let result = @internal._ffi_close(self.inner)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"close failed: \{err}"
} else {
"close failed: \{result}"
}
fail(err_str)
}
}
///|
pub fn Socket::connect_addr(
self : Socket,
addr : SockaddrIn
) -> Unit raise Failure {
let result = @internal._ffi_connect(self.inner, addr.inner)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"connect failed: \{err}"
} else {
"connect failed: \{result}"
}
self.close()
fail(err_str)
}
}
///|
pub fn Socket::connect(
self : Socket,
addr : Hostent,
port : UInt16
) -> SockaddrIn raise Failure {
let sockaddr_in = new_sockaddr_in()
sockaddr_in
..set_family(self.domain)
..set_addr(addr.get_addr())
..set_port(port)
self.connect_addr(sockaddr_in)
sockaddr_in
}
///|
pub fn Socket::bind_addr(
self : Socket,
addr : SockaddrIn
) -> Unit raise Failure {
let result = @internal._ffi_bind(self.inner, addr.inner)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"bind failed: \{err}"
} else {
"bind failed: \{result}"
}
self.close()
fail(err_str)
}
}
///|
pub fn Socket::bind(
self : Socket,
addr : String,
port : UInt16
) -> SockaddrIn raise Failure {
let sockaddr_in = new_sockaddr_in()
sockaddr_in..set_family(self.domain)..set_addr_str(addr)..set_port(port)
self.bind_addr(sockaddr_in)
sockaddr_in
}
///|
pub fn Socket::listen(self : Socket, backlog : Int) -> Unit raise Failure {
let result = @internal._ffi_listen(self.inner, backlog)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"listen failed: \{err}"
} else {
"listen failed: \{result}"
}
self.close()
fail(err_str)
}
}
///|
pub fn Socket::accept(self : Socket) -> (Socket, SockaddrIn) raise Failure {
let addr = new_sockaddr_in()
let fd = @internal._ffi_accept(self.inner, addr.inner)
if fd < 0 {
let err_str = if fd == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"accept failed: \{err}"
} else {
"accept failed: \{fd}"
}
self.close()
fail(err_str)
}
(Socket::{ domain: self.domain, inner: fd }, addr)
}
///|
trait Sendable {
send(Self, socket : Socket, flags : Int) -> Int64 raise Failure
}
///|
pub impl Sendable for FixedArray[Byte] with send(
self : FixedArray[Byte],
socket : Socket,
flags : Int
) -> Int64 raise Failure {
@internal._ffi_send_byte_array(socket.inner, self, flags)
}
///|
pub impl Sendable for Bytes with send(
self : Bytes,
socket : Socket,
flags : Int
) -> Int64 raise Failure {
@internal._ffi_send_bytes(socket.inner, self, flags)
}
///|
pub fn[Sendable : Sendable] Socket::send(
self : Socket,
sendable : Sendable,
flags~ : Int = 0
) -> Int64 raise Failure {
let result = sendable.send(self, flags)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"send failed: \{err}"
} else {
"send failed: \{result}"
}
self.close()
fail(err_str)
}
result
}
///|
trait SendToAble {
send_to(Self, socket : Socket, flags : Int, addr : SockaddrIn) -> Int64 raise Failure
}
///|
pub impl SendToAble for FixedArray[Byte] with send_to(
self : FixedArray[Byte],
socket : Socket,
flags : Int,
addr : SockaddrIn
) -> Int64 raise Failure {
@internal._ffi_sendto_byte_array(socket.inner, self, flags, addr.inner)
}
///|
pub impl SendToAble for Bytes with send_to(
self : Bytes,
socket : Socket,
flags : Int,
addr : SockaddrIn
) -> Int64 raise Failure {
@internal._ffi_sendto_bytes(socket.inner, self, flags, addr.inner)
}
///|
pub fn[S : SendToAble] Socket::send_to(
self : Socket,
sendable : S,
addr : SockaddrIn,
flags~ : Int = 0x800 // MSG_CONFIRM
) -> Int64 raise Failure {
let result = sendable.send_to(self, flags, addr)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"sendto failed: \{err}"
} else {
"sendto failed: \{result}"
}
self.close()
fail(err_str)
}
result
}
///|
pub fn Socket::recv_buffer(
self : Socket,
buf : FixedArray[Byte],
flags~ : Int = 0
) -> Int64 raise Failure {
let result = @internal._ffi_recv(self.inner, buf, flags)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"recv failed: \{err}"
} else {
"recv failed: \{result}"
}
self.close()
fail(err_str)
}
result
}
///|
pub fn Socket::recv(
self : Socket,
buf_size : Int
) -> FixedArray[Byte] raise Failure {
guard buf_size > 0 else { fail("recv: buf_size must be greater than 0") }
let mut buf : FixedArray[Byte] = FixedArray::make(buf_size, (0).to_byte())
let len = self.recv_buffer(buf)
if len.to_int() != buf_size {
buf = FixedArray::makei(len.to_int(), fn(i) { buf[i] })
}
buf
}
///|
pub fn Socket::recv_from_buffer(
self : Socket,
buf : FixedArray[Byte],
flags~ : Int = 0x100 // MSG_WAITALL
) -> (Int64, SockaddrIn) raise Failure {
let addr = new_sockaddr_in()
let result = @internal._ffi_recvfrom(self.inner, buf, flags, addr.inner)
if result < 0 {
let err_str = if result == -1 {
let err = @internal._ffi_get_errno()
let err = @internal._ffi_stderror(err)
"recvfrom failed: \{err}"
} else {
"recvfrom failed: \{result}"
}
self.close()
fail(err_str)
}
(result, addr)
}
///|
pub fn Socket::recv_from(
self : Socket,
buf_size : Int
) -> (FixedArray[Byte], SockaddrIn) raise Failure {
guard buf_size > 0 else { fail("recv_from: buf_size must be greater than 0") }
let mut buf : FixedArray[Byte] = FixedArray::make(buf_size, 0)
let (len, addr) = self.recv_from_buffer(buf)
if len.to_int() != buf_size {
buf = FixedArray::makei(len.to_int(), fn(i) { buf[i] })
}
(buf, addr)
}
///|
struct SockaddrIn {
inner : @internal.FFI_sockaddr_in
}
///|
pub fn new_sockaddr_in() -> SockaddrIn raise Failure {
let sockaddr = @internal._ffi_new_sockaddr_in()
if sockaddr.is_null() {
fail("build_sockaddr_in failed, sockaddr is null")
}
SockaddrIn::{ inner: sockaddr }
}
///|
pub fn SockaddrIn::get_family(self : SockaddrIn) -> AddrFamily raise Failure {
AddrFamily::from_u16(@internal._ffi_sockaddr_in_get_family(self.inner))
}
///|
pub fn SockaddrIn::set_family(self : SockaddrIn, family : AddrFamily) -> Unit {
@internal._ffi_sockaddr_in_set_family(self.inner, family.to_u16())
}
///|
pub fn SockaddrIn::get_addr(self : SockaddrIn) -> UInt {
@internal._ffi_sockaddr_in_get_addr(self.inner)
}
///|
pub fn SockaddrIn::set_addr(self : SockaddrIn, addr : UInt) -> Unit {
@internal._ffi_sockaddr_in_set_addr(self.inner, addr)
}
///|
pub fn SockaddrIn::get_port(self : SockaddrIn) -> UInt16 {
@internal._ffi_sockaddr_in_get_port(self.inner)
}
///|
pub fn SockaddrIn::set_port(self : SockaddrIn, port : UInt16) -> Unit {
@internal._ffi_sockaddr_in_set_port(self.inner, port)
}
///|
pub fn SockaddrIn::get_addr_str(self : SockaddrIn) -> String raise Failure {
let addr = @internal._ffi_sockaddr_in_get_addr_str(self.inner)
addr.to_string()
}
///|
pub fn SockaddrIn::set_addr_str(
self : SockaddrIn,
addr : String
) -> Unit raise Failure {
let cstr = @ctypes.CString::from_string(addr)
let retval = @internal._ffi_sockaddr_in_set_addr_str(self.inner, cstr)
if retval != 0 {
fail("set_addr_str failed: \{retval}")
}
}