+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 };