dial.ha (3356B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use fmt; 5 use net; 6 use net::ip; 7 use net::uri; 8 9 // Dials a remote address, establishing a connection and returning the resulting 10 // [[net::socket]]. The proto parameter should be the transport protocol (e.g. 11 // "tcp"), the address parameter should be the remote address, and the service 12 // should be the name of the service, or the default port to use. 13 // 14 // The interpretation of the address and service parameters is dependent on the 15 // protocol in use. For IP-based protocols (such as TCP or UDP), the address 16 // parameter may be either an IPv4 or IPv6 address, or a name, and may include a 17 // port separated by a colon (':'). If an IPv6 address and a port are both 18 // desired, use brackets ('[' and ']') to separate the address from the port 19 // (e.g. "[::1]:80"). If the port is not specified, it is inferred from the 20 // service parameter. If a name is used instead of an IP address, a DNS lookup 21 // is performed, consulting the local /etc/hosts file or equivalent, if 22 // possible. 23 // 24 // The service parameter can be a service name (e.g. "submission") or a default 25 // port to use, if one is not specified by address. If a service name is used, 26 // an internal list of services is consulted (see [[registersvc]]), and if not 27 // known to Hare, the system service list (e.g. /etc/services) will be 28 // consulted. If the connection port cannot be established, [[errors::invalid]] 29 // is returned. The special service name "unknown" will always consult the 30 // address parameter for a desired port, and will return [[errors::invalid]] if 31 // one is not provided there. 32 // 33 // If the address parameter includes a name, but not a port, an SRV lookup will 34 // be performed alongside the A or AAAA record lookup for that name. If the name 35 // server provides an SRV record for the given service, it will be utilized in 36 // lieu of the service database. 37 export fn dial( 38 proto: str, 39 address: str, 40 service: str, 41 ) (net::socket | error) = { 42 for (let i = 0z; i < len(default_protocols); i += 1) { 43 const p = default_protocols[i]; 44 if (p.name == proto) { 45 return p.dial(address, service); 46 }; 47 }; 48 for (let i = 0z; i < len(protocols); i += 1) { 49 const p = protocols[i]; 50 if (p.name == proto) { 51 return p.dial(address, service); 52 }; 53 }; 54 return net::unknownproto: net::error; 55 }; 56 57 def HOST_MAX: size = 255; 58 59 // Performs a [[dial]] operation for a given URI, taking the service name from 60 // the URI scheme and forming an address from the URI host and port. 61 export fn dial_uri(proto: str, uri: *uri::uri) (net::socket | error) = { 62 // XXX: Should the code to convert a URI to e.g. "[::1]:80" be 63 // generalized for end-user use? 64 if (uri.host is str && len(uri.host as str) > HOST_MAX) { 65 return invalid_address; 66 }; 67 static let addr: [HOST_MAX + len("[]:65535")]u8 = [0...]; 68 69 const colon = if (uri.port != 0) ":" else ""; 70 const port: fmt::formattable = if (uri.port != 0) uri.port else ""; 71 72 let addr = match (uri.host) { 73 case let host: str => 74 yield fmt::bsprintf(addr, "{}{}{}", host, colon, port); 75 case let ip: ip::addr4 => 76 const host = ip::string(ip); 77 yield fmt::bsprintf(addr, "{}{}{}", host, colon, port); 78 case let ip: ip::addr6 => 79 const host = ip::string(ip); 80 yield fmt::bsprintf(addr, "[{}]{}{}", host, colon, port); 81 }; 82 83 return dial(proto, addr, uri.scheme); 84 };