///|
pub(all) enum AddrFamily {
// Only IPv4 is supported for now
AF_INET
// AF_INET6 = 10
}
///|
fn AddrFamily::from_u16(value : UInt16) -> AddrFamily!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
}
///|
fn SocketType::from_u16(value : UInt16) -> SocketType!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
}
///|
fn IpProtocol::from_u16(value : UInt16) -> IpProtocol!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!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!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!Failure {
let protocol = if protocol.is_empty() {
match type_ {
SocketType::SOCK_STREAM => IpProtocol::IPPROTO_TCP
SocketType::SOCK_DGRAM => IpProtocol::IPPROTO_UDP
}
} else {
protocol.unwrap()
}
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!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!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!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!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!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!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)!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!Failure
}
///|
pub impl Sendable for FixedArray[Byte] with send(
self : FixedArray[Byte],
socket : Socket,
flags : Int
) -> Int64!Failure {
@internal._ffi_send_byte_array(socket.inner, self, flags)
}
///|
pub impl Sendable for Bytes with send(
self : Bytes,
socket : Socket,
flags : Int
) -> Int64!Failure {
@internal._ffi_send_bytes(socket.inner, self, flags)
}
///|
pub fn Socket::send[Sendable : Sendable](
self : Socket,
sendable : Sendable,
flags~ : Int = 0
) -> Int64!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!Failure
}
///|
pub impl SendToAble for FixedArray[Byte] with send_to(
self : FixedArray[Byte],
socket : Socket,
flags : Int,
addr : SockaddrIn
) -> Int64!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!Failure {
@internal._ffi_sendto_bytes(socket.inner, self, flags, addr.inner)
}
///|
pub fn Socket::send_to[SendToAble : SendToAble](
self : Socket,
sendable : SendToAble,
addr : SockaddrIn,
flags~ : Int = 0x800 // MSG_CONFIRM
) -> Int64!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!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]!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)!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)!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!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!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!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!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}")
}
}