hare

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

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