commit 4266189fdd1579928e3565f3281f697ad5827c0f
parent dfa3a0becad55306a168c28cd7d95a7a4c81c047
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 21 Jun 2021 14:28:12 -0400
net::dial: new module (skeleton only)
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
6 files changed, 192 insertions(+), 0 deletions(-)
diff --git a/net/dial/README b/net/dial/README
@@ -0,0 +1,13 @@
+[[net::dial]] provides a single function to facilitate the establishment of
+outgoing network connections. It handles port selection, address parsing,
+protocol and service lookup, DNS lookup (via /etc/hosts, /etc/resolv.conf, etc),
+SRV record resolution, and so on. See [[dial]] for details.
+
+Modules implementing their own network protocols are also able to add themselves
+to the protocol and service registry. The protocol registry is used for
+transport-level protocols (such as TCP) and is managed via [[registerproto]];
+the service registry is used for application-level protocols (such as SSH) and
+is managed via [[registersvc]].
+
+Some useful functions for IP-related protocols to interpret the "addr" parameter
+of the dial function are also provided, namely [[resolve]].
diff --git a/net/dial/dial.ha b/net/dial/dial.ha
@@ -0,0 +1,37 @@
+use io;
+use net;
+
+// Dials a remote address using, establishing a connection and returning the
+// resulting [[io::stream]]. 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 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 [[dial::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,
+) (*io::stream | net::error) = {
+ abort(); // TODO
+};
diff --git a/net/dial/ip.ha b/net/dial/ip.ha
@@ -0,0 +1,13 @@
+// Provides default dialers for tcp and udp
+use net;
+use net::ip;
+use net::tcp;
+use net::udp;
+
+fn dial_tcp(addr: str, service: str) (*io::stream | net::error) = {
+ abort(); // TODO
+};
+
+fn dial_udp(addr: str, service: str) (*io::stream | net::error) = {
+ abort(); // TODO
+};
diff --git a/net/dial/registry.ha b/net/dial/registry.ha
@@ -0,0 +1,85 @@
+use io;
+use net;
+
+export type dialer = fn(addr: str, service: str) (*io::stream | net::error);
+
+type protocol = struct {
+ name: str,
+ dial: *dialer,
+};
+
+type service = struct {
+ name: str,
+ alias: []str,
+ port: u16,
+};
+
+let default_protocols: []protocol = [
+ protocol { name = "tcp", dial = &dial_tcp },
+ protocol { name = "udp", dial = &dial_udp },
+];
+
+let default_tcp: []service = [
+ service { name = "ssh", alias = [], port = 22 },
+ service { name = "smtp", alias = ["mail"], port = 25 },
+ service { name = "domain", alias = ["dns"], port = 53 },
+ service { name = "http", alias = ["www"], port = 80 },
+ service { name = "imap2", alias = ["imap"], port = 143 },
+ service { name = "https", alias = [], port = 443 },
+ service { name = "submission", alias = [], port = 587 },
+ service { name = "imaps", alias = [], port = 993 },
+];
+
+let default_udp: []service = [
+ service { name = "domain", alias = ["dns"], port = 53 },
+ service { name = "ntp", alias = [], port = 123 },
+];
+
+let protocols: []protocol = [];
+let tcp_services: []service = [];
+let udp_services: []service = [];
+
+@fini fn fini() void = {
+ free(protocols);
+ free(tcp_services);
+ free(udp_services);
+};
+
+// Registers a new transport-level protocol (e.g. TCP) with the dialer. The name
+// should be statically allocated.
+export fn registerproto(name: str, dial: *dialer) void = {
+ append(protocols, protocol {
+ name = name,
+ dial = dial,
+ });
+};
+
+// Registers a new application-level service (e.g. SSH) with the dialer. Note
+// that the purpose of services is simply to establish the default outgoing
+// port for TCP and UDP connections. The name and alias list should be
+// statically allocated.
+export fn registersvc(
+ name: str,
+ alias: []str,
+ tcp: (u16 | void),
+ udp: (u16 | void),
+) void = {
+ assert(!(tcp is void) || !(udp is void),
+ "Expected registersvc to receive TCP or UDP port, or both");
+ match (tcp) {
+ void => void,
+ port: u16 => append(tcp_services, service {
+ name = name,
+ alias = alias,
+ port = port,
+ }),
+ };
+ match (udp) {
+ void => void,
+ port: u16 => append(udp_services, service {
+ name = name,
+ alias = alias,
+ port = port,
+ }),
+ };
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -550,6 +550,15 @@ net() {
gen_ssa net io os strings net::ip errors rt fmt
}
+net_dial() {
+ printf '# net::dial\n'
+ gen_srcs net::dial \
+ dial.ha \
+ ip.ha \
+ registry.ha
+ gen_ssa net::dial io net net::ip net::tcp net::udp
+}
+
net_dns() {
printf '# net::dns\n'
gen_srcs net::dns \
@@ -834,6 +843,7 @@ linux::signalfd
linux::io_uring
linux::vdso
net
+net::dial
net::dns
net::ip
net::tcp
diff --git a/stdlib.mk b/stdlib.mk
@@ -245,6 +245,10 @@ hare_stdlib_deps+=$(stdlib_linux_vdso)
stdlib_net=$(HARECACHE)/net/net.o
hare_stdlib_deps+=$(stdlib_net)
+# gen_lib net::dial
+stdlib_net_dial=$(HARECACHE)/net/dial/net_dial.o
+hare_stdlib_deps+=$(stdlib_net_dial)
+
# gen_lib net::dns
stdlib_net_dns=$(HARECACHE)/net/dns/net_dns.o
hare_stdlib_deps+=$(stdlib_net_dns)
@@ -828,6 +832,19 @@ $(HARECACHE)/net/net.ssa: $(stdlib_net_srcs) $(stdlib_rt) $(stdlib_io) $(stdlib_
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nnet \
-t$(HARECACHE)/net/net.td $(stdlib_net_srcs)
+# net::dial
+# net::dial
+stdlib_net_dial_srcs= \
+ $(STDLIB)/net/dial/dial.ha \
+ $(STDLIB)/net/dial/ip.ha \
+ $(STDLIB)/net/dial/registry.ha
+
+$(HARECACHE)/net/dial/net_dial.ssa: $(stdlib_net_dial_srcs) $(stdlib_rt) $(stdlib_io) $(stdlib_net) $(stdlib_net_ip) $(stdlib_net_tcp) $(stdlib_net_udp)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/net/dial
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nnet::dial \
+ -t$(HARECACHE)/net/dial/net_dial.td $(stdlib_net_dial_srcs)
+
# net::dns
# net::dns
stdlib_net_dns_srcs= \
@@ -1376,6 +1393,10 @@ hare_testlib_deps+=$(testlib_linux_vdso)
testlib_net=$(TESTCACHE)/net/net.o
hare_testlib_deps+=$(testlib_net)
+# gen_lib net::dial
+testlib_net_dial=$(TESTCACHE)/net/dial/net_dial.o
+hare_testlib_deps+=$(testlib_net_dial)
+
# gen_lib net::dns
testlib_net_dns=$(TESTCACHE)/net/dns/net_dns.o
hare_testlib_deps+=$(testlib_net_dns)
@@ -1979,6 +2000,19 @@ $(TESTCACHE)/net/net.ssa: $(testlib_net_srcs) $(testlib_rt) $(testlib_io) $(test
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nnet \
-t$(TESTCACHE)/net/net.td $(testlib_net_srcs)
+# net::dial
+# net::dial
+testlib_net_dial_srcs= \
+ $(STDLIB)/net/dial/dial.ha \
+ $(STDLIB)/net/dial/ip.ha \
+ $(STDLIB)/net/dial/registry.ha
+
+$(TESTCACHE)/net/dial/net_dial.ssa: $(testlib_net_dial_srcs) $(testlib_rt) $(testlib_io) $(testlib_net) $(testlib_net_ip) $(testlib_net_tcp) $(testlib_net_udp)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/net/dial
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nnet::dial \
+ -t$(TESTCACHE)/net/dial/net_dial.td $(testlib_net_dial_srcs)
+
# net::dns
# net::dns
testlib_net_dns_srcs= \