hare

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

commit c38da7e6ba095f1cbed959f0a08ba804f789da47
parent 7427a415776537506435c7d047f8555fb426b811
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu, 24 Jun 2021 12:40:08 -0400

net::dns: fix IPv6 nameservers; add timeout

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mnet/dns/error.ha | 3++-
Mnet/dns/query.ha | 58++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mnet/udp/+linux.ha | 4++++
Mrt/+linux/syscalls.ha | 4++--
Mscripts/gen-stdlib | 2+-
Mstdlib.mk | 4++--
6 files changed, 61 insertions(+), 14 deletions(-)

diff --git a/net/dns/error.ha b/net/dns/error.ha @@ -26,7 +26,7 @@ export type unknown_error = !u8; // All error types which might be returned from [[net::dns]] functions. export type error = !(format | server_failure | name_error | not_implemented | refused | unknown_error - | errors::overflow | net::error); + | errors::overflow | errors::timeout | net::error); export fn strerror(err: error) const str = { static let buf: [64]u8 = [0...]; @@ -38,6 +38,7 @@ export fn strerror(err: error) const str = { refused => "The name server refuses to perform the specified operation for policy reasons", ue: unknown_error => fmt::bsprintf(buf, "Unknown DNS error {}", ue: u8), errors::overflow => "The encoded message would exceed the buffer size", + errors::timeout => "The DNS request timed out", err: net::error => net::strerror(err), }; }; diff --git a/net/dns/query.ha b/net/dns/query.ha @@ -1,39 +1,81 @@ +use errors; use net::ip; use net::udp; +use rt; +use time; use unix::resolvconf; +// TODO: Let user customize this? +def timeout: time::duration = 3 * time::SECOND; + // Performs a DNS query using the provided list of DNS servers. The caller must // free the return value with [[message_free]]. // // If no DNS servers are provided, the system default servers (if any) are used. export fn query(query: *message, servers: ip::addr...) (*message | error) = { + // TODO: + // - Swap rt::poll for unix::poll + // - Use TCP for messages >512 bytes if (len(servers) == 0) { servers = resolvconf::load(); }; if (len(servers) == 0) { // Fall back to localhost - servers = [[127, 0, 0, 1]: ip::addr4]; + servers = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]: ip::addr6, + [127, 0, 0, 1]: ip::addr4, + ]; }; - const socket = udp::listen(ip::ANY_V4, 0)?; - defer udp::close(socket); + const socket4 = udp::listen(ip::ANY_V4, 0)?; + defer udp::close(socket4); + const socket6 = udp::listen(ip::ANY_V6, 0)?; + defer udp::close(socket6); + const pollfd: [_]rt::pollfd = [ + rt::pollfd { + fd = udp::sockfd(socket4), + events = rt::POLLIN, + ... + }, + rt::pollfd { + fd = udp::sockfd(socket6), + events = rt::POLLIN, + ... + }, + ]; - // TODO: Use TCP for messages >512 bytes let sendbuf: [512]u8 = [0...]; let z = encode(sendbuf, query)?; // We send requests in parallel to all configured servers and take the // first one which sends us a reasonable answer. - for (let i = 0z; i < len(servers); i += 1) { - udp::sendto(socket, sendbuf[..z], servers[i], 53)?; + for (let i = 0z; i < len(servers); i += 1) match (servers[i]) { + ip::addr4 => { + udp::sendto(socket4, sendbuf[..z], servers[i], 53)?; + }, + ip::addr6 => { + udp::sendto(socket6, sendbuf[..z], servers[i], 53)?; + }, }; let header = header { ... }; let recvbuf: [512]u8 = [0...]; for (true) { - // TODO: Add timeout + let ts = rt::timespec { ... }; + time::duration_to_timespec(timeout, &ts); + let nevent = rt::ppoll(&pollfd: *[*]rt::pollfd, + len(pollfd), &ts, null)!; + if (nevent == 0) { + return errors::timeout; + }; + let src: ip::addr = ip::ANY_V4; - z = udp::recvfrom(socket, recvbuf, &src, null)?; + if (pollfd[0].revents & rt::POLLIN == rt::POLLIN) { + z = udp::recvfrom(socket4, recvbuf, &src, null)?; + }; + if (pollfd[1].revents & rt::POLLIN == rt::POLLIN) { + z = udp::recvfrom(socket6, recvbuf, &src, null)?; + }; let expected = false; for (let i = 0z; i < len(servers); i += 1) { diff --git a/net/udp/+linux.ha b/net/udp/+linux.ha @@ -28,6 +28,10 @@ export fn connect( }; }; +// Gets the file descriptor associated with a UDP socket. This function is +// non-portable. +export fn sockfd(sock: socket) int = sock; + // Creates an [[io::stream]] for an open UDP socket, which allows you to send // and receive UDP packets using [[io::read]] and [[io::write]], albiet without // being able to receive the sender address. Only meaningful if the socket was diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -372,7 +372,7 @@ export fn getcwd() (*const char | errno) = { }; export fn ppoll( - fds: *pollfd, + fds: *[*]pollfd, nfds: nfds_t, timeout: const nullable *timespec, sigmask: const nullable *sigset, @@ -381,7 +381,7 @@ export fn ppoll( timeout: uintptr: u64, sigmask: uintptr: u64))?: int; }; -export fn poll(fds: *pollfd, nfds: nfds_t, timeout: int) (int | errno) = { +export fn poll(fds: *[*]pollfd, nfds: nfds_t, timeout: int) (int | errno) = { const ts = timespec { tv_sec = timeout % 1000, tv_nsec = timeout * 1000000, diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -570,7 +570,7 @@ net_dns() { query.ha \ types.ha gen_ssa net::dns ascii endian net net::udp net::ip fmt strings \ - unix::resolvconf + unix::resolvconf rt time errors } gensrcs_net_ip() { diff --git a/stdlib.mk b/stdlib.mk @@ -855,7 +855,7 @@ stdlib_net_dns_srcs= \ $(STDLIB)/net/dns/query.ha \ $(STDLIB)/net/dns/types.ha -$(HARECACHE)/net/dns/net_dns.ssa: $(stdlib_net_dns_srcs) $(stdlib_rt) $(stdlib_ascii) $(stdlib_endian) $(stdlib_net) $(stdlib_net_udp) $(stdlib_net_ip) $(stdlib_fmt) $(stdlib_strings) $(stdlib_unix_resolvconf) +$(HARECACHE)/net/dns/net_dns.ssa: $(stdlib_net_dns_srcs) $(stdlib_rt) $(stdlib_ascii) $(stdlib_endian) $(stdlib_net) $(stdlib_net_udp) $(stdlib_net_ip) $(stdlib_fmt) $(stdlib_strings) $(stdlib_unix_resolvconf) $(stdlib_rt) $(stdlib_time) $(stdlib_errors) @printf 'HAREC \t$@\n' @mkdir -p $(HARECACHE)/net/dns @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nnet::dns \ @@ -2025,7 +2025,7 @@ testlib_net_dns_srcs= \ $(STDLIB)/net/dns/query.ha \ $(STDLIB)/net/dns/types.ha -$(TESTCACHE)/net/dns/net_dns.ssa: $(testlib_net_dns_srcs) $(testlib_rt) $(testlib_ascii) $(testlib_endian) $(testlib_net) $(testlib_net_udp) $(testlib_net_ip) $(testlib_fmt) $(testlib_strings) $(testlib_unix_resolvconf) +$(TESTCACHE)/net/dns/net_dns.ssa: $(testlib_net_dns_srcs) $(testlib_rt) $(testlib_ascii) $(testlib_endian) $(testlib_net) $(testlib_net_udp) $(testlib_net_ip) $(testlib_fmt) $(testlib_strings) $(testlib_unix_resolvconf) $(testlib_rt) $(testlib_time) $(testlib_errors) @printf 'HAREC \t$@\n' @mkdir -p $(TESTCACHE)/net/dns @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nnet::dns \