diff --git a/namesys/dns.go b/namesys/dns.go index 96147534a84..6f1468a5704 100644 --- a/namesys/dns.go +++ b/namesys/dns.go @@ -41,6 +41,11 @@ func (r *DNSResolver) ResolveN(ctx context.Context, name string, depth int) (pat return resolve(ctx, r, name, depth, "/ipns/") } +type lookupRes struct { + path path.Path + error error +} + // resolveOnce implements resolver. // TXT records for a given domain name should contain a b58 // encoded multihash. @@ -50,24 +55,46 @@ func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, if !isd.IsDomain(segments[0]) { return "", errors.New("not a valid domain name") } - log.Infof("DNSResolver resolving %s", segments[0]) - txt, err := r.lookupTXT(segments[0]) + + prefixes := []string{"", "_dnslink."} + + resChan := make(chan lookupRes) + + for _, prefix := range prefixes { + go workDomain(r, prefix + segments[0], resChan) + } + + for active := len(prefixes); active > 0; active-- { + res := <- resChan + if res.error == nil { + return res.path, nil + } + } + + return "", ErrResolveFailed +} + +func workDomain(r *DNSResolver, name string, res chan lookupRes) { + txt, err := r.lookupTXT(name) + if err != nil { - return "", err + res <- lookupRes{"", err} + return } for _, t := range txt { p, err := parseEntry(t) if err == nil { if len(segments) > 1 { - return path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1]) + res <- lookupRes{path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[1])} + } else { + res <-lookupRes{p, nil} } - return p, nil + return } } - - return "", ErrResolveFailed + res <- lookupRes{"", ErrResolveFailed} } func parseEntry(txt string) (path.Path, error) { diff --git a/namesys/dns_test.go b/namesys/dns_test.go index 27b3883db78..0cb83eed348 100644 --- a/namesys/dns_test.go +++ b/namesys/dns_test.go @@ -65,6 +65,9 @@ func newMockDNS() *mockDNS { "ipfs.example.com": []string{ "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", }, + "_dnslink.dipfs.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, "dns1.example.com": []string{ "dnslink=/ipns/ipfs.example.com", }, @@ -85,6 +88,12 @@ func newMockDNS() *mockDNS { "loop2.example.com": []string{ "dnslink=/ipns/loop1.example.com", }, + "_dnslink.dloop1.example.com": []string{ + "dnslink=/ipns/loop2.example.com", + }, + "_dnslink.dloop2.example.com": []string{ + "dnslink=/ipns/loop1.example.com", + }, "bad.example.com": []string{ "dnslink=", }, @@ -100,6 +109,12 @@ func newMockDNS() *mockDNS { "withtrailingrec.example.com": []string{ "dnslink=/ipns/withtrailing.example.com/segment/", }, + "double.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, + "_dnslink.double.example.com": []string{ + "dnslink=/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", + }, }, } } @@ -109,6 +124,7 @@ func TestDNSResolution(t *testing.T) { r := &DNSResolver{lookupTXT: mock.lookupTXT} testResolution(t, r, "multihash.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "ipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) + testResolution(t, r, "dipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "dns1.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) testResolution(t, r, "dns1.example.com", 1, "/ipns/ipfs.example.com", ErrResolveRecursion) testResolution(t, r, "dns2.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) @@ -122,6 +138,10 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "loop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "loop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) testResolution(t, r, "loop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion) + testResolution(t, r, "dloop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion) testResolution(t, r, "bad.example.com", DefaultDepthLimit, "", ErrResolveFailed) testResolution(t, r, "withsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil) testResolution(t, r, "withrecsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil) @@ -129,4 +149,6 @@ func TestDNSResolution(t *testing.T) { testResolution(t, r, "withrecsegment.example.com/test2", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test2", nil) testResolution(t, r, "withrecsegment.example.com/test3/", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test3/", nil) testResolution(t, r, "withtrailingrec.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil) + // _dnslink and . pointing to the same path is good, if they were pointing to different ones behavior would be undefined. + testResolution(t, r, "double.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil) }