Files
c3c/lib/std/net/socket.c3
Christoffer Lerno 9a6d83f526 Updated stream API.
2023-09-03 01:14:15 +02:00

97 lines
2.9 KiB
C

module std::net @if(os::SUPPORTS_INET);
import std::io;
import libc;
struct Socket
{
inline Stream stream;
NativeSocket sock;
Socklen_t ai_addrlen;
// TODO proper way to get the size of sockaddr_storage
// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms740504(v=vs.85)
char[128] ai_addr_storage;
}
macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
{
while (ai)
{
NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
if (sockfd > 0)
{
@body(sockfd, ai);
}
ai = ai.ai_next;
}
}
macro Socket new_socket(fd, ai)
{
Socket sock = { .stream.fns = &SOCKETSTREAM_INTERFACE, .sock = fd, .ai_addrlen = ai.ai_addrlen };
assert(sock.ai_addr_storage.len >= ai.ai_addrlen, "storage %d < addrlen %d", sock.ai_addr_storage.len, ai.ai_addrlen);
mem::copy(&sock.ai_addr_storage, (void*)ai.ai_addr, ai.ai_addrlen);
return sock;
}
enum SocketOption : char (CInt value)
{
REUSEADDR (os::SO_REUSEADDR),
REUSEPORT (os::SO_REUSEPORT) @if(!env::WIN32),
KEEPALIVE (os::SO_KEEPALIVE),
BROADCAST (os::SO_BROADCAST),
OOBINLINE (os::SO_OOBINLINE),
DONTROUTE (os::SO_DONTROUTE),
}
const StreamInterface SOCKETSTREAM_INTERFACE = {
.read_fn = (ReadStreamFn)&Socket.read,
.write_fn = (WriteStreamFn)&Socket.write,
.close_fn = (CloseStreamFn)&Socket.close,
};
fn bool! Socket.get_broadcast(&self) => self.get_option(BROADCAST);
fn bool! Socket.get_keepalive(&self) => self.get_option(KEEPALIVE);
fn bool! Socket.get_reuseaddr(&self) => self.get_option(REUSEADDR);
fn bool! Socket.get_dontroute(&self) => self.get_option(DONTROUTE);
fn bool! Socket.get_oobinline(&self) => self.get_option(OOBINLINE);
fn void! Socket.set_broadcast(&self, bool value) => self.set_option(BROADCAST, value);
fn void! Socket.set_keepalive(&self, bool value) => self.set_option(KEEPALIVE, value);
fn void! Socket.set_reuseaddr(&self, bool value) => self.set_option(REUSEADDR, value);
fn void! Socket.set_dontroute(&self, bool value) => self.set_option(DONTROUTE, value);
fn void! Socket.set_oobinline(&self, bool value) => self.set_option(OOBINLINE, value);
fn void! Socket.set_option(&self, SocketOption option, bool value)
{
CInt flag = (CInt)value;
int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof);
if (errcode != 0) return NetError.SOCKOPT_FAILED?;
}
fn bool! Socket.get_option(&self, SocketOption option)
{
CInt flag;
int errcode = os::setsockopt(self.sock, os::SOL_SOCKET, option.value, &flag, CInt.sizeof);
if (errcode != 0) return NetError.SOCKOPT_FAILED?;
return (bool)flag;
}
fn usz! Socket.read(&self, char[] bytes)
{
isz n = libc::read((Fd)self.sock, bytes.ptr, bytes.len);
if (n < 0) return NetError.READ_FAILED?;
return (usz)n;
}
fn usz! Socket.write(&self, char[] bytes)
{
isz n = libc::write((Fd)self.sock, bytes.ptr, bytes.len);
if (n < 0) return NetError.WRITE_FAILED?;
return (usz)n;
}
fn void! Socket.close(&self) @inline
{
self.sock.close()!;
}