···1717 HTTPClient http.Client
1818 // DNS resolver used for DNS handle resolution. Calling code can use a custom Dialer to query against a specific DNS server, or re-implement the interface for even more control over the resolution process
1919 Resolver net.Resolver
2020+ // when doing DNS handle resolution, should this resolver attempt re-try against an authoritative nameserver if the first TXT lookup fails?
2121+ TryAuthoritativeDNS bool
2022}
21232224var _ Directory = (*BaseDirectory)(nil)
+65-12
atproto/identity/handle.go
···88 "net"
99 "net/http"
1010 "strings"
1111+ "time"
11121213 "github.com/bluesky-social/indigo/atproto/syntax"
1314)
14151616+func parseTXTResp(res []string) (syntax.DID, error) {
1717+ for _, s := range res {
1818+ if strings.HasPrefix(s, "did=") {
1919+ parts := strings.SplitN(s, "=", 2)
2020+ did, err := syntax.ParseDID(parts[1])
2121+ if err != nil {
2222+ return "", fmt.Errorf("invalid DID in handle DNS record: %w", err)
2323+ }
2424+ return did, nil
2525+ }
2626+ }
2727+ return "", ErrHandleNotFound
2828+}
2929+1530// Does not cross-verify, only does the handle resolution step.
1631func (d *BaseDirectory) ResolveHandleDNS(ctx context.Context, handle syntax.Handle) (syntax.DID, error) {
1732 res, err := d.Resolver.LookupTXT(ctx, "_atproto."+handle.String())
1818- // look for NXDOMAIN
3333+ // check for NXDOMAIN
1934 var dnsErr *net.DNSError
2035 if errors.As(err, &dnsErr) {
2136 if dnsErr.IsNotFound {
···2540 if err != nil {
2641 return "", fmt.Errorf("handle DNS resolution failed: %w", err)
2742 }
4343+ return parseTXTResp(res)
4444+}
28452929- for _, s := range res {
3030- if strings.HasPrefix(s, "did=") {
3131- parts := strings.SplitN(s, "=", 2)
3232- did, err := syntax.ParseDID(parts[1])
3333- if err != nil {
3434- return "", fmt.Errorf("invalid DID in handle DNS record: %w", err)
4646+// this is a variant of ResolveHandleDNS which first does an authoritative nameserver lookup, then queries there
4747+func (d *BaseDirectory) ResolveHandleDNSAuthoritative(ctx context.Context, handle syntax.Handle) (syntax.DID, error) {
4848+ // lookup nameserver using configured resolver
4949+ resNS, err := d.Resolver.LookupNS(ctx, handle.String())
5050+ // check for NXDOMAIN
5151+ var dnsErr *net.DNSError
5252+ if errors.As(err, &dnsErr) {
5353+ if dnsErr.IsNotFound {
5454+ return "", ErrHandleNotFound
5555+ }
5656+ }
5757+ if err != nil {
5858+ return "", fmt.Errorf("handle DNS resolution failed: %w", err)
5959+ }
6060+ if len(resNS) == 0 {
6161+ return "", ErrHandleNotFound
6262+ }
6363+ ns := resNS[0].Host
6464+ if !strings.Contains(ns, ":") {
6565+ ns = ns + ":53"
6666+ }
6767+6868+ // create a custom resolver to use the specific nameserver for TXT lookup
6969+ resolver := &net.Resolver{
7070+ PreferGo: true,
7171+ Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
7272+ rd := net.Dialer{
7373+ Timeout: time.Second * 5,
3574 }
3636- return did, nil
7575+ return rd.DialContext(ctx, network, ns)
7676+ },
7777+ }
7878+ res, err := resolver.LookupTXT(ctx, "_atproto."+handle.String())
7979+ // check for NXDOMAIN
8080+ if errors.As(err, &dnsErr) {
8181+ if dnsErr.IsNotFound {
8282+ return "", ErrHandleNotFound
3783 }
3884 }
3939- return "", ErrHandleNotFound
8585+ if err != nil {
8686+ return "", fmt.Errorf("handle DNS resolution failed: %w", err)
8787+ }
8888+ return parseTXTResp(res)
4089}
41904291func (d *BaseDirectory) ResolveHandleWellKnown(ctx context.Context, handle syntax.Handle) (syntax.DID, error) {
···47964897 resp, err := d.HTTPClient.Do(req)
4998 if err != nil {
5050- // look for NXDOMAIN
9999+ // check for NXDOMAIN
51100 var dnsErr *net.DNSError
52101 if errors.As(err, &dnsErr) {
53102 if dnsErr.IsNotFound {
···75124func (d *BaseDirectory) ResolveHandle(ctx context.Context, handle syntax.Handle) (syntax.DID, error) {
76125 // TODO: *could* do resolution in parallel, but expecting that sequential is sufficient to start
77126 did, dnsErr := d.ResolveHandleDNS(ctx, handle)
7878- if dnsErr == nil {
127127+ if dnsErr == ErrHandleNotFound && d.TryAuthoritativeDNS {
128128+ // try harder with authoritative lookup
129129+ did, dnsErr = d.ResolveHandleDNSAuthoritative(ctx, handle)
130130+ }
131131+ if nil == dnsErr { // if *not* an error
79132 return did, nil
80133 }
81134 did, httpErr := d.ResolveHandleWellKnown(ctx, handle)
8282- if httpErr == nil {
135135+ if nil == httpErr { // if *not* an error
83136 return did, nil
84137 }
85138