SwiftNIO based DNS Client

DNS is a very commonly needed protocol. Currently, SwiftNIO based applications use the operating system's synchronous calls for resolving domain names. Aside from this, there's a lack of feature support in the POSIX defined APIs. This can prevent software from accessing resource records like SRV and TXT.

Motivation

My personal motiviation is the MongoKitten project, where I need(ed) to access TXT and SRV records as described above. This needs to support iOS, macOS, and Linux.

An example implementation

There is an example implementation of a DNS Client available at OpenKitten/NioDNS. I think there's a lot of work to be done, specifically in unit tests and features. The internals seem pretty reliable so far.

NioDNS

A synchronous example of NioDNS for simplicity. Note that the intended use case of NioDNS is specifically not synchronous.

import NIODNS

let eventLoop: EventLoop = ...

// 8.8.8.8 could be another DNS Server and if left empty
// is read from the `/etc/resolv.conf` file
let dnsClient = try DNSClient.connect(to: "8.8.8.8", on: eventLoop).wait()

let records: [TxtRecord] = try dnsClient.fetchRecords(TxtRecord.self, forDomain: "example.com").wait()
9 Likes

Strongly agreed that a SwiftNIO based DNS client would be extremely useful. In particular as the ecosystem matures there will likely be interest in making a non-blocking resolver available in the NIO community for high performance work as well as for broadening the API surface.

I'm in favour of the SSWG adopting this project.

3 Likes

Awesome! Having a SwiftNIO based DNS client would be really great and is something that will be required at some point anyway.

I think what you've done so far with starting this as a separate repository is exactly the right call. DNS clients after all are really hard to fully get right and it's much easier to evolve this as a separate repository. Assuming your goal is to at some point getting this to be able to serve as a drop-in replacement for the system's resolver I would even think this could eventually be part of SwiftNIO, if you're happy with this of course.

Regarding the SSWG adopting this project: At first I was not sure if DNS (apart from the obviously important looking up IPs from a hostname) really meets the 'providing the basic server needs' but it seems that MongoDB clients need access to SRV/TXT records, correct? If that's the case then I think this should definitely become an SSWG adopted project because otherwise the SSWG couldn't adopt a MongoDB driver which IMHO definitely falls under the 'basic needs'.

Correct, the MongoDB proposal would rely on SRV and TXT records if you want to be feature complete and support cloud providers like Atlas. I'd be happy with this being adopted into SwiftNIO. Although I don't feel that it's strictly necessary. The protocol laid out in SwiftNIO 1.x was usable and adopted into my implementation of a DNS Client as you can see.

Thanks for the clarification, +1 from me then. Just out of curiosity: Would it be possible to separate the bits that need TXT/SRV from the 'core' of a MongoDB driver or is there a hard dependency on that even for the core bits? Ie. is it possible without losing functionality to have CoreMongoDriver that can live without access to TXT/SRV and a separate MongoOnCloudProviders package that adds this extra functionality?

That's awesome. I'll leave this up to you then :slight_smile:. Out of curiosity again: Are you already running SwiftNIO where you completely replace GetaddrinfoResolver with your resolver? Or are you using both at the same time right now?

Yeah, that's doable. I only need it for the initial connection phase (DNS Seedlist Discovery). There's no limitation preventing you from making it a separate package. You could add the DNSClient's feature in an extension on the ConnectionSettings struct that MongoKitten has now and/or the alternative for the other propsal.

MongoKitten runs SwiftNIO with a NIO.Resolver conformant client. It doesn't yet use the resolver for creating a socket connection, so I'm using both at the same time right now. But I did test that functionality with an HTTP Client. The only reason I didn't use my own resolver for this is the fact that I forgot to apply it there :sweat_smile:

1 Like

We should define the scope of the client. Should it support DNS over TSL, DNS over HTTPS, …

My personal motto is "reduce scope creep". Let's begin by supporting DNS over UDP, and consider it a high priority roadmap item to support fallback to TCP. We can consider DoH and DoT as long-tail concerns that don't immediately need to be addressed to be useful.

1 Like

I concur with @lukasa. I might even prefer making a separate pitch in the future for additions like DoH.

DNS library that can do TXT/SRV would be great for service discovery and client load balancing so +1 for minimal scope, focused, library that can do this