hare

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

dial.ha (3368B)


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