hare

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

query.ha (2353B)


      1 // License: MPL-2.0
      2 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      3 use errors;
      4 use net;
      5 use net::ip;
      6 use net::udp;
      7 use time;
      8 use unix::poll;
      9 use unix::resolvconf;
     10 
     11 // TODO: Let user customize this?
     12 def timeout: time::duration = 3 * time::SECOND;
     13 
     14 // Performs a DNS query using the provided list of DNS servers. The caller must
     15 // free the return value with [[message_free]].
     16 //
     17 // If no DNS servers are provided, the system default servers (if any) are used.
     18 export fn query(query: *message, servers: ip::addr...) (*message | error) = {
     19 	// TODO: Use TCP for messages >512 bytes
     20 	if (len(servers) == 0) {
     21 		servers = resolvconf::load();
     22 	};
     23 	if (len(servers) == 0) {
     24 		// Fall back to localhost
     25 		servers = [ip::LOCAL_V6, ip::LOCAL_V4];
     26 	};
     27 
     28 	let socket4 = udp::listen(ip::ANY_V4, 0)?;
     29 	defer net::close(socket4)!;
     30 	let socket6 = udp::listen(ip::ANY_V6, 0)?;
     31 	defer net::close(socket6)!;
     32 	const pollfd: [_]poll::pollfd = [
     33 		poll::pollfd {
     34 			fd = socket4,
     35 			events = poll::event::POLLIN,
     36 			...
     37 		},
     38 		poll::pollfd {
     39 			fd = socket6,
     40 			events = poll::event::POLLIN,
     41 			...
     42 		},
     43 	];
     44 
     45 	let buf: [512]u8 = [0...];
     46 	let z = encode(buf, query)?;
     47 
     48 	// We send requests in parallel to all configured servers and take the
     49 	// first one which sends us a reasonable answer.
     50 	for (let i = 0z; i < len(servers); i += 1) match (servers[i]) {
     51 	case ip::addr4 =>
     52 		udp::sendto(socket4, buf[..z], servers[i], 53)?;
     53 	case ip::addr6 =>
     54 		udp::sendto(socket6, buf[..z], servers[i], 53)?;
     55 	};
     56 
     57 	let header = header { ... };
     58 	for (true) {
     59 		let nevent = poll::poll(pollfd, timeout)!;
     60 		if (nevent == 0) {
     61 			return errors::timeout;
     62 		};
     63 
     64 		let src: ip::addr = ip::ANY_V4;
     65 		if (pollfd[0].revents & poll::event::POLLIN != 0) {
     66 			z = udp::recvfrom(socket4, buf, &src, null)?;
     67 		};
     68 		if (pollfd[1].revents & poll::event::POLLIN != 0) {
     69 			z = udp::recvfrom(socket6, buf, &src, null)?;
     70 		};
     71 
     72 		let expected = false;
     73 		for (let i = 0z; i < len(servers); i += 1) {
     74 			if (ip::equal(src, servers[i])) {
     75 				expected = true;
     76 				break;
     77 			};
     78 		};
     79 		if (!expected) {
     80 			continue;
     81 		};
     82 
     83 		const dec = decoder_init(buf[..z]);
     84 		decode_header(&dec, &header)?;
     85 		if (header.id == query.header.id && header.op.qr == qr::RESPONSE) {
     86 			break;
     87 		};
     88 	};
     89 
     90 	assert(!header.op.tc, "TODO: Retry with TCP for truncated DNS response");
     91 
     92 	check_rcode(header.op.rcode)?;
     93 	return decode(buf[..z])?;
     94 };