diff --git a/cmd/root.go b/cmd/root.go index c449b1b..74cb6df 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -54,7 +54,8 @@ func init() { } rootCmd.PersistentFlags().StringVarP(&dnsProviderName, - "provider", "p", "google", "Preferred DNS provider to use. [possible: google, cloudflare, quad9, raw]") + "provider", "p", "googlefront", + "Preferred DNS provider to use. [possible: googlefront, google, cloudflare, quad9, raw]") rootCmd.PersistentFlags().BoolVarP(&validateSSL, "validate-certificate", "K", false, "Validate DoH provider SSL certificates") } @@ -77,6 +78,9 @@ func validateDNSDomain() { func validateDNSProvider() { switch dnsProviderName { + case "googlefront": + dnsProvider = dnsclient.NewGoogleFrontDNS() + break case "google": dnsProvider = dnsclient.NewGoogleDNS() break diff --git a/dnsclient/client.go b/dnsclient/client.go index 6f5f8a0..ee9f0e4 100644 --- a/dnsclient/client.go +++ b/dnsclient/client.go @@ -10,6 +10,12 @@ func NewGoogleDNS() *GoogleDNS { return &GoogleDNS{BaseURL: "https://dns.google.com/resolve"} } +// NewGoogleFrontDNS starts a new Google DNS-over-HTTPS resolver Client +// The Host header for this request is updated in the client itself +func NewGoogleFrontDNS() *GoogleFrontDNS { + return &GoogleFrontDNS{BaseURL: "https://www.google.com/resolve"} +} + // NewCloudFlareDNS starts a new Cloudflare DNS-over-HTTPS resolver Client func NewCloudFlareDNS() *CloudflareDNS { return &CloudflareDNS{BaseURL: "https://cloudflare-dns.com/dns-query"} diff --git a/dnsclient/google_front.go b/dnsclient/google_front.go new file mode 100644 index 0000000..a187c7c --- /dev/null +++ b/dnsclient/google_front.go @@ -0,0 +1,70 @@ +package dnsclient + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + "strconv" + "time" + + "github.com/miekg/dns" +) + +// GoogleFrontDNS is a Client instance resolving using Googles DNS-over-HTTPS service, +// fronted using www.google.com +type GoogleFrontDNS struct { + BaseURL string +} + +// Lookup performs a DNS lookup using Google +func (c *GoogleFrontDNS) Lookup(name string, rType uint16) Response { + + client := http.Client{ + Timeout: time.Second * 20, + } + + req, err := http.NewRequest("GET", c.BaseURL, nil) + if err != nil { + log.Fatal(err) + } + + // Update the Host client header to dns.google.com + // Ref: https://twitter.com/vysecurity/status/1058947074392125440 + req.Host = "dns.google.com" + + q := req.URL.Query() + q.Add("name", name) + q.Add("type", strconv.Itoa(int(rType))) + q.Add("cd", "false") // ignore DNSSEC + // TODO: add random_padding + req.URL.RawQuery = q.Encode() + + res, err := client.Do(req) + if err != nil { + log.Fatal(err) + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Fatal(err) + } + + dnsRequestResponse := requestResponse{} + err = json.Unmarshal(body, &dnsRequestResponse) + if err != nil { + log.Fatal(err) + } + + fout := Response{} + + if len(dnsRequestResponse.Answer) <= 0 { + return fout + } + + fout.TTL = dnsRequestResponse.Answer[0].TTL + fout.Data = dnsRequestResponse.Answer[0].Data + fout.Status = dns.RcodeToString[dnsRequestResponse.Status] + + return fout +}