package dns
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"strings"
"golang.org/x/net/dns/dnsmessage"
)
type DoHLookuper struct {
URL string
HTTPClient *http .Client
}
func (l *DoHLookuper ) LookupTXT (ctx context .Context , name string ) ([]string , error ) {
if l == nil || l .URL == "" {
return nil , fmt .Errorf ("doh: missing endpoint url" )
}
msg , err := packTXTQuery (name )
if err != nil {
return nil , err
}
req , err := http .NewRequestWithContext (ctx , http .MethodPost , l .URL , bytes .NewReader (msg ))
if err != nil {
return nil , err
}
req .Header .Set ("Accept" , "application/dns-message" )
req .Header .Set ("Content-Type" , "application/dns-message" )
client := l .HTTPClient
if client == nil {
client = http .DefaultClient
}
resp , err := client .Do (req )
if err != nil {
return nil , fmt .Errorf ("doh lookup %q: %w" , name , err )
}
defer resp .Body .Close ()
if resp .StatusCode < 200 || resp .StatusCode >= 300 {
return nil , fmt .Errorf ("doh lookup %q: http status %s" , name , resp .Status )
}
body , err := io .ReadAll (io .LimitReader (resp .Body , 64 *1024 ))
if err != nil {
return nil , fmt .Errorf ("doh lookup %q: read response: %w" , name , err )
}
txt , err := unpackTXTResponse (body )
if err != nil {
return nil , fmt .Errorf ("doh lookup %q: %w" , name , err )
}
return txt , nil
}
func packTXTQuery(name string ) ([]byte , error ) {
dnsName , err := dnsmessage .NewName (ensureTrailingDot (name ))
if err != nil {
return nil , fmt .Errorf ("doh: bad name %q: %w" , name , err )
}
b := dnsmessage .NewBuilder (nil , dnsmessage .Header {RecursionDesired : true })
if err := b .StartQuestions (); err != nil {
return nil , err
}
if err := b .Question (dnsmessage .Question {
Name : dnsName ,
Type : dnsmessage .TypeTXT ,
Class : dnsmessage .ClassINET ,
}); err != nil {
return nil , err
}
msg , err := b .Finish ()
if err != nil {
return nil , err
}
return msg , nil
}
func unpackTXTResponse(msg []byte ) ([]string , error ) {
var p dnsmessage .Parser
if _ , err := p .Start (msg ); err != nil {
return nil , err
}
if err := p .SkipAllQuestions (); err != nil {
return nil , err
}
var out []string
for {
h , err := p .AnswerHeader ()
if err == dnsmessage .ErrSectionDone {
break
}
if err != nil {
return nil , err
}
if h .Type != dnsmessage .TypeTXT {
if err := p .SkipAnswer (); err != nil {
return nil , err
}
continue
}
txt , err := p .TXTResource ()
if err != nil {
return nil , err
}
out = append (out , strings .Join (txt .TXT , "" ))
}
if len (out ) == 0 {
return nil , fmt .Errorf ("no TXT answers" )
}
return out , nil
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .