hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 67ca58a25156b5c32910296855846479f47f80b5
parent 280d4ad0a11d1f9cac9de4788dc14970b572c168
Author: Lorenz (xha) <me@xha.li>
Date:   Sat, 25 Nov 2023 15:18:23 +0100

OpenBSD: add net::tcp

Signed-off-by: Lorenz (xha) <me@xha.li>

Diffstat:
Anet/tcp/+openbsd.ha | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 159 insertions(+), 0 deletions(-)

diff --git a/net/tcp/+openbsd.ha b/net/tcp/+openbsd.ha @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use errors; +use io; +use net; +use net::ip; +use os; +use rt; + +// Opens a TCP connection to the given host and port. Blocks until the +// connection is established. +export fn connect( + addr: ip::addr, + port: u16, + options: connect_option... +) (net::socket | net::error) = { + const sockaddr = ip::to_native(addr, port); + const family = match (addr) { + case ip::addr4 => + yield rt::AF_INET: int; + case ip::addr6 => + yield rt::AF_INET6: int; + }; + let flags = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflag => + flags |= fl; + case => void; + }; + }; + flags ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_STREAM | flags, 0)) { + case let err: rt::errno => + return errors::errno(err); + case let fd: int => + yield fd; + }; + + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case keepalive => + setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; + case => void; + }; + }; + const sz = ip::native_addrlen(addr); + match (rt::connect(sockfd, &sockaddr, sz)) { + case let err: rt::errno => + if (err != rt::EINPROGRESS) { + return errors::errno(err); + }; + case int => void; + }; + return io::fdopen(sockfd); +}; + +// Binds a TCP socket to the given address. +export fn listen( + addr: ip::addr, + port: u16, + options: listen_option... +) (net::socket | net::error) = { + const sockaddr = ip::to_native(addr, port); + const family = match (addr) { + case ip::addr4 => + yield rt::AF_INET: int; + case ip::addr6 => + yield rt::AF_INET6: int; + }; + let flags = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflag => + flags |= fl; + case => void; + }; + }; + flags ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_STREAM | flags, 0)) { + case let err: rt::errno => + return errors::errno(err); + case let fd: int => + yield fd; + }; + + let bk: u32 = 10; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case reuseaddr => + setsockopt(sockfd, rt::SO_REUSEADDR, true)?; + case reuseport => + setsockopt(sockfd, rt::SO_REUSEPORT, true)?; + case keepalive => + setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; + case let b: backlog => + bk = b; + case => void; + }; + }; + + const sz = ip::native_addrlen(addr); + match (rt::bind(sockfd, &sockaddr, sz)) { + case let err: rt::errno => + return errors::errno(err); + case int => void; + }; + match (rt::listen(sockfd, bk)) { + case let err: rt::errno => + return errors::errno(err); + case int => void; + }; + + for (let i = 0z; i < len(options); i += 1) { + let portout = match (options[i]) { + case let p: portassignment => + yield p; + case => + continue; + }; + let sn = rt::sockaddr {...}; + let al = size(rt::sockaddr): u32; + match (rt::getsockname(sockfd, &sn, &al)) { + case let err: rt::errno => + return errors::errno(err); + case int => void; + }; + const addr = ip::from_native(sn); + *portout = addr.1; + }; + + return sockfd; +}; + +// Returns the remote address for a given connection, or void if none is +// available. +export fn peeraddr(peer: net::socket) ((ip::addr, u16) | void) = { + let sn = rt::sockaddr {...}; + let sz = size(rt::sockaddr): u32; + if (rt::getpeername(peer, &sn, &sz) is rt::errno) { + return; + }; + return ip::from_native(sn); +}; + +fn setsockopt( + sockfd: int, + option: int, + value: bool, +) (void | net::error) = { + let val: int = if (value) 1 else 0; + match (rt::setsockopt(sockfd, rt::SOL_SOCKET, option, + &val: *opaque, size(int): u32)) { + case let err: rt::errno => + return errors::errno(err); + case int => void; + }; +};