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