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