1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use errors;
use io;
use net;
use net::ip;
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 f = 0i;
for (let i = 0z; i < len(options); i += 1) {
match (options[i]) {
case let fl: net::sockflag =>
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 =>
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 = size(rt::sockaddr): u32;
match (rt::connect(sockfd, &sockaddr, sz)) {
case let err: rt::errno =>
if (err != rt::EINPROGRESS) {
return errors::errno(err);
};
assert(f & rt::SOCK_NONBLOCK == rt::SOCK_NONBLOCK);
case void => 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 f = 0i;
for (let i = 0z; i < len(options); i += 1) {
match (options[i]) {
case let fl: net::sockflag =>
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 =>
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;
};
};
match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr): u32)) {
case let err: rt::errno =>
return errors::errno(err);
case void => 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;
};
};
|