-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net: allow custom Resolver method implementation(s) #12503
Comments
Should I implement and submit the change for codereview? Or wait for some comments here? |
No need to prototype it yet. The code will be relatively easy compared to getting the design right. I suspect that signature isn't general enough. Maybe it's good enough for a dialer, but perhaps it needs a different name. I bet we don't want to define an interface in the net package. If anything, it could just be an optional func type on the Dialer, similar to funcs on http://golang.org/pkg/net/http/#Transport |
Perhaps call it Anything that does a lookup could ask for optional field for |
I would also like to see a type Resolver interface {
LookupAddr(addr string) (names []string, err error)
LookupCNAME(name string) (cname string, err error)
LookupHost(host string) (addrs []string, err error)
LookupIP(host string) (ips []IP, err error)
LookupMX(name string) (mxs []*MX, err error)
LookupNS(name string) (nss []*NS, err error)
LookupPort(network, service string) (port int, err error)
LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error)
LookupTXT(name string) (txts []string, err error)
} The timeout & deadline functionality could be configured when the resolver is created: func NewResolver(options ResolverOption...) (Resolver, error)
type ResolverOption func(*resolver) error
func ResolverTimeout(duration time.Duration) ResolverOption
func ResolverDeadline(deadline time.Time) ResolverOption |
@benburkert, that is not a Go-style (small) interface. Once you have 9 methods, surely somebody would want to add a tenth later, but they can't for compatibility reasons. 9 methods is also hard to implement. We'd probably have to add some sort of EmptyResolver type that people could embed which just returned errors for everything. I'd start with looking at which interfaces are actually needed by the things this bug is about. Maybe you'd have 9 interfaces instead (maybe starting with 3?) and combine them as needed like io.ReadWriteCloser? I don't know. I haven't given this much thought yet. |
What about Lookup(recordtype, query string) ... It's similar to our Dial(network, address string) function, and would permit wildcard, ANY, and lookups for types not yet added to the dns spec. Just spitballing... |
I just ran in to this issue myself, except a little bit abstracted away from I'm writing a utility that's going to talk over TLS to backend systems (HTTP + JSON) and I'm using Consul to discover the individual backend nodes. The biggest issue is that I don't have all of my system's DNS requests being serviced by Consul, so pulling a configuration from So I'll end up needing to first obtain a list of IP addresses from the Consul DNS endpoint and then use that IP address in the URL. Following that, I'll need to set the If the |
At the moment, Dial runs the following processes serially for simplicity:
In near future, when we want more performance on some circumstances, we probably run:
For both cases, the Resolver interface needs to take information for host and service filters. Moreover, it would probably need certificates for supporting upcoming DNS over TLS and DANE. Looks like this proposal makes it possible to place complicated DNS-related packages at x/net repository. I'm happy if we have fancy name/service discovery functionality without replumbing of packages in standard library. |
I am trying to run a network application at Android arm64 system and It would be nice if I can implement a custom dns resolver and tell my application to use it. Here is similar issue from another project [1]. |
Valid use-case for custom resolver, but have you tried using the Android NDK to build your binary? |
I'd similarly be interested in having timing information available, similar to Happy to open up a separate proposal if it's felt to be off-topic for this one? |
This needs a proper proposal document to move forward. |
An extension to the work done in #16672 |
In particular, this got submitted: https://go-review.googlesource.com/29440 |
hi all, this topic seems to be implemented and still open so I'd like to try it ;) The only option I miss is to avoid the fallback to another DNS - I thought about a switch. |
Copying my comment from https://go-review.googlesource.com/c/go/+/115855#message-d80f076d91f28a2e5aa2f1eb6fdd88a33aec9502 ....
/cc @iangudger |
Another way to achieve this with the current interface would be to use the PacketResolver from golang.org/cl/107306, wrap it in a net.Conn implantation, and return it from Resolver.Dial. |
Oh, nice. PacketResolver makes such a glue package even easier. |
Check out https://godoc.org/github.com/benburkert/dns for some prior art of that glue package. |
how is this going? is there a current working example to force using 8.8.8.8 resolver and somesort of timeout? |
I'm also hitting this with a different use case: making a service that sends webhooks. I need a way to avoid posting webhooks to internal IPs.
Ideally there would be a way to "MITM" the Resolver call, to inject my own error if a returned IP is internal. |
I've had to do this kind of manual resolution with both torrent trackers and DHT global bootstrap resolution in order to implement IP blocklists. In both cases I'd rather just provide a resolution hook and filter or disallow IPs from the blocked ranges in the results. |
@Dirbaio I did something similar in the past, but without doing any resolver stuff. (This is very old code, not sure if still valid)
Usage
It does allow dialing to internal IP, but not able to send any requests to that. |
For anyone needs this functionality until a patch comes (if it comes at all), I recently wrote a partial net.Dialer replacement that accepts an interface Resolver. After using the custom resolver, it still uses net.Dialer internally and tries to mimic the original net.Dialer's behaviour when possible. It can also be used with http.Transport and http.Client. Check it out here: https://github.com/cevatbarisyilmaz/ara |
I appreciate your work, can you provide |
Sure, I'll take a look. |
I pushed the commit though I didn't really test it yet (at least it didn't disrupt the current tests). Feel free to open an issue in the repo for any further things. |
Is there any chance that whatever approach is taken here (in the |
See github.com/ncruces/go-dns for more "prior art" on hooking into This bit implements the strategy mentioned in the above comment (a fake |
I figured out the code bellow. package main
import (
"log"
"net"
"time"
"github.com/benburkert/dns"
)
func init() {
zone := &dns.Zone{
Origin: "example.org.",
TTL: 5 * time.Minute,
RRs: dns.RRSet{
"foo": {
dns.TypeA: []dns.Record{
&dns.A{A: net.ParseIP("1.2.3.4")},
},
},
},
}
mux := new(dns.ResolveMux)
mux.Handle(dns.TypeANY, zone.Origin, zone)
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: (&dns.Client{
Resolver: mux,
}).Dial,
}
}
func main() {
log.Println(net.LookupHost("foo.example.org")) // it's working. output: [1.2.3.4] <nil>
log.Println(net.LookupHost("www.example.org")) // error no such host. I don't known how to fallback to DNS query
} |
@bradfitz check if this similar to what you had in mind https://go-review.googlesource.com/c/net/+/347850 |
I've prototyped a fairly simple change that significantly narrows the required customization to a single-function interface that doesn't abstract everything that Resolver does, just the part that is used by net.Dialer (and thus by net/http), namely the internetAddrList() private method. In the net package, I add (in dial.go in my prototype):
And allow users to provide an alternate implementation for net.Dialer to use. For maximum usefulness, it is best to also allow a *net.Resolver to be used as an InternetAddrLister so people can provide a replacement by just wrapping around the existing net.Resolver implementation (for cases where they want to do light customization rather than provide a whole custom resolution implementation). I think this function is a very useful abstraction (and the Go authors appear to agree since this is used quite a few places) and we should just make it a public method of Resolver. I can submit my changes but I wanted to align on the approach before doing so. |
This mocks net.LookupIP with a wrapper type. In future, this might be easier to accomplish with the help of the standard library (see discussion in golang/go#12503). With muety#1 in the pipeline, I might consider swapping the stdlib resolver with a full fledged DNS client (github.com/miekg/dns), as the stdlib resolver does not return TTL values.
This mocks net.LookupIP with a wrapper type. In future, this might be easier to accomplish with the help of the standard library (see discussion in golang/go#12503). With muety#1 in the pipeline, I might consider swapping the stdlib resolver with a full fledged DNS client (github.com/miekg/dns), as the stdlib resolver does not return TTL values.
Until now, we assumed the control plane endpoint host to be an IP, but that's an unnecessary restriction. The template now consumes a new environment variable: `CONTROL_PLANE_ENDPOINT_HOST` It's optional and defaults to the value of the existing `CONTROL_PLANE_ENDPOINT_IP` environment variable. As the host is required to resolve to that IP, we'd actually not need it anymore to be given explicitly. However, as we still render the kube-vip manifest in the template and don't have anything nice there to do the translation automatically, we keep the explicit IP for now. The controller code is not aware of the `CONTROL_PLANE_ENDPOINT_IP` value, though. It only sees the control plane endpoint host, which can now be either an IP or an FQDN, so the controller must be able to resolve the name to use the corresponding IP to find the matching IP block and to find the correct IP failover group. Unit tests for DNS resolving were not as convenient as I expected. The stdlib tests don't provide any means to mock it, they even rely on external network reachability to resolve some Google domain names. They pointed to golang/go#12503, though. There's an interesting link to https://github.com/ncruces/go-dns, showing how to inject custom behavior using a custom dialer, enabling things such as caching. It's overkill for us, though, so our used approach for unit testing is simpler.
I mentioned in #12476 that I would like to detect the time it took for DNS resolution phase of the Dial process in
Dialer.Dial
. The solution posted there was very hacky and adds unnecessarily to the API.@bradfitz suggested
This seems like an excellent idea. Here is how I propose we go about it by adding minimal complexity and preserving code compatibility.
I propose net package adds a new
Resolver
interface.The signature of
Resolver.Resolve
is same aslookupIPDeadline
whichDial
eventually uses.Dialer
gets an optional fieldCustomResolver
of typeResolver
.The
Resolver
object (ornil
) gets passed around thru the resolution process.Dialer.Dial
->resolveAddrList
->internetAddrList
.internetAddrList
currently always useslookupIPDeadline
, it would need to be changed such that if the passed custom resolver is not nil then use it, otherwise uselookupIPDeadline
.Other functions calling
resolveAddrList
orinternetAddrList
would need to be modified to add an extranil
argument . This does not break code compatibility because they are unexported functions.Benefits of allowing a custom
Resolver
Resolver
implementations.The text was updated successfully, but these errors were encountered: