1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

New provider: INWX (#808)

* adds initial support for INWX

* adds all features to the INWX provider

* allows domain for tests in creds.json for INWX

* runs go generate to update docs for INWX

* fixes formatting with gofmt

* changes goinwx to github.com/nrdcg/goinwx v0.8.0

* simplifies inwx sandbox check

* changes inwx unknown key error to a warning

* adds models.PostProcessRecords for inwx records

* replaces strings.TrimRight with [:-1] to remove final dot for inwx

* adds a comment about the domain creds.json key for the inwx provider

* removes warning for invalid creds.json keys in the inwx provider

* adds TOTP calculation support for inwx

* adds comments to inwxProvider

* improves INWX error messages

* adds additional documentation about the TOTP support for INWX

* adds inwx documentation

* bumps goinwx to 0.8.1 to fix the inwx API
This commit is contained in:
Sven Peter
2020-08-17 14:45:44 +02:00
committed by GitHub
parent 889ed75668
commit f88c60a8f3
12 changed files with 535 additions and 6 deletions

1
OWNERS
View File

@ -11,6 +11,7 @@ providers/gandi_v5 @TomOnTime
# providers/gcloud # providers/gcloud
providers/hexonet @papakai providers/hexonet @papakai
providers/internetbs @pragmaton providers/internetbs @pragmaton
providers/inwx @svenpeter42
providers/linode @koesie10 providers/linode @koesie10
providers/namecheap @captncraig providers/namecheap @captncraig
# providers/namedotcom # providers/namedotcom

View File

@ -29,6 +29,7 @@ Currently supported DNS providers:
- Google DNS - Google DNS
- HEXONET - HEXONET
- Internet.bs - Internet.bs
- INWX
- Linode - Linode
- NS1 - NS1
- Name.com - Name.com

View File

@ -20,6 +20,7 @@
<th class="rotate"><div><span>GCLOUD</span></div></th> <th class="rotate"><div><span>GCLOUD</span></div></th>
<th class="rotate"><div><span>HEXONET</span></div></th> <th class="rotate"><div><span>HEXONET</span></div></th>
<th class="rotate"><div><span>INTERNETBS</span></div></th> <th class="rotate"><div><span>INTERNETBS</span></div></th>
<th class="rotate"><div><span>INWX</span></div></th>
<th class="rotate"><div><span>LINODE</span></div></th> <th class="rotate"><div><span>LINODE</span></div></th>
<th class="rotate"><div><span>NAMECHEAP</span></div></th> <th class="rotate"><div><span>NAMECHEAP</span></div></th>
<th class="rotate"><div><span>NAMEDOTCOM</span></div></th> <th class="rotate"><div><span>NAMEDOTCOM</span></div></th>
@ -85,6 +86,9 @@
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -178,6 +182,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
@ -241,6 +248,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
@ -312,6 +322,9 @@
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i> <i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="INWX does not support the ALIAS or ANAME record type.">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
@ -363,6 +376,9 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="info" data-toggle="tooltip" data-container="body" data-placement="top" title="Supported by INWX but not implemented yet.">
<i class="fa fa-circle-o text-info" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
@ -420,6 +436,9 @@
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
@ -485,6 +504,9 @@
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success" data-toggle="tooltip" data-container="body" data-placement="top" title="PTR records with empty targets are not supported">
<i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
@ -540,6 +562,9 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
@ -595,6 +620,9 @@
<i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i> <i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success" data-toggle="tooltip" data-container="body" data-placement="top" title="SRV records with empty targets are not supported.">
<i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="The namecheap web console allows you to make SRV records, but their api does not let you read or set them"> <td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="The namecheap web console allows you to make SRV records, but their api does not let you read or set them">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i> <i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
@ -662,6 +690,9 @@
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
@ -717,6 +748,9 @@
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
@ -768,6 +802,9 @@
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="INWX only supports a single entry for TXT records">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger"> <td class="danger">
@ -816,6 +853,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -840,6 +878,9 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
@ -877,6 +918,9 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="info" data-toggle="tooltip" data-container="body" data-placement="top" title="DS records are only supported at the apex and require a different API call that hasn&#39;t been implemented yet.">
<i class="fa fa-circle-o text-info" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
@ -928,6 +972,9 @@
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
@ -1001,6 +1048,9 @@
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
<td class="info" data-toggle="tooltip" data-container="body" data-placement="top" title="Supported by INWX but not implemented yet.">
<i class="fa fa-circle-o text-info" aria-hidden="true"></i>
</td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
@ -1085,6 +1135,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
@ -1161,6 +1214,9 @@
<i class="fa fa-circle-o text-info" aria-hidden="true"></i> <i class="fa fa-circle-o text-info" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td class="info"> <td class="info">
<i class="fa fa-circle-o text-info" aria-hidden="true"></i> <i class="fa fa-circle-o text-info" aria-hidden="true"></i>
</td> </td>

99
docs/_providers/inwx.md Normal file
View File

@ -0,0 +1,99 @@
---
name: INWX
layout: default
jsId: INWX
---
# INWX
INWX.de is a Berlin-based domain registrar.
## Configuration
In your `creds.json` file you must provide your INWX
username and password:
{% highlight json %}
{
"inwx":{
"username": "yourUsername",
"password": "yourPassword"
}
}
{% endhighlight %}
### Two factor authentication
If two factor authentication has been enabled you will also need to provide a valid TOTP number.
This can also be done
via an environment variable:
{% highlight json %}
{
"inwx":{
"username": "yourUsername",
"password": "yourPassword",
"totp": "$INWX_TOTP"
}
}
{% endhighlight %}
and then you can run
{% highlight bash %}
$ INWX_TOTP=12345 dnscontrol preview
{% endhighlight %}
It is also possible to directly provide the shared TOTP secret using the key "totp-key" in `creds.json`.
This secret is only shown once when two factor authentication is enabled and you'll have to make sure to write it down then.
**Important Notes**:
* Anyone with access to this `creds.json` file will have *full* access to your INWX account and will be able to transfer and/or delete your domains
* Storing the shared secret together with the password weakens two factor authentication because both factors are stored in a single place.
{% highlight json %}
{
"inwx":{
"username": "yourUsername",
"password": "yourPassword",
"totp-key": "yourTOTPSharedSecret"
}
}
{% endhighlight %}
### Sandbox
You can optionally also specify sandbox with a value of 1 to
redirect all requests to the sandbox API instead:
{% highlight json %}
{
"inwx":{
"username": "yourUsername",
"password": "yourPassword",
"sandbox": "1"
}
}
{% endhighlight %}
If sandbox is omitted or set to any other value the production
API will be used.
## Metadata
This provider does not recognize any special metadata fields unique to
INWX.
## Usage
Example Javascript for `example.tld` registered with INWX
and delegated to CloudFlare:
{% highlight js %}
var regInwx = NewRegistrar('inwx', 'INWX')
var dnsCF = NewDnsProvider('cloudflare', 'CLOUDFLAREAPI')
D("example.tld", regInwx, DnsProvider(dnsCF),
A("test","1.2.3.4")
);
{%endhighlight%}

View File

@ -80,6 +80,7 @@ Maintainers of contributed providers:
* `GANDI_V5` @TomOnTime * `GANDI_V5` @TomOnTime
* `HEXONET` @papakai * `HEXONET` @papakai
* `INTERNETBS` @pragmaton * `INTERNETBS` @pragmaton
* `INWX` @svenpeter42
* `LINODE` @koesie10 * `LINODE` @koesie10
* `NAMECHEAP` @captncraig * `NAMECHEAP` @captncraig
* `NETCUP` @kordianbruck * `NETCUP` @kordianbruck

4
go.mod
View File

@ -30,9 +30,11 @@ require (
github.com/mittwald/go-powerdns v0.4.0 github.com/mittwald/go-powerdns v0.4.0
github.com/mjibson/esc v0.2.0 github.com/mjibson/esc v0.2.0
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04
github.com/nrdcg/goinwx v0.8.1
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
github.com/philhug/opensrs-go v0.0.0-20171126225031-9dfa7433020d github.com/philhug/opensrs-go v0.0.0-20171126225031-9dfa7433020d
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.2.0
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
github.com/sergi/go-diff v1.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect
@ -48,7 +50,7 @@ require (
golang.org/x/mod v0.3.0 // indirect golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20200625001655-4c5254603344 golang.org/x/net v0.0.0-20200625001655-4c5254603344
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/tools v0.0.0-20200626032829-bcbc01e07a20 // indirect golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207 // indirect
google.golang.org/api v0.28.0 google.golang.org/api v0.28.0
google.golang.org/appengine v1.6.6 // indirect google.golang.org/appengine v1.6.6 // indirect
gopkg.in/ini.v1 v1.42.0 // indirect gopkg.in/ini.v1 v1.42.0 // indirect

17
go.sum
View File

@ -61,6 +61,8 @@ github.com/aws/aws-sdk-go v1.32.10/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZve
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 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 h1:sIv3xbwhhAG94a62Q/rrSBtrWcXiYgldNOeqifyKSgo=
github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8/go.mod h1:bqqNsI2akL+lLWyApkYY0cxquWPKwEBU0Wd3chi3TEg= github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8/go.mod h1:bqqNsI2akL+lLWyApkYY0cxquWPKwEBU0Wd3chi3TEg=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -92,6 +94,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/exoscale/egoscale v0.23.0 h1:hoUDzrO8yNoobNdnrRvlRFjfg3Ng0vQTrv6bXRJu6z0= github.com/exoscale/egoscale v0.23.0 h1:hoUDzrO8yNoobNdnrRvlRFjfg3Ng0vQTrv6bXRJu6z0=
github.com/exoscale/egoscale v0.23.0/go.mod h1:hRo78jkjkCDKpivQdRBEpNYF5+cVpCJCPDg2/r45KaY= github.com/exoscale/egoscale v0.23.0/go.mod h1:hRo78jkjkCDKpivQdRBEpNYF5+cVpCJCPDg2/r45KaY=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/go-acme/lego v2.7.2+incompatible h1:ThhpPBgf6oa9X/vRd0kEmWOsX7+vmYdckmGZSb+FEp0= github.com/go-acme/lego v2.7.2+incompatible h1:ThhpPBgf6oa9X/vRd0kEmWOsX7+vmYdckmGZSb+FEp0=
github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
@ -190,6 +193,8 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU= github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU=
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8= github.com/hashicorp/vault/sdk v0.1.13 h1:mOEPeOhT7jl0J4AMl1E705+BcmeRs1VmKNb9F0sMLy8=
@ -208,6 +213,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc=
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -226,6 +233,8 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mittwald/go-powerdns v0.4.0 h1:vEl2+4JINusy5NF8weObVRCuvHv8wqNBVMPZSQWq9zo= github.com/mittwald/go-powerdns v0.4.0 h1:vEl2+4JINusy5NF8weObVRCuvHv8wqNBVMPZSQWq9zo=
github.com/mittwald/go-powerdns v0.4.0/go.mod h1:bI/sZBAWyTViDknOTp19VfDxVEnh1U7rWPx2aRKtlzg= github.com/mittwald/go-powerdns v0.4.0/go.mod h1:bI/sZBAWyTViDknOTp19VfDxVEnh1U7rWPx2aRKtlzg=
@ -235,6 +244,8 @@ github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAA
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/nrdcg/goinwx v0.8.1 h1:20EQ/JaGFnSKwiDH2JzjIpicffl3cPk6imJBDqVBVtU=
github.com/nrdcg/goinwx v0.8.1/go.mod h1:tILVc10gieBp/5PMvbcYeXM6pVQ+c9jxDZnpaR1UW7c=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 h1:37VE5TYj2m/FLA9SNr4z0+A0JefvTmR60Zwf8XSEV7c= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014 h1:37VE5TYj2m/FLA9SNr4z0+A0JefvTmR60Zwf8XSEV7c=
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
@ -249,6 +260,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o=
github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU=
@ -295,6 +308,7 @@ github.com/vultr/govultr v0.2.0 h1:CZSNNCk+PHz9hzmfH2PFGkDgc3qNetwZqtcaqL8shlg=
github.com/vultr/govultr v0.2.0/go.mod h1:glSLa57Jdj5s860EEc6+DEBbb/t3aUOKnB4gVPmDVlQ= github.com/vultr/govultr v0.2.0/go.mod h1:glSLa57Jdj5s860EEc6+DEBbb/t3aUOKnB4gVPmDVlQ=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -382,6 +396,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -455,6 +470,8 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200626032829-bcbc01e07a20 h1:q+ysxVHVQNTVHgzwjuk4ApAILRbfOLARfnEaqCIBR6A= golang.org/x/tools v0.0.0-20200626032829-bcbc01e07a20 h1:q+ysxVHVQNTVHgzwjuk4ApAILRbfOLARfnEaqCIBR6A=
golang.org/x/tools v0.0.0-20200626032829-bcbc01e07a20/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200626032829-bcbc01e07a20/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207 h1:8Kg+JssU1jBZs8GIrL5pl4nVyaqyyhdmHAR4D1zGErg=
golang.org/x/tools v0.0.0-20200811215021-48a8ffc5b207/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -623,7 +623,7 @@ func makeTests(t *testing.T) []*TestGroup {
), ),
testgroup("Null MX", testgroup("Null MX",
not("AZURE_DNS", "GANDI_V5", "NAMEDOTCOM", "DIGITALOCEAN", "NETCUP", "DNSIMPLE"), // These providers don't support RFC 7505 not("AZURE_DNS", "GANDI_V5", "INWX", "NAMEDOTCOM", "DIGITALOCEAN", "NETCUP", "DNSIMPLE"), // These providers don't support RFC 7505
tc("Null MX", mx("@", 0, ".")), tc("Null MX", mx("@", 0, ".")),
), ),
@ -656,14 +656,14 @@ func makeTests(t *testing.T) []*TestGroup {
), ),
testgroup("ws TXT", testgroup("ws TXT",
not("CLOUDFLAREAPI", "NAMEDOTCOM"), not("CLOUDFLAREAPI", "INWX", "NAMEDOTCOM"),
// These providers strip whitespace at the end of TXT records. // These providers strip whitespace at the end of TXT records.
// TODO(tal): Add a check for this in normalize/validate.go // TODO(tal): Add a check for this in normalize/validate.go
tc("Change a TXT with ws at end", txt("foo", "with space at end ")), tc("Change a TXT with ws at end", txt("foo", "with space at end ")),
), ),
testgroup("empty TXT", testgroup("empty TXT",
not("CLOUDFLAREAPI", "NETCUP"), not("CLOUDFLAREAPI", "INWX", "NETCUP"),
tc("TXT with empty str", txt("foo1", "")), tc("TXT with empty str", txt("foo1", "")),
// https://github.com/StackExchange/dnscontrol/issues/598 // https://github.com/StackExchange/dnscontrol/issues/598
// We decided that permitting the TXT target to be an empty // We decided that permitting the TXT target to be an empty
@ -786,7 +786,7 @@ func makeTests(t *testing.T) []*TestGroup {
tc("Change Weight", srv("_sip._tcp", 52, 62, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")), tc("Change Weight", srv("_sip._tcp", 52, 62, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")),
tc("Change Port", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")), tc("Change Port", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")),
), ),
testgroup("SRV w/ null target", requires(providers.CanUseSRV), not("EXOSCALE", "HEXONET", "NAMEDOTCOM"), testgroup("SRV w/ null target", requires(providers.CanUseSRV), not("EXOSCALE", "HEXONET", "INWX", "NAMEDOTCOM"),
tc("Null Target", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, ".")), tc("Null Target", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, ".")),
), ),

