commit 80dae68c0b0e2d1cb98f9f69e41d61e2f8630771
parent c1e639c21462f722c445066987da362b0abab7e1
Author: Mykyta Holubakha <hilobakho@gmail.com>
Date: Sat, 3 Apr 2021 00:49:42 +0300
net: add high-level UNIX socket interface
Diffstat:
2 files changed, 108 insertions(+), 47 deletions(-)
diff --git a/net/+linux/socket.ha b/net/+linux/socket.ha
@@ -1,51 +1,60 @@
use io;
use net::ip;
+use net::unix;
use os;
use rt;
use strings;
-// Opens a TCP connection to the given host and port. Blocks until the
-// connection is established.
-export fn connect(
- addr: ip::addr,
- port: u16,
+fn connect_fd(
+ addr: rt::sockaddr,
options: connect_option...
-) (*io::stream | io::error) = {
+) (int | io::error) = {
const sockfd = mksockfd(addr)?;
for (let i = 0z; i < len(options); i += 1) {
// The only option is keepalive right now
setsockopt(sockfd, rt::SO_KEEPALIVE, true);
};
+ wrap(rt::connect(sockfd, &addr, sockasz(addr)))?;
+ return sockfd;
+};
+// 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...
+) (*io::stream | io::error) = {
const sockaddr = ip::to_native(addr, port);
- const asize = match (addr) {
- v4: ip::addr4 => size(rt::sockaddr_in): u32,
- v6: ip::addr6 => size(rt::sockaddr_in6): u32,
- };
- wrap(rt::connect(sockfd, &sockaddr, asize))?;
-
- return os::fdopen(sockfd, ip::string(addr),
+ const sockfd = connect_fd(sockaddr, options...)?;
+ return os::fdopen(sockfd, string(addr),
io::mode::READ | io::mode::WRITE);
};
+// Opens a UNIX socket connection to the path. Blocks until the
+// connection is established.
export fn connect_unix(
- path: str,
+ addr: unix::addr,
options: connect_option...
) (*io::stream | io::error) = {
- abort(); // TODO
+ let sockaddr = match (unix::to_native(addr)) {
+ a: rt::sockaddr => a,
+ unix::invalid => return io::unsupported, // path too long
+ };
+ const sockfd = connect_fd(sockaddr, options...)?;
+ return os::fdopen(sockfd, string(addr),
+ io::mode::READ | io::mode::WRITE);
};
-type tcp_listener = struct {
+type stream_listener = struct {
l: listener,
fd: int,
};
-// Binds a TCP listener to the given address.
-export fn listen(
- addr: ip::addr,
- port: u16,
+fn listen_fd(
+ addr: rt::sockaddr,
options: listen_option...
-) (*listener | io::error) = {
+) (int | io::error) = {
const sockfd = mksockfd(addr)?;
let bk: u32 = 10;
@@ -62,55 +71,77 @@ export fn listen(
};
setfcntl(sockfd, rt::O_CLOEXEC);
- let sockaddr = ip::to_native(addr, port);
- const asize = match (addr) {
- v4: ip::addr4 => size(rt::sockaddr_in): u32,
- v6: ip::addr6 => size(rt::sockaddr_in6): u32,
- };
- wrap(rt::bind(sockfd, &sockaddr, asize))?;
+ wrap(rt::bind(sockfd, &addr, sockasz(addr)))?;
wrap(rt::listen(sockfd, bk))?;
match (portout) {
p: *u16 => {
+ if (addr.in.sin_family != rt::AF_INET &&
+ addr.in.sin_family != rt::AF_INET6) {
+ return io::unsupported;
+ };
let sn = rt::sockaddr {...};
- let al = asize;
+ let al = sockasz(addr);
wrap(rt::getsockname(sockfd, &sn, &al))?;
const addr = ip::from_native(sn);
*p = addr.1;
},
null => void,
};
- return alloc(tcp_listener {
+
+ return sockfd;
+};
+
+// Binds a TCP listener to the given address.
+export fn listen(
+ addr: ip::addr,
+ port: u16,
+ options: listen_option...
+) (*listener | io::error) = {
+ let sockaddr = ip::to_native(addr, port);
+ let sockfd = listen_fd(sockaddr, options...)?;
+ return alloc(stream_listener {
l = listener {
- accept = &tcp_accept,
- shutdown = &tcp_shutdown,
+ accept = &stream_accept,
+ shutdown = &stream_shutdown,
},
fd = sockfd,
}): *listener;
};
-fn tcp_accept(l: *listener) (*io::stream | io::error) = {
- assert(l.accept == &tcp_accept);
- let l = l: *tcp_listener;
+fn stream_accept(l: *listener) (*io::stream | io::error) = {
+ assert(l.accept == &stream_accept);
+ let l = l: *stream_listener;
let sn = rt::sockaddr {...};
const sz = size(rt::sockaddr): u32;
const fd = wrap(rt::accept(l.fd, &sn, &sz))?;
- const addr = ip::from_native(sn).0;
- return os::fdopen(fd, ip::string(addr),
+ const addr = from_native(sn);
+ return os::fdopen(fd, string(addr),
io::mode::READ | io::mode::WRITE);
};
-fn tcp_shutdown(l: *listener) void = {
- assert(l.shutdown == &tcp_shutdown);
- let l = l: *tcp_listener;
+fn stream_shutdown(l: *listener) void = {
+ assert(l.shutdown == &stream_shutdown);
+ let l = l: *stream_listener;
rt::close(l.fd);
free(l);
};
-// Binds a Unix socket at the given path.
+// Binds a UNIX socket listener at the given path.
export fn listen_unix(
- path: str,
+ addr: unix::addr,
options: listen_option...
) (*listener | io::error) = {
- abort(); // TODO
+ let sockaddr = match (unix::to_native(addr)) {
+ a: rt::sockaddr => a,
+ unix::invalid => return io::unsupported, // path too long
+ };
+ let sockfd = listen_fd(sockaddr, options...)?;
+ return alloc(stream_listener {
+ l = listener {
+ accept = &stream_accept,
+ shutdown = &stream_shutdown,
+ },
+ fd = sockfd,
+ }): *listener;
};
diff --git a/net/+linux/util.ha b/net/+linux/util.ha
@@ -1,5 +1,7 @@
use io;
+use fmt;
use net::ip;
+use net::unix;
use os;
use rt;
@@ -35,12 +37,33 @@ fn wrap(ie: (int | rt::errno)) (int | io::error) = {
};
};
-fn mksockfd(addr: ip::addr) (int | io::error) = {
- const af = match (addr) {
- ip::addr4 => rt::AF_INET: int,
- ip::addr6 => rt::AF_INET6: int,
+fn mksockfd(addr: rt::sockaddr) (int | io::error) = {
+ return wrap(rt::socket(addr.in.sin_family: int, rt::SOCK_STREAM, 0))?;
+};
+
+fn sockasz(addr: rt::sockaddr) u32 = {
+ return switch (addr.in.sin_family) {
+ rt::AF_INET => size(rt::sockaddr_in): u32,
+ rt::AF_INET6 => size(rt::sockaddr_in6): u32,
+ rt::AF_UNIX => size(rt::sockaddr_un): u32,
+ * => size(rt::sockaddr): u32,
+ };
+};
+
+fn string(addr: (ip::addr | unix::addr)) str = {
+ return match (addr) {
+ ip: ip::addr => ip::string(ip),
+ un: unix::addr => un,
+ };
+};
+
+fn from_native(addr: rt::sockaddr) (ip::addr | unix::addr) = {
+ return switch (addr.in.sin_family) {
+ rt::AF_INET => ip::from_native(addr).0,
+ rt::AF_INET6 => ip::from_native(addr).0,
+ rt::AF_UNIX => unix::from_native(addr),
+ * => abort("Wrong address family"),
};
- return wrap(rt::socket(af, rt::SOCK_STREAM, 0))?;
};
// Returns the remote address for a given connection, or void if none is
@@ -57,3 +80,10 @@ export fn peeraddr(stream: *io::stream) ((ip::addr, u16) | void) = {
};
return ip::from_native(sn);
};
+
+// Gets the fd of the listener's socket.
+export fn listenerfd(l: *listener) (int | void) = {
+ if (l.accept == &stream_accept) {
+ return (l: *stream_listener).fd;
+ };
+};