commit c3610791a32991d46814afa36c19a9c2521424ec
parent f882bb8727c7b012e6aa030eb6165553dae29331
Author: Drew DeVault <sir@cmpwn.com>
Date: Wed, 23 Jun 2021 16:17:43 -0400
net::dns: send requests to all nameservers at once
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/net/dns/query.ha b/net/dns/query.ha
@@ -6,9 +6,13 @@ use unix::resolvconf;
// 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, addr: ip::addr...) (*message | error) = {
- if (len(addr) == 0) {
- addr = resolvconf::load();
+export fn query(query: *message, servers: ip::addr...) (*message | error) = {
+ if (len(servers) == 0) {
+ servers = resolvconf::load();
+ };
+ if (len(servers) == 0) {
+ // Fall back to localhost
+ servers = [[127, 0, 0, 1]: ip::addr4];
};
let socket = udp::listen(ip::ANY_V4, 0)?;
@@ -17,8 +21,12 @@ export fn query(query: *message, addr: ip::addr...) (*message | error) = {
// TODO: Use TCP for messages >512 bytes
let sendbuf: [512]u8 = [0...];
let z = encode(sendbuf, query)?;
- // TODO: Query multiple servers
- udp::sendto(socket, sendbuf[..z], addr[0], 53)?;
+
+ // 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)?;
+ };
let header = header { ... };
let recvbuf: [512]u8 = [0...];
@@ -26,19 +34,27 @@ export fn query(query: *message, addr: ip::addr...) (*message | error) = {
// TODO: Add timeout
let src: ip::addr = ip::ANY_V4;
z = udp::recvfrom(socket, recvbuf, &src, null)?;
- if (!ip::equal(src, addr[0])) {
+
+ let expected = false;
+ for (let i = 0z; i < len(servers); i += 1) {
+ if (ip::equal(src, servers[i])) {
+ expected = true;
+ break;
+ };
+ };
+ if (!expected) {
continue;
};
let dec = decoder_init(recvbuf[..z]);
decode_header(&dec, &header)?;
- if (header.id != query.header.id || header.op.qr != qr::RESPONSE) {
- continue;
+ if (header.id == query.header.id && header.op.qr == qr::RESPONSE) {
+ break;
};
-
- break;
};
+ assert(!header.op.tc, "TODO: Retry with TCP for truncated DNS response");
+
check_rcode(header.op.rcode)?;
return decode(recvbuf[..z])?;
};