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:
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 \