hare

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

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:
Anet/dial/README | 13+++++++++++++
Anet/dial/dial.ha | 37+++++++++++++++++++++++++++++++++++++
Anet/dial/ip.ha | 13+++++++++++++
Anet/dial/registry.ha | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 10++++++++++
Mstdlib.mk | 34++++++++++++++++++++++++++++++++++
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= \