hare

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

+linux.ha (3658B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use errors;
      5 use io;
      6 use net;
      7 use net::ip;
      8 use rt;
      9 
     10 // Opens a TCP connection to the given host and port. Blocks until the
     11 // connection is established.
     12 export fn connect(
     13 	addr: ip::addr,
     14 	port: u16,
     15 	options: connect_option...
     16 ) (net::socket | net::error) = {
     17 	const sockaddr = ip::to_native(addr, port);
     18 	const family = match (addr) {
     19 	case ip::addr4 =>
     20 		yield rt::AF_INET: int;
     21 	case ip::addr6 =>
     22 		yield rt::AF_INET6: int;
     23 	};
     24 	let f = 0i;
     25 	for (let i = 0z; i < len(options); i += 1) {
     26 		match (options[i]) {
     27 		case let fl: net::sockflag =>
     28 			f |= fl;
     29 		case => void;
     30 		};
     31 	};
     32 	f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
     33 	const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) {
     34 	case let err: rt::errno =>
     35 		return errors::errno(err);
     36 	case let fd: int =>
     37 		yield fd;
     38 	};
     39 
     40 	for (let i = 0z; i < len(options); i += 1) {
     41 		match (options[i]) {
     42 		case keepalive =>
     43 			setsockopt(sockfd, rt::SO_KEEPALIVE, true)?;
     44 		case => void;
     45 		};
     46 	};
     47 	const sz = size(rt::sockaddr): u32;
     48 	match (rt::connect(sockfd, &sockaddr, sz)) {
     49 	case let err: rt::errno =>
     50 		if (err != rt::EINPROGRESS) {
     51 			return errors::errno(err);
     52 		};
     53 		assert(f & rt::SOCK_NONBLOCK == rt::SOCK_NONBLOCK);
     54 	case int => void;
     55 	};
     56 	return io::fdopen(sockfd);
     57 };
     58 
     59 // Binds a TCP socket to the given address.
     60 export fn listen(
     61 	addr: ip::addr,
     62 	port: u16,
     63 	options: listen_option...
     64 ) (net::socket | net::error) = {
     65 	const sockaddr = ip::to_native(addr, port);
     66 	const family = match (addr) {
     67 	case ip::addr4 =>
     68 		yield rt::AF_INET: int;
     69 	case ip::addr6 =>
     70 		yield rt::AF_INET6: int;
     71 	};
     72 	let f = 0i;
     73 	for (let i = 0z; i < len(options); i += 1) {
     74 		match (options[i]) {
     75 		case let fl: net::sockflag =>
     76 			f |= fl;
     77 		case => void;
     78 		};
     79 	};
     80 	f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
     81 	const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) {
     82 	case let err: rt::errno =>
     83 		return errors::errno(err);
     84 	case let fd: int =>
     85 		yield fd;
     86 	};
     87 
     88 	let bk: u32 = 10;
     89 	for (let i = 0z; i < len(options); i += 1) {
     90 		match (options[i]) {
     91 		case reuseaddr =>
     92 			setsockopt(sockfd, rt::SO_REUSEADDR, true)?;
     93 		case reuseport =>
     94 			setsockopt(sockfd, rt::SO_REUSEPORT, true)?;
     95 		case keepalive =>
     96 			setsockopt(sockfd, rt::SO_KEEPALIVE, true)?;
     97 		case let b: backlog =>
     98 			bk = b;
     99 		case => void;
    100 		};
    101 	};
    102 
    103 	match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr): u32)) {
    104 	case let err: rt::errno =>
    105 		return errors::errno(err);
    106 	case int => void;
    107 	};
    108 	match (rt::listen(sockfd, bk)) {
    109 	case let err: rt::errno =>
    110 		return errors::errno(err);
    111 	case int => void;
    112 	};
    113 
    114 	for (let i = 0z; i < len(options); i += 1) {
    115 		let portout = match (options[i]) {
    116 		case let p: portassignment =>
    117 			yield p;
    118 		case =>
    119 			continue;
    120 		};
    121 		let sn = rt::sockaddr {...};
    122 		let al = size(rt::sockaddr): u32;
    123 		match (rt::getsockname(sockfd, &sn, &al)) {
    124 		case let err: rt::errno =>
    125 			return errors::errno(err);
    126 		case int => void;
    127 		};
    128 		const addr = ip::from_native(sn);
    129 		*portout = addr.1;
    130 	};
    131 
    132 	return sockfd;
    133 };
    134 
    135 // Returns the remote address for a given connection, or void if none is
    136 // available.
    137 export fn peeraddr(peer: net::socket) ((ip::addr, u16) | void) = {
    138 	let sn = rt::sockaddr {...};
    139 	let sz = size(rt::sockaddr): u32;
    140 	if (rt::getpeername(peer, &sn, &sz) is rt::errno) {
    141 		return;
    142 	};
    143 	return ip::from_native(sn);
    144 };
    145 
    146 fn setsockopt(
    147 	sockfd: int,
    148 	option: int,
    149 	value: bool,
    150 ) (void | net::error) = {
    151 	let val: int = if (value) 1 else 0;
    152 	match (rt::setsockopt(sockfd, rt::SOL_SOCKET, option,
    153 			&val: *opaque, size(int): u32)) {
    154 	case let err: rt::errno =>
    155 		return errors::errno(err);
    156 	case int => void;
    157 	};
    158 };