diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html
index 0e21808c1..a2a965dd0 100644
--- a/docs/_includes/matrix.html
+++ b/docs/_includes/matrix.html
@@ -15,6 +15,7 @@
DESEC |
DIGITALOCEAN |
DNSIMPLE |
+ DNSOVERHTTPS |
EXOSCALE |
GANDI_V5 |
GCLOUD |
@@ -72,6 +73,9 @@
|
+
+
+ |
|
@@ -153,6 +157,9 @@
|
+
+
+ |
|
@@ -240,6 +247,9 @@
|
+
+
+ |
|
@@ -321,6 +331,7 @@
|
+ |
|
@@ -387,6 +398,7 @@
|
|
|
+ |
|
@@ -439,6 +451,7 @@
|
+ |
|
@@ -510,6 +523,7 @@
|
+ |
|
@@ -582,6 +596,7 @@
|
|
|
+ |
|
@@ -632,6 +647,7 @@
|
+ |
|
@@ -710,6 +726,7 @@
|
+ |
|
@@ -768,6 +785,7 @@
|
+ |
|
@@ -826,6 +844,7 @@
|
+ |
|
@@ -878,6 +897,7 @@
|
|
|
+ |
|
@@ -917,6 +937,7 @@
|
|
|
+ |
|
@@ -955,6 +976,7 @@
|
|
|
+ |
|
@@ -1003,6 +1025,7 @@
|
+ |
|
@@ -1081,6 +1104,9 @@
|
+
+
+ |
|
@@ -1168,6 +1194,9 @@
|
+
+
+ |
|
@@ -1252,6 +1281,7 @@
|
+ |
|
diff --git a/docs/_providers/doh.md b/docs/_providers/doh.md
new file mode 100644
index 000000000..010f35382
--- /dev/null
+++ b/docs/_providers/doh.md
@@ -0,0 +1,37 @@
+---
+name: DNS-over-HTTPS
+title: DNS-over-HTTPS Provider
+layout: default
+jsId: DNSOVERHTTPS
+---
+# DNS-over-HTTPS Provider
+
+This is a read-only/monitoring "registrar". It does a DNS NS lookup to confirm the nameserver servers are correct. This "registrar" is unable to update the NS servers but will alert you if they are incorrect. A common use of this provider is when the domain is with a registrar that does not have an API.
+
+## Configuration
+The DNS-over-HTTPS provider does not require anything in `creds.json`. By default, it uses Google Public DNS however you may configure an alternative RFC 8484 DoH provider.
+
+{% highlight json %}
+{
+ "DNS-over-HTTPS": {
+ "host": "cloudflare-dns.com"
+ }
+}
+{% endhighlight %}
+
+Some common DoH providers are `cloudflare-dns.com` [Cloudflare](https://developers.cloudflare.com/1.1.1.1/dns-over-https), `9.9.9.9` [Quad9](https://www.quad9.net/about/), and `dns.google` [Google Public DNS](https://developers.google.com/speed/public-dns/docs/doh)
+
+## Metadata
+This provider does not recognize any special metadata fields unique to Internet.bs.
+
+## Usage
+Example Javascript:
+
+{% highlight js %}
+var REG_MONITOR = NewRegistrar('DNS-over-HTTPS', 'DNSOVERHTTPS');
+
+D("example.com", REG_MONITOR,
+ NNAMESERVER("ns1.example.com."),
+ NNAMESERVER("ns2.example.com."),
+);
+{% endhighlight %}
diff --git a/go.mod b/go.mod
index 339fa3927..267f9b35e 100644
--- a/go.mod
+++ b/go.mod
@@ -13,6 +13,7 @@ require (
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
github.com/andybalholm/cascadia v1.2.0 // indirect
github.com/aws/aws-sdk-go v1.34.13
+ github.com/babolivier/go-doh-client v0.0.0-20200723140836-2f86c709ac4a
github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8
github.com/cenkalti/backoff v2.1.1+incompatible // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
diff --git a/go.sum b/go.sum
index 1f855b4aa..fc338f8d0 100644
--- a/go.sum
+++ b/go.sum
@@ -80,6 +80,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.34.13 h1:wwNWSUh4FGJxXVOVVNj2lWI8wTe5hK8sGWlK7ziEcgg=
github.com/aws/aws-sdk-go v1.34.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
+github.com/babolivier/go-doh-client v0.0.0-20200723140836-2f86c709ac4a h1:ecIUxgsssZnvVeNNsreMlWe6Y7RCfcoayiO7xI0+bC0=
+github.com/babolivier/go-doh-client v0.0.0-20200723140836-2f86c709ac4a/go.mod h1:WorCk0sF6w5RjJorPxPL80q35XcMAPew90L8WSZxobY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8 h1:sIv3xbwhhAG94a62Q/rrSBtrWcXiYgldNOeqifyKSgo=
github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8/go.mod h1:bqqNsI2akL+lLWyApkYY0cxquWPKwEBU0Wd3chi3TEg=
diff --git a/providers/_all/all.go b/providers/_all/all.go
index e7aa7cde7..8a9800a4a 100644
--- a/providers/_all/all.go
+++ b/providers/_all/all.go
@@ -12,6 +12,7 @@ import (
_ "github.com/StackExchange/dnscontrol/v3/providers/desec"
_ "github.com/StackExchange/dnscontrol/v3/providers/digitalocean"
_ "github.com/StackExchange/dnscontrol/v3/providers/dnsimple"
+ _ "github.com/StackExchange/dnscontrol/v3/providers/doh"
_ "github.com/StackExchange/dnscontrol/v3/providers/exoscale"
_ "github.com/StackExchange/dnscontrol/v3/providers/gandi_v5"
_ "github.com/StackExchange/dnscontrol/v3/providers/gcloud"
diff --git a/providers/doh/api.go b/providers/doh/api.go
new file mode 100644
index 000000000..98602c7e9
--- /dev/null
+++ b/providers/doh/api.go
@@ -0,0 +1,36 @@
+package doh
+
+import (
+ "fmt"
+ "sort"
+
+ "github.com/babolivier/go-doh-client"
+)
+
+type api struct {
+ host string
+}
+
+func (c *api) getNameservers(domain string) ([]string, error) {
+ resolver := doh.Resolver{
+ Host: c.host,
+ Class: doh.IN,
+ }
+
+ // Perform a NS lookup
+ nss, _, err := resolver.LookupNS(domain)
+ if err != nil {
+ return nil, fmt.Errorf("failed fetching nameservers list (DNS-over-HTTPS): %s", err)
+ }
+
+ ns := []string{}
+ for _, res := range nss {
+ ns = append(ns, res.Host)
+ }
+ sort.Strings(ns)
+ return ns, nil
+}
+
+func (c *api) updateNameservers(ns []string, domain string) error {
+ return fmt.Errorf("DNS-over-HTTPS 'Registrar' is read only, changes must be applied to %s manually", domain)
+}
diff --git a/providers/doh/dohProvider.go b/providers/doh/dohProvider.go
new file mode 100644
index 000000000..357eccf71
--- /dev/null
+++ b/providers/doh/dohProvider.go
@@ -0,0 +1,61 @@
+package doh
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/StackExchange/dnscontrol/v3/models"
+ "github.com/StackExchange/dnscontrol/v3/providers"
+)
+
+/*
+
+DNS over HTTPS 'Registrar':
+
+Info required in `creds.json`:
+ - host DNS over HTTPS host (eg 9.9.9.9)
+*/
+
+func init() {
+ providers.RegisterRegistrarType("DNSOVERHTTPS", newDNSOverHTTPS)
+}
+
+func newDNSOverHTTPS(m map[string]string) (providers.Registrar, error) {
+ api := &api{
+ host: m["host"],
+ }
+ if api.host == "" {
+ api.host = "dns.google"
+ }
+ return api, nil
+}
+
+// GetRegistrarCorrections gathers corrections that would being n to match dc.
+func (c *api) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
+ nss, err := c.getNameservers(dc.Name)
+ if err != nil {
+ return nil, err
+ }
+ foundNameservers := strings.Join(nss, ",")
+
+ expected := []string{}
+ for _, ns := range dc.Nameservers {
+ expected = append(expected, ns.Name)
+ }
+ sort.Strings(expected)
+ expectedNameservers := strings.Join(expected, ",")
+
+ if foundNameservers == expectedNameservers {
+ return nil, nil
+ }
+
+ return []*models.Correction{
+ {
+ Msg: fmt.Sprintf("Update nameservers %s -> %s", foundNameservers, expectedNameservers),
+ F: func() error {
+ return c.updateNameservers(expected, dc.Name)
+ },
+ },
+ }, nil
+}