View File

@ -123,5 +123,11 @@
"VULTR": { "VULTR": {
"domain": "$VULTR_DOMAIN", "domain": "$VULTR_DOMAIN",
"token": "$VULTR_TOKEN" "token": "$VULTR_TOKEN"
},
"INWX": {
"username": "$INWX_USER",
"password": "$INWX_PASSWORD",
"domain": "$INWX_DOMAIN",
"sandbox": "1",
} }
} }

View File

@ -17,6 +17,7 @@ import (
_ "github.com/StackExchange/dnscontrol/v3/providers/gcloud" _ "github.com/StackExchange/dnscontrol/v3/providers/gcloud"
_ "github.com/StackExchange/dnscontrol/v3/providers/hexonet" _ "github.com/StackExchange/dnscontrol/v3/providers/hexonet"
_ "github.com/StackExchange/dnscontrol/v3/providers/internetbs" _ "github.com/StackExchange/dnscontrol/v3/providers/internetbs"
_ "github.com/StackExchange/dnscontrol/v3/providers/inwx"
_ "github.com/StackExchange/dnscontrol/v3/providers/linode" _ "github.com/StackExchange/dnscontrol/v3/providers/linode"
_ "github.com/StackExchange/dnscontrol/v3/providers/namecheap" _ "github.com/StackExchange/dnscontrol/v3/providers/namecheap"
_ "github.com/StackExchange/dnscontrol/v3/providers/namedotcom" _ "github.com/StackExchange/dnscontrol/v3/providers/namedotcom"

View File

@ -29,7 +29,7 @@ const (
// CanUsePTR indicates the provider can handle PTR records // CanUsePTR indicates the provider can handle PTR records
CanUsePTR CanUsePTR
// CanUseNAPTR indicates the provider can handle PTR records // CanUseNAPTR indicates the provider can handle NAPTR records
CanUseNAPTR CanUseNAPTR
// CanUseSRV indicates the provider can handle SRV records // CanUseSRV indicates the provider can handle SRV records

View File

@ -0,0 +1,345 @@
package inwx
import (
"encoding/json"
"fmt"
"sort"
"strings"
"time"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
"github.com/StackExchange/dnscontrol/v3/providers"
"github.com/nrdcg/goinwx"
"github.com/pquerna/otp/totp"
)
/*
INWX Registrar and DNS provider
Info required in `creds.json`:
- username
- password
Either of the following settings is required when two factor authentication is enabled:
- totp (TOTP code if 2FA is enabled; best specified as an env variable)
- totp-key (shared TOTP secret used to generate a valid TOTP code; not recommended since
this effectively defeats the purpose of two factor authentication by storing
both factors at the same place)
Additional settings available in `creds.json`:
- sandbox (set to 1 to use the sandbox API from INWX)
*/
// InwxApi is a thin wrapper around goinwx.Client.
type InwxApi struct {
client *goinwx.Client
sandbox bool
}
// InwxDefaultNs contains the default INWX nameservers.
var InwxDefaultNs = []string{"ns.inwx.de", "ns2.inwx.de", "ns3.inwx.eu", "ns4.inwx.com", "ns5.inwx.net"}
// InwxSandboxDefaultNs contains the default INWX nameservers in the sandbox / OTE.
var InwxSandboxDefaultNs = []string{"ns.ote.inwx.de", "ns2.ote.inwx.de"}
// features is used to let dnscontrol know which features are supported by INWX.
var features = providers.DocumentationNotes{
providers.CanUseAlias: providers.Cannot("INWX does not support the ALIAS or ANAME record type."),
providers.CanUseCAA: providers.Can(),
providers.CanUseDS: providers.Unimplemented("DS records are only supported at the apex and require a different API call that hasn't been implemented yet."),
providers.CanUsePTR: providers.Can("PTR records with empty targets are not supported"),
providers.CanUseNAPTR: providers.Can(),
providers.CanUseSRV: providers.Can("SRV records with empty targets are not supported."),
providers.CanUseSSHFP: providers.Can(),
providers.CanUseTLSA: providers.Can(),
providers.CanUseTXTMulti: providers.Cannot("INWX only supports a single entry for TXT records"),
providers.CanAutoDNSSEC: providers.Unimplemented("Supported by INWX but not implemented yet."),
providers.DocOfficiallySupported: providers.Cannot(),
providers.DocDualHost: providers.Can(),
providers.DocCreateDomains: providers.Unimplemented("Supported by INWX but not implemented yet."),
providers.CanGetZones: providers.Can(),
providers.CanUseAzureAlias: providers.Cannot(),
}
// init registers the registrar and the domain service provider with dnscontrol.
func init() {
providers.RegisterRegistrarType("INWX", newInwxReg)
providers.RegisterDomainServiceProviderType("INWX", newInwxDsp, features)
}
// getOTP either returns the TOTPValue or uses TOTPKey and the current time to generate a valid TOTPValue.
func getOTP(TOTPValue string, TOTPKey string) (string, error) {
if TOTPValue != "" {
return TOTPValue, nil
} else if TOTPKey != "" {
tan, err := totp.GenerateCode(TOTPKey, time.Now())
if err != nil {
return "", fmt.Errorf("INWX: Unable to generate TOTP from totp-key: %v", err)
}
return tan, nil
} else {
return "", fmt.Errorf("INWX: two factor authentication required but no TOTP configured.")
}
}
// loginHelper tries to login and then unlocks the account using two factor authentication if required.
func (api *InwxApi) loginHelper(TOTPValue string, TOTPKey string) error {
resp, err := api.client.Account.Login()
if err != nil {
return fmt.Errorf("INWX: Unable to login")
}
switch TFA := resp.TFA; TFA {
case "0":
if TOTPKey != "" || TOTPValue != "" {
fmt.Printf("INWX: Warning: no TOTP requested by INWX but totp/totp-key is present in `creds.json`\n")
}
case "GOOGLE-AUTH":
tan, err := getOTP(TOTPValue, TOTPKey)
if err != nil {
return err
}
err = api.client.Account.Unlock(tan)
if err != nil {
return fmt.Errorf("INWX: Could not unlock account: %w.", err)
}
default:
return fmt.Errorf("INWX: Unknown two factor authentication mode `%s` has been requested.", resp.TFA)
}
return nil
}
// newInwx initializes InwxApi and create a session.
func newInwx(m map[string]string) (*InwxApi, error) {
username, password := m["username"], m["password"]
TOTPValue, TOTPKey := m["totp"], m["totp-key"]
sandbox := m["sandbox"] == "1"
if username == "" {
return nil, fmt.Errorf("INWX: username must be provided.")
}
if password == "" {
return nil, fmt.Errorf("INWX: password must be provided.")
}
if TOTPValue != "" && TOTPKey != "" {
return nil, fmt.Errorf("INWX: totp and totp-key must not be specified at the same time.")
}
opts := &goinwx.ClientOptions{Sandbox: sandbox}
client := goinwx.NewClient(username, password, opts)
api := &InwxApi{client: client, sandbox: sandbox}
err := api.loginHelper(TOTPValue, TOTPKey)
if err != nil {
return nil, err
}
return api, nil
}
// newInwxReg is called to initialize the INWX registrar provider.
func newInwxReg(m map[string]string) (providers.Registrar, error) {
return newInwx(m)
}
// new InwxDsp is called to initialize the INWX domain service provider.
func newInwxDsp(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
return newInwx(m)
}
// makeNameserverRecordRequest is a helper function used to convert a RecordConfig to an INWX NS Record Request.
func makeNameserverRecordRequest(domain string, rec *models.RecordConfig) *goinwx.NameserverRecordRequest {
content := rec.GetTargetField()
req := &goinwx.NameserverRecordRequest{
Domain: domain,
Type: rec.Type,
Content: content,
Name: rec.GetLabel(),
TTL: int(rec.TTL),
}
switch rType := rec.Type; rType {
/* INWX is a little bit special for CNAME,NS,MX and SRV records:
The API will not accept any target with a final dot but will
instead always add this final dot internally.
Records with empty targets (i.e. records with target ".")
are not allowed.
*/
case "CNAME", "NS":
req.Content = content[:len(content)-1]
case "MX":
req.Priority = int(rec.MxPreference)
req.Content = content[:len(content)-1]
case "SRV":
req.Priority = int(rec.SrvPriority)
req.Content = fmt.Sprintf("%d %d %v", rec.SrvWeight, rec.SrvPort, content[:len(content)-1])
default:
req.Content = rec.GetTargetCombined()
}
return req
}
// createRecord is used by GetDomainCorrections to create a new record.
func (api *InwxApi) createRecord(domain string, rec *models.RecordConfig) error {
req := makeNameserverRecordRequest(domain, rec)
_, err := api.client.Nameservers.CreateRecord(req)
return err
}
// updateRecord is used by GetDomainCorrections to update an existing record.
func (api *InwxApi) updateRecord(RecordID int, rec *models.RecordConfig) error {
req := makeNameserverRecordRequest("", rec)
err := api.client.Nameservers.UpdateRecord(RecordID, req)
return err
}
// deleteRecord is used by GetDomainCorrections to delete a record.
func (api *InwxApi) deleteRecord(RecordID int) error {
return api.client.Nameservers.DeleteRecord(RecordID)
}
// GetDomainCorrections finds the currently existing records and returns the corrections required to update them.
func (api *InwxApi) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc.Punycode()
foundRecords, err := api.GetZoneRecords(dc.Name)
if err != nil {
return nil, err
}
models.PostProcessRecords(foundRecords)
differ := diff.New(dc)
_, create, del, mod := differ.IncrementalDiff(foundRecords)
corrections := []*models.Correction{}
for _, d := range create {
des := d.Desired
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return api.createRecord(dc.Name, des) },
})
}
for _, d := range del {
existingID := d.Existing.Original.(goinwx.NameserverRecord).ID
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return api.deleteRecord(existingID) },
})
}
for _, d := range mod {
rec := d.Desired
existingID := d.Existing.Original.(goinwx.NameserverRecord).ID
corrections = append(corrections, &models.Correction{
Msg: d.String(),
F: func() error { return api.updateRecord(existingID, rec) },
})
}
return corrections, nil
}
// GetNameservers returns the default nameservers for INWX.
func (api *InwxApi) GetNameservers(domain string) ([]*models.Nameserver, error) {
if api.sandbox {
return models.ToNameservers(InwxSandboxDefaultNs)
} else {
return models.ToNameservers(InwxDefaultNs)
}
}
// GetZoneRecords receives the current records from Inwx and converts them to models.RecordConfig.
func (api *InwxApi) GetZoneRecords(domain string) (models.Records, error) {
info, err := api.client.Nameservers.Info(&goinwx.NameserverInfoRequest{Domain: domain})
if err != nil {
return nil, err
}
var records = []*models.RecordConfig{}
for _, record := range info.Records {
if record.Type == "SOA" {
continue
}
/* INWX is a little bit special for CNAME,NS,MX and SRV records:
The API will not accept any target with a final dot but will
instead always add this final dot internally.
Records with empty targets (i.e. records with target ".")
are not allowed.
*/
if record.Type == "CNAME" || record.Type == "MX" || record.Type == "NS" || record.Type == "SRV" {
record.Content = record.Content + "."
}
rc := &models.RecordConfig{
TTL: uint32(record.TTL),
Original: record,
}
rc.SetLabelFromFQDN(record.Name, domain)
switch rType := record.Type; rType {
case "MX":
err = rc.SetTargetMX(uint16(record.Priority), record.Content)
case "SRV":
err = rc.SetTargetSRVPriorityString(uint16(record.Priority), record.Content)
default:
err = rc.PopulateFromString(rType, record.Content, domain)
}
if err != nil {
panic(fmt.Errorf("INWX: unparsable record received: %w", err))
}
records = append(records, rc)
}
return records, nil
}
// updateNameservers is used by GetRegistrarCorrections to update the domain's nameservers.
func (api *InwxApi) updateNameservers(ns []string, domain string) func() error {
return func() error {
request := &goinwx.DomainUpdateRequest{
Domain: domain,
Nameservers: ns,
}
_, err := api.client.Domains.Update(request)
return err
}
}
// GetRegistrarCorrections is part of the registrar provider and determines if the nameservers have to be updated.
func (api *InwxApi) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
info, err := api.client.Domains.Info(dc.Name, 0)
if err != nil {
return nil, err
}
sort.Strings(info.Nameservers)
foundNameservers := strings.Join(info.Nameservers, ",")
expected := []string{}
for _, ns := range dc.Nameservers {
expected = append(expected, ns.Name)
}
sort.Strings(expected)
expectedNameservers := strings.Join(expected, ",")
if foundNameservers != expectedNameservers {
return []*models.Correction{
{
Msg: fmt.Sprintf("Update nameservers %s -> %s", foundNameservers, expectedNameservers),
F: api.updateNameservers(expected, dc.Name),
},
}, nil
}
return nil, nil
}