It was very relevant to me a couple days ago, so I think it's not the case that Go programs that look up names link by default dynamically to libc, just for whatever that's worth to anyone.
(Our company, Fly.io, runs container images for customers on Firecracker microVMs around the world, and I had to build a DNS-dependent service, in Go, that runs directly from our (Rust) init and can't assume a libc exists).
The downside to Go shipping it's own implementation of DNS resolution is that on systems that support far more rich set of DNS options (such as split DNS based upon hostnames in macOS) is that it doesn't work if the binary is built with a pure Go implementation.
That leaves users annoyed, because sending all DNS into a VPN tunnel is not always an option.
If cgo is enabled (the usual case), Go uses libc for DNS if the configuration looks exotic enough that the pure-Go code wouldn't give the same results.
Mostly just init stuff. We're transforming Docker containers (mostly) into standalone VMs, so it's doing all the scut work of taking a completely stripped-bare booted kernel and getting it to a state where you can run an arbitrary Linux program on it.
I wouldn't want to take the thread off on a huge tangent, it's just funny that this was just recently super relevant to me (it would have been problematic if DNS-dependent Go programs depended on libc, because right now I can't assume there's a libc binary to be dynamically linked to).
Bringing it back to Go and its (sometimes libc-dependent) DNS libraries: it is very annoying how fiddly it is to get a Go program to use an alternative DNS server.
A really solid alternative DNS client implementation can be found here: https://github.com/miekg/dns. Real easy to read and vet compared to a few other libraries I ran into when working on this problem.
I'm currently in the process, right now, of writing a minimal init in Rust to do exactly the same thing. Is yours something you'd consider sharing?
The sum total of what I want to do: bring up loopback, bring up the one and only Ethernet interface, set up its IP and basic routing, run another program, and do some basic log reporting.
I hack on our init, but I didn't write it. So it's not my place to share it. And there's some us-specific stuff in it that wouldn't be super helpful to you. But you could definitely ask Jerome on our team; he's a super helpful guy, even if he's super quiet here (he's like the anti-me). One way or the other I'm sure we can help you get where you're going!
Feel free to follow up with Kurt, me or Michael if you don't get a quick response; Jerome is in Montreal and he may be buried under a mountain of snow and brown gravy.
If you can can control which DNS servers it talks to, implementing the DNS spec directly is a pretty trivial exercise to get full control. (If you can't, the main complexity is dealing with implementation quirks)
(Our company, Fly.io, runs container images for customers on Firecracker microVMs around the world, and I had to build a DNS-dependent service, in Go, that runs directly from our (Rust) init and can't assume a libc exists).