hare

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

commit 2f3b9aa82299f68037b6223b588f752e94ae8e8b
parent 40b07de56edb98d655097d8a82e76bfb8965d5c0
Author: Egor <egor@opensrc.club>
Date:   Tue, 10 May 2022 20:03:52 +0300

net: add sockflags to connect/accept/socketpair

Signed-off-by: Egor <egor@opensrc.club>

Diffstat:
Mnet/tcp/+freebsd.ha | 31++++++++++++++++++++++++++-----
Mnet/tcp/+linux.ha | 31++++++++++++++++++++++++++-----
Mnet/tcp/options.ha | 6++++--
Mnet/udp/+freebsd.ha | 29+++++++++++++++++++++++++----
Mnet/udp/+linux.ha | 29+++++++++++++++++++++++++----
Mnet/udp/options.ha | 6+++++-
Mnet/unix/+freebsd.ha | 31++++++++++++++++++++++++++-----
Mnet/unix/+linux.ha | 31++++++++++++++++++++++++++-----
Mnet/unix/options.ha | 6+++++-
Mnet/unix/socketpair.ha | 10++++++++--
10 files changed, 176 insertions(+), 34 deletions(-)

diff --git a/net/tcp/+freebsd.ha b/net/tcp/+freebsd.ha @@ -22,7 +22,16 @@ export fn connect( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -30,8 +39,11 @@ export fn connect( }; for (let i = 0z; i < len(options); i += 1) { - // The only option is keepalive right now - setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; + 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)) { @@ -55,7 +67,16 @@ export fn listen( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -73,7 +94,7 @@ export fn listen( setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; case let b: backlog => bk = b; - case let p: portassignment => void; + case => void; }; }; diff --git a/net/tcp/+linux.ha b/net/tcp/+linux.ha @@ -22,7 +22,16 @@ export fn connect( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -30,8 +39,11 @@ export fn connect( }; for (let i = 0z; i < len(options); i += 1) { - // The only option is keepalive right now - setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; + match (options[i]) { + case keepalive => + setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; + case => void; + }; }; const sz = size(rt::sockaddr): u32; match (rt::connect(sockfd, &sockaddr, sz)) { @@ -55,7 +67,16 @@ export fn listen( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -73,7 +94,7 @@ export fn listen( setsockopt(sockfd, rt::SO_KEEPALIVE, true)?; case let b: backlog => bk = b; - case let p: portassignment => void; + case => void; }; }; diff --git a/net/tcp/options.ha b/net/tcp/options.ha @@ -1,5 +1,6 @@ // License: MPL-2.0 // (c) 2021 Drew DeVault <sir@cmpwn.com> +use net; // Enables keep-alive for a socket. export type keepalive = void; @@ -20,7 +21,7 @@ export type reuseaddr = void; export type portassignment = *u16; // Options for [[connect]]. -export type connect_option = keepalive; +export type connect_option = (keepalive | net::sockflags); // Options for [[listen]]. export type listen_option = ( @@ -28,4 +29,5 @@ export type listen_option = ( reuseport | reuseaddr | backlog | - portassignment); + portassignment | + net::sockflags); diff --git a/net/udp/+freebsd.ha b/net/udp/+freebsd.ha @@ -12,6 +12,7 @@ use rt; export fn connect( dest: ip::addr, port: u16, + options: connect_option... ) (io::file | net::error) = { const family = match (dest) { case ip::addr4 => @@ -19,7 +20,13 @@ export fn connect( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + // Only sockflags for now + f |= options[i]; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -48,7 +55,16 @@ export fn listen( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -64,7 +80,12 @@ export fn listen( }; for (let i = 0z; i < len(options); i += 1) { - // The only option is portassignment right now + 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)) { @@ -73,7 +94,7 @@ export fn listen( case int => void; }; const addr = ip::from_native(sn); - *options[i] = addr.1; + *portout = addr.1; }; return io::fdopen(sockfd); diff --git a/net/udp/+linux.ha b/net/udp/+linux.ha @@ -12,6 +12,7 @@ use rt; export fn connect( dest: ip::addr, port: u16, + options: connect_option... ) (io::file | net::error) = { const family = match (dest) { case ip::addr4 => @@ -19,7 +20,13 @@ export fn connect( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + // Only sockflags for now + f |= options[i]; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -48,7 +55,16 @@ export fn listen( case ip::addr6 => yield rt::AF_INET6: int; }; - const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -64,7 +80,12 @@ export fn listen( }; for (let i = 0z; i < len(options); i += 1) { - // The only option is portassignment right now + 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)) { @@ -73,7 +94,7 @@ export fn listen( case int => void; }; const addr = ip::from_native(sn); - *options[i] = addr.1; + *portout = addr.1; }; return io::fdopen(sockfd); diff --git a/net/udp/options.ha b/net/udp/options.ha @@ -1,12 +1,16 @@ // License: MPL-2.0 // (c) 2021 Drew DeVault <sir@cmpwn.com> +use net; // To have the system select an arbitrary unused port for [[listen]], set port to // zero. To retrieve the assigned port, provide this as one of the options and // the addressed u16 will be filled in with the port. export type portassignment = *u16; +// Options for [[connect]]. +export type connect_option = net::sockflags; + // Options available for [[listen]]. -export type listen_option = portassignment; +export type listen_option = (portassignment | net::sockflags); // TODO: Add send/recv flags diff --git a/net/unix/+freebsd.ha b/net/unix/+freebsd.ha @@ -12,14 +12,23 @@ use types; // Opens a UNIX socket connection to the path. Blocks until the connection is // established. -export fn connect(addr: addr) (io::file | net::error) = { +export fn connect( + addr: addr, + options: connect_option... +) (io::file | net::error) = { let sockaddr = match (to_native(addr)) { case let a: rt::sockaddr => yield a; case invalid => return errors::unsupported; // path too long }; - const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + // Only sockflags for now + f |= options[i]; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -47,7 +56,16 @@ export fn listen( case invalid => return errors::unsupported; // path too long }; - const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -56,8 +74,11 @@ export fn listen( let bk: u32 = 10; for (let i = 0z; i < len(options); i += 1) { - // Only option is backlog right now - bk = options[i]; + match (options[i]) { + case let b: backlog => + bk = b; + case => void; + }; }; match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr_un): u32)) { diff --git a/net/unix/+linux.ha b/net/unix/+linux.ha @@ -13,14 +13,23 @@ use types; // Opens a UNIX socket connection to the path. Blocks until the connection is // established. -export fn connect(addr: addr) (io::file | net::error) = { +export fn connect( + addr: addr, + options: connect_option... +) (io::file | net::error) = { let sockaddr = match (to_native(addr)) { case let a: rt::sockaddr => yield a; case invalid => return errors::unsupported; // path too long }; - const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + // Only sockflags for now + f |= options[i]; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -48,7 +57,16 @@ export fn listen( case invalid => return errors::unsupported; // path too long }; - const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | rt::SOCK_CLOEXEC, 0)) { + let f = 0i; + for (let i = 0z; i < len(options); i += 1) { + match (options[i]) { + case let fl: net::sockflags => + f |= fl; + case => void; + }; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | f, 0)) { case let err: rt::errno => return errors::errno(err); case let fd: int => @@ -57,8 +75,11 @@ export fn listen( let bk: u32 = 10; for (let i = 0z; i < len(options); i += 1) { - // Only option is backlog right now - bk = options[i]; + match (options[i]) { + case let b: backlog => + bk = b; + case => void; + }; }; match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr_un): u32)) { diff --git a/net/unix/options.ha b/net/unix/options.ha @@ -1,9 +1,13 @@ // License: MPL-2.0 // (c) 2021 Drew DeVault <sir@cmpwn.com> +use net; // Configures the backlog size for a listener. If not specified, a sensible // default (10) is used. export type backlog = u32; +// Options for [[connect]]. +export type connect_option = net::sockflags; + // Options for [[listen]]. -export type listen_option = backlog; +export type listen_option = (backlog | net::sockflags); diff --git a/net/unix/socketpair.ha b/net/unix/socketpair.ha @@ -9,9 +9,15 @@ use io; // A thin wrapper around socketpair(2) that presumes [[rt::AF_UNIX]] for the // domain and returns an unnamed pair of sockets of type [[rt::SOCK_STREAM]]. -export fn socketpair() ((io::file, io::file) | net::error) = { +export fn socketpair(flags: net::sockflags...) ((io::file, io::file) | net::error) = { let sv: [2]int = [0...]; - match (rt::socketpair(rt::AF_UNIX : int, (rt::SOCK_STREAM | rt::SOCK_CLOEXEC) : int, 0, &sv)) { + // Apply any supplied flags + let f = 0i; + for (let i = 0z; i < len(flags); i += 1) { + f |= flags[i]; + }; + f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC + match (rt::socketpair(rt::AF_UNIX : int, (rt::SOCK_STREAM | f) : int, 0, &sv)) { case let err: rt::errno => return errors::errno(err); case =>