mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
NEW PROVIDER: MSDNS (#1005)
* New provider * Add support for SRV records * Modify ACTIVEDIRECTORY_PS provider to warn that it is deprecated.
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -3,6 +3,7 @@
|
||||
# Git itself
|
||||
.gitattributes eol=lf
|
||||
.gitigore eol=lf
|
||||
OWNERS eol=lf
|
||||
|
||||
# dnscontrol files are unix line-endings:
|
||||
*.md text eol=lf
|
||||
|
1
OWNERS
1
OWNERS
@ -15,6 +15,7 @@ providers/hetzner @das7pad
|
||||
providers/hexonet @papakai
|
||||
providers/internetbs @pragmaton
|
||||
providers/inwx @svenpeter42
|
||||
providers/msdns @tlimoncelli
|
||||
providers/linode @koesie10
|
||||
providers/namecheap @captncraig
|
||||
# providers/namedotcom
|
||||
|
@ -17,7 +17,7 @@ Windows). The provider model is extensible, so more providers can be added.
|
||||
Currently supported DNS providers:
|
||||
- AWS Route 53
|
||||
- AXFR+DDNS
|
||||
- Active Directory
|
||||
- Active Directory (Deprecated, see Microsoft DNS)
|
||||
- Azure DNS
|
||||
- BIND
|
||||
- ClouDNS
|
||||
@ -34,6 +34,7 @@ Currently supported DNS providers:
|
||||
- INWX
|
||||
- Internet.bs
|
||||
- Linode
|
||||
- Microsoft Windows Server DNS Server
|
||||
- NS1
|
||||
- Name.com
|
||||
- Namecheap
|
||||
|
@ -4,6 +4,14 @@ layout: default
|
||||
jsId: ACTIVEDIRECTORY_PS
|
||||
title: ActiveDirectory_PS Provider
|
||||
---
|
||||
|
||||
# WARNING:
|
||||
|
||||
WARNING: This provider is deprecated and will eventually be removed.
|
||||
Please switch to MSDNS. It is more modern and reliable. The
|
||||
`creds.json` fields changed names; otherwise it should be an
|
||||
uneventful upgrade.
|
||||
|
||||
# ActiveDirectory_PS Provider
|
||||
This provider updates an Microsoft Active Directory server DNS server. It interacts with AD via PowerShell commands that are generated and executed on the local machine. This means that DNSControl must be run on a Windows host. This driver automatically deactivates itself when run on non-Windows systems.
|
||||
|
||||
@ -17,7 +25,7 @@ To activate this mode, set `"fakeps":"true"` inside your credentials file for th
|
||||
|
||||
## Configuration
|
||||
|
||||
The `ActiveDirectory_PS` provider reads an `ADServer` setting from `creds.json` to know the name of the ActiveDirectory DNS Server to update.
|
||||
The `ActiveDirectory_PS` provider reads an `ADServer` setting from `creds.json` to know the name of the ActiveDirectory DNS Server to update.
|
||||
|
||||
{% highlight javascript %}
|
||||
{
|
||||
|
96
docs/_providers/msdns.md
Normal file
96
docs/_providers/msdns.md
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
name: Microsoft DNS Server (Windows Server)
|
||||
layout: default
|
||||
jsId: MSDNS
|
||||
title: Microsoft DNS Server on Microsoft Windows Server
|
||||
---
|
||||
|
||||
# Microsoft DNS Server on Microsoft Windows Server
|
||||
|
||||
This provider updates a Microsoft DNS server.
|
||||
|
||||
It interacts with the server via PowerShell commands. As a result, DNSControl
|
||||
must be run on Windows and will automatically disable itself when run on
|
||||
non-Windows systems.
|
||||
|
||||
DNSControl will use `New-PSSession` to execute the commands remotely if
|
||||
`computername` is set in `creds.json` (see below).
|
||||
|
||||
This provider will replace `ACTIVEDIRECTORY_PS` which is deprecated.
|
||||
|
||||
# Caveats
|
||||
|
||||
* Two systems updating a zone is never a good idea. If Windows Dynamic
|
||||
DNS and DNSControl are both updating a zone, there will be
|
||||
unhappiness. DNSControl will blindly remove the dynamic records
|
||||
unless precautions such as `IGNORE*` and `NO_PURGE` are in use.
|
||||
* This is a new provider and has not been tested extensively,
|
||||
especially the `pssession` feature.
|
||||
|
||||
# Running on Non-Windows systems
|
||||
|
||||
Currently this driver disables itself when run on Non-Windows systems.
|
||||
|
||||
It should be possible for non-Windows hosts with PowerShell Core installed to
|
||||
execute commands remotely via SSH. The module used to talk to PowerShell
|
||||
supports this. It should be easy to implement. Volunteers requested.
|
||||
|
||||
## Configuration
|
||||
|
||||
The `ActiveDirectory_PS` provider reads an `computername` setting from
|
||||
`creds.json` to know the name of the ActiveDirectory DNS Server to run the commands on.
|
||||
Otherwise
|
||||
|
||||
{% highlight javascript %}
|
||||
{
|
||||
"msdns": {
|
||||
"dnsserver": "ny-dc01",
|
||||
"pssession": "mywindowshost"
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
An example DNS configuration:
|
||||
|
||||
{% highlight javascript %}
|
||||
var REG_NONE = NewRegistrar('none', 'NONE')
|
||||
var MSDNS = NewDnsProvider("msdns", "MSDNS");
|
||||
|
||||
D('example.tld', REG_NONE, DnsProvider(MSDNS),
|
||||
A("test","1.2.3.4")
|
||||
)
|
||||
{% endhighlight %}
|
||||
|
||||
|
||||
# Converting from `ACTIVEDIRECTORY_PS`
|
||||
|
||||
If you were using the `ACTIVEDIRECTORY_PS` provider and are switching to `MSDNS`, make the following changes:
|
||||
|
||||
1. In `dnsconfig.js`, change `ACTIVEDIRECTORY_PS` to `MSDNS` in any `NewDnsProvider()` calls.
|
||||
|
||||
2. In `creds.json`: Since unused fields are quietly ignored, it is
|
||||
safe to list both the old and new options:
|
||||
a. Add a field "dnsserver" with the DNS server's name. (OPTIONAL if dnscontrol is run on the DNS server.)
|
||||
b. If the PowerShell commands need to be run on a different host using a `PSSession`, add `pssession: "remoteserver",` where `remoteserver` is the name of the server where the PowerShell commands should run.
|
||||
c. The MSDNS provider will quietly ignore `fakeps`, `pslog` and `psout`. Feel free to leave them in `creds.json` until you are sure you aren't going back to the old provider.
|
||||
|
||||
During the transition your `creds.json` file might look like:
|
||||
|
||||
{% highlight javascript %}
|
||||
{
|
||||
"msdns": {
|
||||
"ADServer": "ny-dc01", << Delete these after you have
|
||||
"fakeps": "true", << verified that MSDNS works
|
||||
"pslog": "log.txt", << properly.
|
||||
"psout": "out.txt",
|
||||
"dnsserver": "ny-dc01",
|
||||
"pssession": "mywindowshost"
|
||||
}
|
||||
}
|
||||
{% endhighlight %}
|
||||
|
||||
3. Run `dnscontrol preview` to make sure the provider works as expected.
|
||||
|
||||
4. If for any reason you need to revert, simply change `dnsconfig.js` to refer to `ACTIVEDIRECTORY_PS` again (or use `git` commands). If you are reverting because you found a bug, please [file an issue](https://github.com/StackExchange/dnscontrol/issues/new).
|
||||
|
||||
5. Once you are confident in the new provider, remove `ADServer`, `fakeps`, `pslog`, `psout` from `creds.json`.
|
7
go.mod
7
go.mod
@ -15,6 +15,7 @@ require (
|
||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.36.0
|
||||
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
|
||||
github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e
|
||||
github.com/billputer/go-namecheap v0.0.0-20170915210158-0c7adb0710f8
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
@ -38,14 +39,13 @@ require (
|
||||
github.com/hashicorp/vault/api v1.0.4
|
||||
github.com/hexonet/go-sdk v3.5.0+incompatible
|
||||
github.com/jarcoal/httpmock v1.0.4 // indirect
|
||||
github.com/juju/testing v0.0.0-20201216035041-2be42bba85f3 // indirect
|
||||
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/miekg/dns v1.1.35
|
||||
github.com/mitchellh/mapstructure v1.4.0 // indirect
|
||||
github.com/mittwald/go-powerdns v0.5.2
|
||||
github.com/mjibson/esc v0.2.0
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/nrdcg/goinwx v0.8.1
|
||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
|
||||
github.com/philhug/opensrs-go v0.0.0-20171126225031-9dfa7433020d
|
||||
@ -66,7 +66,7 @@ require (
|
||||
github.com/vultr/govultr v1.0.0
|
||||
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect
|
||||
golang.org/x/mod v0.4.0 // indirect
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
|
||||
golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19
|
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435 // indirect
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
|
||||
@ -75,7 +75,6 @@ require (
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20201203001206-6486ece9c497 // indirect
|
||||
google.golang.org/grpc v1.34.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20170502175150-c563826f4cbe
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
|
61
go.sum
61
go.sum
@ -85,6 +85,8 @@ github.com/aws/aws-sdk-go v1.36.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zK
|
||||
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0=
|
||||
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e h1:KCjb01YiNoRaJ5c+SbnPLWjVzU9vqRYHg3e5JcN50nM=
|
||||
github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e/go.mod h1:f7vw6ObmmNcyFQLhZX9eUGBJGpnwTJFDvVjqZxIxHWY=
|
||||
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/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
@ -280,6 +282,32 @@ github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfE
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
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/juju/ansiterm v0.0.0-20160907234532-b99631de12cf/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
|
||||
github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
|
||||
github.com/juju/cmd v0.0.0-20171107070456-e74f39857ca0/go.mod h1:yWJQHl73rdSX4DHVKGqkAip+huBslxRwS8m9CrOLq18=
|
||||
github.com/juju/collections v0.0.0-20200605021417-0d0ec82b7271/go.mod h1:5XgO71dV1JClcOJE+4dzdn4HrI5LiyKd7PlVG6eZYhY=
|
||||
github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/errors v0.0.0-20200330140219-3fe23663418f h1:MCOvExGLpaSIzLYB4iQXEHP4jYVU6vmzLNQPdMVrxnM=
|
||||
github.com/juju/errors v0.0.0-20200330140219-3fe23663418f/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/juju/httpprof v0.0.0-20141217160036-14bf14c30767/go.mod h1:+MaLYz4PumRkkyHYeXJ2G5g5cIW0sli2bOfpmbaMV/g=
|
||||
github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/loggo v0.0.0-20200526014432-9ce3a2e09b5e h1:FdDd7bdI6cjq5vaoYlK1mfQYfF9sF2VZw8VEZMsl5t8=
|
||||
github.com/juju/loggo v0.0.0-20200526014432-9ce3a2e09b5e/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/mutex v0.0.0-20171110020013-1fe2a4bf0a3a/go.mod h1:Y3oOzHH8CQ0Ppt0oCKJ2JFO81/EsWenH5AEqigLH+yY=
|
||||
github.com/juju/retry v0.0.0-20151029024821-62c620325291/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4=
|
||||
github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4=
|
||||
github.com/juju/testing v0.0.0-20180402130637-44801989f0f7/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/juju/testing v0.0.0-20201216035041-2be42bba85f3 h1:ram7cW6jDPTu6cv9xDMwa+tO7RsO4BdsubxrJ4EEw+E=
|
||||
github.com/juju/testing v0.0.0-20201216035041-2be42bba85f3/go.mod h1:IbSKFoKW0bzmbDZ7rBwF/L3lO3b1bpmOIhTXQl/WJxw=
|
||||
github.com/juju/utils v0.0.0-20180424094159-2000ea4ff043/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk=
|
||||
github.com/juju/utils v0.0.0-20200116185830-d40c2fe10647/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk=
|
||||
github.com/juju/utils/v2 v2.0.0-20200923005554-4646bfea2ef1/go.mod h1:fdlDtQlzundleLLz/ggoYinEt/LmnrpNKcNTABQATNI=
|
||||
github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
|
||||
github.com/juju/version v0.0.0-20180108022336-b64dbd566305/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
|
||||
github.com/juju/version v0.0.0-20191219164919-81c1be00b9a6/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
|
||||
github.com/julienschmidt/httprouter v1.1.1-0.20151013225520-77a895ad01eb/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
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=
|
||||
@ -294,7 +322,14 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20160125035106-4fbf7632a2c6/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/masterzen/azure-sdk-for-go v3.2.0-beta.0.20161014135628-ee4f0065d00c+incompatible/go.mod h1:mf8fjOu33zCqxUjuiU3I8S1lJMyEAlH+0F2+M5xl3hE=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20161014151040-7a535cd943fc/go.mod h1:CfZSN7zwz5gJiFhZJz49Uzk7mEBHIceWmbFmYx7Hf7E=
|
||||
github.com/masterzen/xmlpath v0.0.0-20140218185901-13f4951698ad/go.mod h1:A0zPC53iKKKcXYxr4ROjpQRQ5FgJXtelNdSmHHuq/tY=
|
||||
github.com/mattn/go-colorable v0.0.6/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.0-20160806122752-66b8e73f3f5c/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
@ -324,6 +359,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
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/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||
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/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ=
|
||||
@ -399,12 +435,14 @@ go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
|
||||
@ -447,6 +485,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -479,12 +518,13 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
@ -547,9 +587,11 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435 h1:25AvDqqB9PrNqj1FLf2/70I4W0L19qqoaFq3gjNwbKk=
|
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -729,16 +771,22 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v1 v1.0.0-20161222125816-442357a80af5/go.mod h1:u0ALmqvLRxLI95fkdCEWrE6mhWYZW1aMOJHp5YXLHTg=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/h2non/gock.v1 v1.0.14 h1:fTeu9fcUvSnLNacYvYI54h+1/XEteDyHvrVCZEEEYNM=
|
||||
gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/httprequest.v1 v1.1.1/go.mod h1:/CkavNL+g3qLOrpFHVrEx4NKepeqR4XTZWNj4sGGjz0=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20170502175150-c563826f4cbe h1:fuu3vZ8C6O8mk8Ich8YfkDv/Zpnx1HUotQk8JocBcSw=
|
||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20170502175150-c563826f4cbe/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw=
|
||||
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
||||
@ -747,11 +795,14 @@ gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
@ -767,6 +818,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
launchpad.net/xmlpath v0.0.0-20130614043138-000000000004/go.mod h1:vqyExLOM3qBx7mvYRkoxjSCF945s0mbe7YynlKYXtsA=
|
||||
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
@ -652,7 +652,20 @@ func makeTests(t *testing.T) []*TestGroup {
|
||||
),
|
||||
|
||||
testgroup("Null MX",
|
||||
not("AZURE_DNS", "GANDI_V5", "INWX", "NAMEDOTCOM", "DIGITALOCEAN", "NETCUP", "DNSIMPLE", "HEDNS", "VULTR", "OVH"), // These providers don't support RFC 7505
|
||||
// These providers don't support RFC 7505
|
||||
not(
|
||||
"AZURE_DNS",
|
||||
"DIGITALOCEAN",
|
||||
"DNSIMPLE",
|
||||
"GANDI_V5",
|
||||
"HEDNS",
|
||||
"INWX",
|
||||
"MSDNS",
|
||||
"NAMEDOTCOM",
|
||||
"NETCUP",
|
||||
"OVH",
|
||||
"VULTR",
|
||||
),
|
||||
tc("Null MX", mx("@", 0, ".")),
|
||||
),
|
||||
|
||||
@ -689,11 +702,22 @@ func makeTests(t *testing.T) []*TestGroup {
|
||||
tc("Create a TXT with spaces", txt("foo", "with spaces")),
|
||||
tc("Create 1 TXT as array", txtmulti("foo", []string{"simple"})), // Same as non-TXTMulti
|
||||
clear(),
|
||||
tc("Create a 254-byte TXT", txt("foo", strings.Repeat("A", 254))),
|
||||
),
|
||||
|
||||
testgroup("max-sized TXT",
|
||||
not(
|
||||
"INWX", // INWX does not support
|
||||
"MSDNS", // Supports 254-byte strings, not 255. Seems like a bug.
|
||||
),
|
||||
tc("Create a 255-byte TXT", txt("foo", strings.Repeat("A", 255))),
|
||||
),
|
||||
|
||||
testgroup("single TXT with single-quote",
|
||||
not("INWX"),
|
||||
not(
|
||||
"INWX", // Bug in the API prevents this.
|
||||
"MSDNS", // TODO(tlim): Should be easy to implement.
|
||||
),
|
||||
tc("Create TXT with single-quote", txt("foo", "blah`blah")),
|
||||
),
|
||||
|
||||
@ -705,7 +729,13 @@ func makeTests(t *testing.T) []*TestGroup {
|
||||
),
|
||||
|
||||
testgroup("empty TXT",
|
||||
not("HETZNER", "HEXONET", "INWX", "NETCUP"),
|
||||
not(
|
||||
"HETZNER", // Not supported.
|
||||
"HEXONET", // Not supported.
|
||||
"INWX", // Not supported.
|
||||
"MSDNS", // Not supported.
|
||||
"NETCUP", // Not supported.
|
||||
),
|
||||
tc("TXT with empty str", txt("foo1", "")),
|
||||
// https://github.com/StackExchange/dnscontrol/issues/598
|
||||
// We decided that permitting the TXT target to be an empty
|
||||
@ -754,28 +784,36 @@ func makeTests(t *testing.T) []*TestGroup {
|
||||
// Tests the paging code of providers. Many providers page at 100.
|
||||
// Notes:
|
||||
// - Gandi: page size is 100, therefore we test with 99, 100, and 101
|
||||
// - NS1: free acct only allows 50 records, therefore we skip
|
||||
// - DIGITALOCEAN: page size is 100 (default: 20)
|
||||
// - CLOUDFLAREAPI: Infinite pagesize but due to slow speed, skipping.
|
||||
not("NS1", "CLOUDFLAREAPI"),
|
||||
not(
|
||||
"NS1", // Free acct only allows 50 records, therefore we skip
|
||||
"CLOUDFLAREAPI", // Infinite pagesize but due to slow speed, skipping.
|
||||
"MSDNS", // No paging done. No need to test.
|
||||
),
|
||||
tc("99 records", manyA("rec%04d", "1.2.3.4", 99)...),
|
||||
tc("100 records", manyA("rec%04d", "1.2.3.4", 100)...),
|
||||
tc("101 records", manyA("rec%04d", "1.2.3.4", 101)...),
|
||||
),
|
||||
|
||||
testgroup("pager601",
|
||||
// AWS: https://github.com/StackExchange/dnscontrol/issues/493
|
||||
//only("AZURE_DNS", "HEXONET", "ROUTE53"),
|
||||
only("HEXONET", "ROUTE53", "GCLOUD"), // AZURE_DNS is failing.
|
||||
only(
|
||||
//"MSDNS", // No paging done. No need to test.
|
||||
//"AZURE_DNS", // Currently failing.
|
||||
"HEXONET",
|
||||
"GCLOUD",
|
||||
"ROUTE53", // See https://github.com/StackExchange/dnscontrol/issues/493
|
||||
),
|
||||
tc("601 records", manyA("rec%04d", "1.2.3.4", 600)...),
|
||||
tc("Update 601 records", manyA("rec%04d", "1.2.3.5", 600)...),
|
||||
),
|
||||
|
||||
testgroup("pager1201",
|
||||
// AWS: https://github.com/StackExchange/dnscontrol/issues/493
|
||||
// Azure: https://github.com/StackExchange/dnscontrol/issues/770
|
||||
//only("AZURE_DNS", "HEXONET", "ROUTE53"),
|
||||
only("HEXONET", "ROUTE53"), // AZURE_DNS is failing.
|
||||
only(
|
||||
//"MSDNS", // No paging done. No need to test.
|
||||
//"AZURE_DNS", // Currently failing. See https://github.com/StackExchange/dnscontrol/issues/770
|
||||
"HEXONET",
|
||||
"ROUTE53", // https://github.com/StackExchange/dnscontrol/issues/493
|
||||
),
|
||||
tc("1200 records", manyA("rec%04d", "1.2.3.4", 1200)...),
|
||||
tc("Update 1200 records", manyA("rec%04d", "1.2.3.5", 1200)...),
|
||||
),
|
||||
@ -830,7 +868,14 @@ 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 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", "INWX", "NAMEDOTCOM"),
|
||||
testgroup("SRV w/ null target", requires(providers.CanUseSRV),
|
||||
not(
|
||||
"EXOSCALE", // Not supported.
|
||||
"HEXONET", // Not supported.
|
||||
"INWX", // Not supported.
|
||||
"MSDNS", // Not supported.
|
||||
"NAMEDOTCOM", // Not supported.
|
||||
),
|
||||
tc("Null Target", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, ".")),
|
||||
),
|
||||
|
||||
|
@ -96,6 +96,11 @@
|
||||
"domain": "$LINODE_DOMAIN",
|
||||
"token": "$LINODE_TOKEN"
|
||||
},
|
||||
"MSDNS": {
|
||||
"domain": "$MSDNS_DOMAIN",
|
||||
"dnsserver": "$MSDNS_DNSSERVER",
|
||||
"pssession": "$MSDNS_PSSESSION"
|
||||
},
|
||||
"NAMECHEAP": {
|
||||
"apikey": "$NAMECHEAP_KEY",
|
||||
"apiuser": "$NAMECHEAP_USER",
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
_ "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/msdns"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/namecheap"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/namedotcom"
|
||||
_ "github.com/StackExchange/dnscontrol/v3/providers/netcup"
|
||||
|
@ -34,6 +34,7 @@ func init() {
|
||||
}
|
||||
|
||||
func newDNS(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||
fmt.Printf("WARNING: ACTIVEDIRECTORY_PS provider is being replaced by MSDNS. Please convert. Details in https://stackexchange.github.io/dnscontrol/providers/msdns\n")
|
||||
|
||||
fake := false
|
||||
if fVal := config["fakeps"]; fVal == "true" {
|
||||
|
122
providers/msdns/convert.go
Normal file
122
providers/msdns/convert.go
Normal file
@ -0,0 +1,122 @@
|
||||
package msdns
|
||||
|
||||
// Convert the provider's native record description to models.RecordConfig.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
)
|
||||
|
||||
// extractProps and collects Name/Value pairs into maps for easier access.
|
||||
func extractProps(cip []ciProperty) (map[string]string, map[string]uint32, error) {
|
||||
|
||||
// Sadly this structure is dynamic JSON i.e. .Value could be an int, string,
|
||||
// or a map. We peek at the first byte to guess at the contents.
|
||||
|
||||
// We store strings in sprops, numbers in uprops. Maps are special: Currently
|
||||
// the only map we decode is a map with the same duration in many units. We
|
||||
// simply pick the Seconds unit and store it as a number.
|
||||
|
||||
sprops := map[string]string{}
|
||||
uprops := map[string]uint32{}
|
||||
for _, p := range cip {
|
||||
name := p.Name
|
||||
if len(p.Value) == 0 {
|
||||
// Empty string? Skip it.
|
||||
} else if p.Value[0] == '"' {
|
||||
// First byte is a quote. Must be a string.
|
||||
var svalue string
|
||||
err := json.Unmarshal(p.Value, &svalue)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not unmarshal string value=%q: %w", p.Value, err)
|
||||
}
|
||||
sprops[name] = svalue
|
||||
} else if p.Value[0] == '{' {
|
||||
// First byte is {. Must be a map.
|
||||
var dvalue ciValueDuration
|
||||
err := json.Unmarshal(p.Value, &dvalue)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not unmarshal duration value=%q: %w", p.Value, err)
|
||||
}
|
||||
uprops[name] = uint32(dvalue.TotalSeconds)
|
||||
} else {
|
||||
// Assume it is a number.
|
||||
var uvalue uint32
|
||||
err := json.Unmarshal(p.Value, &uvalue)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not unmarshal uint value=%q: %w", p.Value, err)
|
||||
}
|
||||
uprops[name] = uvalue
|
||||
}
|
||||
}
|
||||
return sprops, uprops, nil
|
||||
}
|
||||
|
||||
// nativeToRecord takes a DNS record from DNS and returns a native RecordConfig struct.
|
||||
func nativeToRecords(nr nativeRecord, origin string) (*models.RecordConfig, error) {
|
||||
rc := &models.RecordConfig{
|
||||
Type: nr.RecordType,
|
||||
Original: nr,
|
||||
}
|
||||
rc.SetLabel(nr.HostName, origin)
|
||||
rc.TTL = uint32(nr.TimeToLive.TotalSeconds)
|
||||
|
||||
sprops, uprops, err := extractProps(nr.RecordData.CimInstanceProperties)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch rtype := nr.RecordType; rtype {
|
||||
case "A":
|
||||
contents := sprops["IPv4Address"]
|
||||
ip := net.ParseIP(contents)
|
||||
if ip == nil || ip.To4() == nil {
|
||||
return nil, fmt.Errorf("invalid IP in A record: %q", contents)
|
||||
}
|
||||
rc.SetTargetIP(ip)
|
||||
case "AAAA":
|
||||
contents := sprops["IPv6Address"]
|
||||
ip := net.ParseIP(contents)
|
||||
if ip == nil || ip.To16() == nil {
|
||||
return nil, fmt.Errorf("invalid IPv6 in AAAA record: %q", contents)
|
||||
}
|
||||
rc.SetTargetIP(ip)
|
||||
case "CNAME":
|
||||
rc.SetTarget(sprops["HostNameAlias"])
|
||||
case "MX":
|
||||
rc.SetTargetMX(uint16(uprops["Preference"]), sprops["MailExchange"])
|
||||
case "NS":
|
||||
rc.SetTarget(sprops["NameServer"])
|
||||
case "PTR":
|
||||
rc.SetTarget(sprops["PtrDomainName"])
|
||||
case "SRV":
|
||||
rc.SetTargetSRV(
|
||||
uint16(uprops["Priority"]),
|
||||
uint16(uprops["Weight"]),
|
||||
uint16(uprops["Port"]),
|
||||
sprops["DomainName"],
|
||||
)
|
||||
case "SOA":
|
||||
// We discard SOA records for now. Windows DNS doesn't let us delete
|
||||
// them and they get in the way of integration tests. In the future,
|
||||
// we should support SOA records by (1) ignoring them in the
|
||||
// integration tests. (2) generatePSModify will have to special-case
|
||||
// updates.
|
||||
return nil, nil
|
||||
// If we weren't ignoring them, the code would look like this:
|
||||
//rc.SetTargetSOA(sprops["PrimaryServer"], sprops["ResponsiblePerson"],
|
||||
// uprops["SerialNumber"], uprops["RefreshInterval"], uprops["RetryDelay"],
|
||||
// uprops["ExpireLimit"], uprops["MinimumTimeToLive"])
|
||||
case "TXT":
|
||||
rc.SetTargetTXTString(sprops["DescriptiveText"])
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"msdns/convert.go:nativeToRecord rtype=%q unknown: props=%+v and %+v",
|
||||
rtype, sprops, uprops)
|
||||
}
|
||||
|
||||
return rc, nil
|
||||
}
|
72
providers/msdns/corrections.go
Normal file
72
providers/msdns/corrections.go
Normal file
@ -0,0 +1,72 @@
|
||||
package msdns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
||||
)
|
||||
|
||||
// GetDomainCorrections gets existing records, diffs them against existing, and returns corrections.
|
||||
func (c *msdnsProvider) GenerateDomainCorrections(dc *models.DomainConfig, existing models.Records) ([]*models.Correction, error) {
|
||||
|
||||
// Read foundRecords:
|
||||
foundRecords, err := c.GetZoneRecords(dc.Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("c.GetDNSZoneRecords(%v) failed: %v", dc.Name, err)
|
||||
}
|
||||
|
||||
// Normalize
|
||||
models.PostProcessRecords(foundRecords)
|
||||
|
||||
differ := diff.New(dc)
|
||||
_, creates, dels, modifications, err := differ.IncrementalDiff(foundRecords)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate changes.
|
||||
corrections := []*models.Correction{}
|
||||
for _, del := range dels {
|
||||
corrections = append(corrections, c.deleteRec(c.dnsserver, dc.Name, del))
|
||||
}
|
||||
for _, cre := range creates {
|
||||
corrections = append(corrections, c.createRec(c.dnsserver, dc.Name, cre)...)
|
||||
}
|
||||
for _, m := range modifications {
|
||||
corrections = append(corrections, c.modifyRec(c.dnsserver, dc.Name, m))
|
||||
}
|
||||
return corrections, nil
|
||||
|
||||
}
|
||||
|
||||
func (c *msdnsProvider) deleteRec(dnsserver, domainname string, cor diff.Correlation) *models.Correction {
|
||||
rec := cor.Existing
|
||||
return &models.Correction{
|
||||
Msg: cor.String(),
|
||||
F: func() error {
|
||||
return c.shell.RecordDelete(dnsserver, domainname, rec)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *msdnsProvider) createRec(dnsserver, domainname string, cre diff.Correlation) []*models.Correction {
|
||||
rec := cre.Desired
|
||||
arr := []*models.Correction{{
|
||||
Msg: cre.String(),
|
||||
F: func() error {
|
||||
return c.shell.RecordCreate(dnsserver, domainname, rec)
|
||||
},
|
||||
}}
|
||||
return arr
|
||||
}
|
||||
|
||||
func (c *msdnsProvider) modifyRec(dnsserver, domainname string, m diff.Correlation) *models.Correction {
|
||||
old, rec := m.Existing, m.Desired
|
||||
return &models.Correction{
|
||||
Msg: m.String(),
|
||||
F: func() error {
|
||||
return c.shell.RecordModify(dnsserver, domainname, old, rec)
|
||||
},
|
||||
}
|
||||
}
|
8
providers/msdns/domains.go
Normal file
8
providers/msdns/domains.go
Normal file
@ -0,0 +1,8 @@
|
||||
package msdns
|
||||
|
||||
import "github.com/StackExchange/dnscontrol/v3/models"
|
||||
|
||||
func (c *msdnsProvider) GetNameservers(string) ([]*models.Nameserver, error) {
|
||||
// TODO: If using AD for publicly hosted zones, probably pull these from config.
|
||||
return nil, nil
|
||||
}
|
9
providers/msdns/listzones.go
Normal file
9
providers/msdns/listzones.go
Normal file
@ -0,0 +1,9 @@
|
||||
package msdns
|
||||
|
||||
func (c *msdnsProvider) ListZones() ([]string, error) {
|
||||
zones, err := c.shell.GetDNSServerZoneAll(c.dnsserver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return zones, err
|
||||
}
|
129
providers/msdns/msdnsProvider.go
Normal file
129
providers/msdns/msdnsProvider.go
Normal file
@ -0,0 +1,129 @@
|
||||
package msdns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
"github.com/StackExchange/dnscontrol/v3/providers"
|
||||
)
|
||||
|
||||
// This is the struct that matches either (or both) of the Registrar and/or DNSProvider interfaces:
|
||||
type msdnsProvider struct {
|
||||
dnsserver string // Which DNS Server to update
|
||||
pssession string // Remote machine to PSSession to
|
||||
shell DNSAccessor // Handle for
|
||||
}
|
||||
|
||||
var features = providers.DocumentationNotes{
|
||||
providers.CanGetZones: providers.Can(),
|
||||
providers.CanUseAlias: providers.Cannot(),
|
||||
providers.CanUseCAA: providers.Cannot(),
|
||||
providers.CanUseDS: providers.Unimplemented(),
|
||||
providers.CanUsePTR: providers.Can(),
|
||||
providers.CanUseSRV: providers.Can(),
|
||||
providers.CanUseTLSA: providers.Unimplemented(),
|
||||
providers.CanUseTXTMulti: providers.Unimplemented(),
|
||||
providers.DocCreateDomains: providers.Cannot("This provider assumes the zone already existing on the dns server"),
|
||||
providers.DocDualHost: providers.Cannot("This driver does not manage NS records, so should not be used for dual-host scenarios"),
|
||||
providers.DocOfficiallySupported: providers.Can(),
|
||||
}
|
||||
|
||||
// Register with the dnscontrol system.
|
||||
// This establishes the name (all caps), and the function to call to initialize it.
|
||||
func init() {
|
||||
providers.RegisterDomainServiceProviderType("MSDNS", newDNS, features)
|
||||
}
|
||||
|
||||
func newDNS(config map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
fmt.Println("INFO: PowerShell not available. Disabling Active Directory provider.")
|
||||
return providers.None{}, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
p := &msdnsProvider{
|
||||
dnsserver: config["dnsserver"],
|
||||
}
|
||||
p.shell, err = newPowerShell(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Section 3: Domain Service Provider (DSP) related functions
|
||||
|
||||
// NB(tal): To future-proof your code, all new providers should
|
||||
// implement GetDomainCorrections exactly as you see here
|
||||
// (byte-for-byte the same). In 3.0
|
||||
// we plan on using just the individual calls to GetZoneRecords,
|
||||
// PostProcessRecords, and so on.
|
||||
//
|
||||
// Currently every provider does things differently, which prevents
|
||||
// us from doing things like using GetZoneRecords() of a provider
|
||||
// to make convertzone work with all providers.
|
||||
|
||||
// GetDomainCorrections get the current and existing records,
|
||||
// post-process them, and generate corrections.
|
||||
func (client *msdnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
||||
existing, err := client.GetZoneRecords(dc.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
models.PostProcessRecords(existing)
|
||||
clean := PrepFoundRecords(existing)
|
||||
PrepDesiredRecords(dc)
|
||||
return client.GenerateDomainCorrections(dc, clean)
|
||||
}
|
||||
|
||||
// GetZoneRecords gathers the DNS records and converts them to
|
||||
// dnscontrol's format.
|
||||
func (client *msdnsProvider) GetZoneRecords(domain string) (models.Records, error) {
|
||||
|
||||
// Get the existing DNS records in native format.
|
||||
nativeExistingRecords, err := client.shell.GetDNSZoneRecords(client.dnsserver, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Convert them to DNScontrol's native format:
|
||||
existingRecords := make([]*models.RecordConfig, 0, len(nativeExistingRecords))
|
||||
for _, rr := range nativeExistingRecords {
|
||||
rc, err := nativeToRecords(rr, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rc != nil {
|
||||
existingRecords = append(existingRecords, rc)
|
||||
}
|
||||
}
|
||||
|
||||
return existingRecords, nil
|
||||
}
|
||||
|
||||
// PrepFoundRecords munges any records to make them compatible with
|
||||
// this provider. Usually this is a no-op.
|
||||
func PrepFoundRecords(recs models.Records) models.Records {
|
||||
// If there are records that need to be modified, removed, etc. we
|
||||
// do it here. Usually this is a no-op.
|
||||
return recs
|
||||
}
|
||||
|
||||
// PrepDesiredRecords munges any records to best suit this provider.
|
||||
func PrepDesiredRecords(dc *models.DomainConfig) {
|
||||
// Sort through the dc.Records, eliminate any that can't be
|
||||
// supported; modify any that need adjustments to work with the
|
||||
// provider. We try to do minimal changes otherwise it gets
|
||||
// confusing.
|
||||
|
||||
dc.Punycode()
|
||||
}
|
||||
|
||||
// NB(tlim): If we want to implement a registrar, refer to
|
||||
// http://go.microsoft.com/fwlink/?LinkId=288158
|
||||
// (Get-DnsServerZoneDelegation) for hints about which PowerShell
|
||||
// commands to use.
|
414
providers/msdns/powershell.go
Normal file
414
providers/msdns/powershell.go
Normal file
@ -0,0 +1,414 @@
|
||||
package msdns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
"github.com/TomOnTime/utfutil"
|
||||
ps "github.com/bhendo/go-powershell"
|
||||
"github.com/bhendo/go-powershell/backend"
|
||||
"github.com/bhendo/go-powershell/middleware"
|
||||
)
|
||||
|
||||
type psHandle struct {
|
||||
shell ps.Shell
|
||||
}
|
||||
|
||||
func newPowerShell(config map[string]string) (*psHandle, error) {
|
||||
|
||||
back := &backend.Local{}
|
||||
sh, err := ps.New(back)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shell := sh
|
||||
|
||||
pssession := config["pssession"]
|
||||
if pssession != "" {
|
||||
fmt.Printf("INFO: PowerShell commands will run on %q\n", pssession)
|
||||
// create a remote shell by wrapping the existing one in the session middleware
|
||||
mconfig := middleware.NewSessionConfig()
|
||||
mconfig.ComputerName = pssession
|
||||
|
||||
session, err := middleware.NewSession(sh, mconfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
shell = session
|
||||
}
|
||||
|
||||
psh := &psHandle{
|
||||
shell: shell,
|
||||
}
|
||||
return psh, nil
|
||||
}
|
||||
|
||||
func (psh *psHandle) Exit() {
|
||||
psh.shell.Exit()
|
||||
}
|
||||
|
||||
type dnsZone map[string]interface{}
|
||||
|
||||
func (psh *psHandle) GetDNSServerZoneAll(dnsserver string) ([]string, error) {
|
||||
stdout, stderr, err := psh.shell.Execute(generatePSZoneAll(dnsserver))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stderr != "" {
|
||||
fmt.Printf("STDERROR = %q\n", stderr)
|
||||
return nil, fmt.Errorf("unexpected stderr from Get-DnsServerZones: %q", stderr)
|
||||
}
|
||||
|
||||
var zones []dnsZone
|
||||
json.Unmarshal([]byte(stdout), &zones)
|
||||
|
||||
var result []string
|
||||
for _, z := range zones {
|
||||
zonename := z["ZoneName"].(string)
|
||||
result = append(result, zonename)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// powerShellDump runs a PowerShell command to get a dump of all records in a DNS zone.
|
||||
func generatePSZoneAll(dnsserver string) string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, `Get-DnsServerZone`)
|
||||
if dnsserver != "" {
|
||||
fmt.Fprintf(&b, ` -ComputerName "%v"`, dnsserver)
|
||||
}
|
||||
fmt.Fprintf(&b, ` | `)
|
||||
fmt.Fprintf(&b, `ConvertTo-Json`)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (psh *psHandle) GetDNSZoneRecords(dnsserver, domain string) ([]nativeRecord, error) {
|
||||
tmpfile, err := ioutil.TempFile("", "zonerecords.*.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tmpfile.Close()
|
||||
|
||||
stdout, stderr, err := psh.shell.Execute(generatePSZoneDump(dnsserver, domain, tmpfile.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stdout != "" {
|
||||
fmt.Printf("STDOUT = %q\n", stderr)
|
||||
}
|
||||
if stderr != "" {
|
||||
fmt.Printf("STDERROR = %q\n", stderr)
|
||||
return nil, fmt.Errorf("unexpected stderr from PSZoneDump: %q", stderr)
|
||||
}
|
||||
|
||||
contents, err := utfutil.ReadFile(tmpfile.Name(), utfutil.WINDOWS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Remove(tmpfile.Name())
|
||||
|
||||
var records []nativeRecord
|
||||
json.Unmarshal([]byte(contents), &records)
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// powerShellDump runs a PowerShell command to get a dump of all records in a DNS zone.
|
||||
func generatePSZoneDump(dnsserver, domainname string, filename string) string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, `Get-DnsServerResourceRecord`)
|
||||
if dnsserver != "" {
|
||||
fmt.Fprintf(&b, ` -ComputerName "%v"`, dnsserver)
|
||||
}
|
||||
fmt.Fprintf(&b, ` -ZoneName "%v"`, domainname)
|
||||
fmt.Fprintf(&b, ` | `)
|
||||
fmt.Fprintf(&b, `ConvertTo-Json -depth 4`) // Tested with 3 (causes errors). 4 and larger work.
|
||||
fmt.Fprintf(&b, ` > %s`, filename)
|
||||
//fmt.Printf("DEBUG PSZoneDump CMD = (\n%s\n)\n", b.String())
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Functions for record manipulation
|
||||
|
||||
func (psh *psHandle) RecordDelete(dnsserver, domain string, rec *models.RecordConfig) error {
|
||||
_, stderr, err := psh.shell.Execute(generatePSDelete(dnsserver, domain, rec))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stderr != "" {
|
||||
fmt.Printf("STDERROR = %q\n", stderr)
|
||||
return fmt.Errorf("unexpected stderr from PSDelete: %q", stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generatePSDelete(dnsserver, domain string, rec *models.RecordConfig) string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, `echo DELETE "%s" "%s" "%s"`, rec.Type, rec.Name, rec.GetTargetCombined())
|
||||
fmt.Fprintf(&b, " ; ")
|
||||
fmt.Fprintf(&b, `Remove-DnsServerResourceRecord`)
|
||||
if dnsserver != "" {
|
||||
fmt.Fprintf(&b, ` -ComputerName "%s"`, dnsserver)
|
||||
}
|
||||
fmt.Fprintf(&b, ` -Force`)
|
||||
fmt.Fprintf(&b, ` -ZoneName "%s"`, domain)
|
||||
fmt.Fprintf(&b, ` -Name "%s"`, rec.Name)
|
||||
fmt.Fprintf(&b, ` -RRType "%s"`, rec.Type)
|
||||
if rec.Type == "MX" {
|
||||
fmt.Fprintf(&b, ` -RecordData %d,"%s"`, rec.MxPreference, rec.GetTargetField())
|
||||
} else if rec.Type == "SRV" {
|
||||
// https://www.gitmemory.com/issue/MicrosoftDocs/windows-powershell-docs/1149/511916884
|
||||
fmt.Fprintf(&b, ` -RecordData %d,%d,%d,"%s"`, rec.SrvPriority, rec.SrvWeight, rec.SrvPort, rec.GetTargetField())
|
||||
} else {
|
||||
fmt.Fprintf(&b, ` -RecordData "%s"`, rec.GetTargetField())
|
||||
}
|
||||
//fmt.Printf("DEBUG PSDelete CMD = (\n%s\n)\n", b.String())
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (psh *psHandle) RecordCreate(dnsserver, domain string, rec *models.RecordConfig) error {
|
||||
_, stderr, err := psh.shell.Execute(generatePSCreate(dnsserver, domain, rec))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stderr != "" {
|
||||
fmt.Printf("STDERROR = %q\n", stderr)
|
||||
return fmt.Errorf("unexpected stderr from PSCreate: %q", stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generatePSCreate(dnsserver, domain string, rec *models.RecordConfig) string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, `echo CREATE "%s" "%s" "%s"`, rec.Type, rec.Name, rec.GetTargetCombined())
|
||||
fmt.Fprintf(&b, " ; ")
|
||||
|
||||
fmt.Fprint(&b, `Add-DnsServerResourceRecord`)
|
||||
if dnsserver != "" {
|
||||
fmt.Fprintf(&b, ` -ComputerName "%s"`, dnsserver)
|
||||
}
|
||||
fmt.Fprintf(&b, ` -ZoneName "%s"`, domain)
|
||||
fmt.Fprintf(&b, ` -Name "%s"`, rec.GetLabel())
|
||||
fmt.Fprintf(&b, ` -TimeToLive $(New-TimeSpan -Seconds %d)`, rec.TTL)
|
||||
switch rec.Type {
|
||||
case "A":
|
||||
fmt.Fprintf(&b, ` -A -IPv4Address "%s"`, rec.GetTargetIP())
|
||||
case "AAAA":
|
||||
fmt.Fprintf(&b, ` -AAAA -IPv6Address "%s"`, rec.GetTargetIP())
|
||||
//case "ATMA":
|
||||
// fmt.Fprintf(&b, ` -Atma -Address <String> -AddressType {E164 | AESA}`, rec.GetTargetField())
|
||||
//case "AFSDB":
|
||||
// fmt.Fprintf(&b, ` -Afsdb -ServerName <String> -SubType <UInt16>`, rec.GetTargetField())
|
||||
case "SRV":
|
||||
fmt.Fprintf(&b, ` -Srv -DomainName "%s" -Port %d -Priority %d -Weight %d`, rec.GetTargetField(), rec.SrvPort, rec.SrvPriority, rec.SrvWeight)
|
||||
case "CNAME":
|
||||
fmt.Fprintf(&b, ` -CName -HostNameAlias "%s"`, rec.GetTargetField())
|
||||
//case "X25":
|
||||
// fmt.Fprintf(&b, ` -X25 -PsdnAddress <String>`, rec.GetTargetField())
|
||||
//case "WKS":
|
||||
// fmt.Fprintf(&b, ` -Wks -InternetAddress <IPAddress> -InternetProtocol {UDP | TCP} -Service <String[]>`, rec.GetTargetField())
|
||||
case "TXT":
|
||||
fmt.Fprintf(&b, ` -Txt -DescriptiveText "%s"`, rec.GetTargetField())
|
||||
//case "RT":
|
||||
// fmt.Fprintf(&b, ` -RT -IntermediateHost <String> -Preference <UInt16>`, rec.GetTargetField())
|
||||
//case "RP":
|
||||
// fmt.Fprintf(&b, ` -RP -Description <String> -ResponsiblePerson <String>`, rec.GetTargetField())
|
||||
case "PTR":
|
||||
fmt.Fprintf(&b, ` -Ptr -PtrDomainName "%s"`, rec.GetTargetField())
|
||||
case "NS":
|
||||
fmt.Fprintf(&b, ` -NS -NameServer "%s"`, rec.GetTargetField())
|
||||
case "MX":
|
||||
fmt.Fprintf(&b, ` -MX -MailExchange "%s" -Preference %d`, rec.GetTargetField(), rec.MxPreference)
|
||||
//case "ISDN":
|
||||
// fmt.Fprintf(&b, ` -Isdn -IsdnNumber <String> -IsdnSubAddress <String>`, rec.GetTargetField())
|
||||
//case "HINFO":
|
||||
// fmt.Fprintf(&b, ` -HInfo -Cpu <String> -OperatingSystem <String>`, rec.GetTargetField())
|
||||
//case "DNAME":
|
||||
// fmt.Fprintf(&b, ` -DName -DomainNameAlias <String>`, rec.GetTargetField())
|
||||
//case "DHCID":
|
||||
// fmt.Fprintf(&b, ` -DhcId -DhcpIdentifier <String>`, rec.GetTargetField())
|
||||
//case "TLSA":
|
||||
// fmt.Fprintf(&b, ` -TLSA -CertificateAssociationData <System.String> -CertificateUsage {CAConstraint | ServiceCertificateConstraint | TrustAnchorAssertion | DomainIssuedCertificate} -MatchingType {ExactMatch | Sha256Hash | Sha512Hash} -Selector {FullCertificate | SubjectPublicKeyInfo}`, rec.GetTargetField())
|
||||
default:
|
||||
panic(fmt.Errorf("generatePSCreate() has not implemented recType=%s recName=%#v content=%#v)",
|
||||
rec.Type, rec.GetLabel(), rec.GetTargetField()))
|
||||
// We panic so that we quickly find any switch statements
|
||||
// that have not been updated for a new RR type.
|
||||
}
|
||||
//fmt.Printf("DEBUG PSCreate CMD = (\n%s\n)\n", b.String())
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (psh *psHandle) RecordModify(dnsserver, domain string, old, rec *models.RecordConfig) error {
|
||||
_, stderr, err := psh.shell.Execute(generatePSModify(dnsserver, domain, old, rec))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stderr != "" {
|
||||
fmt.Printf("STDERROR = %q\n", stderr)
|
||||
return fmt.Errorf("unexpected stderr from PSModify: %q", stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generatePSModify(dnsserver, domain string, old, rec *models.RecordConfig) string {
|
||||
// The simple way is to just remove the old record and insert the new record.
|
||||
return generatePSDelete(dnsserver, domain, old) + ` ; ` + generatePSCreate(dnsserver, domain, rec)
|
||||
// NB: SOA records can't be deleted. When we implement them, we'll
|
||||
// need to special case them and generate an in-place modification
|
||||
// command.
|
||||
}
|
||||
|
||||
// Note about the old generatePSModify:
|
||||
//
|
||||
// The old method is to generate PowerShell code that extracts the resource
|
||||
// record, clones it, makes modifications to the clone, and replaces the old
|
||||
// object with the modified clone. In theory this is cleaner.
|
||||
//
|
||||
// Sadly that technique is considerably slower (PowerShell seems to take a
|
||||
// long time doing it) and it is more brittle (each new rType seems to be a
|
||||
// new adventure).
|
||||
//
|
||||
// The other benefit of the Delete/Create method is that it more heavily
|
||||
// exercises existing code that is known to work.
|
||||
//
|
||||
// Sadly I can't bring myself to erase the code yet. I still hope this can
|
||||
// be fixed. Deep down I know we should just accept that Del/Create is better.
|
||||
|
||||
// if old.GetLabel() != rec.GetLabel() {
|
||||
// panic(fmt.Sprintf("generatePSModify assertion failed: %q != %q", old.GetLabel(), rec.GetLabel()))
|
||||
// }
|
||||
//
|
||||
// var b bytes.Buffer
|
||||
// fmt.Fprintf(&b, `echo "MODIFY %s %s %s old=(%s) new=(%s):"`, rec.GetLabel(), domain, rec.Type, old.GetTargetCombined(), rec.GetTargetCombined())
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// fmt.Fprintf(&b, "$OldObj = Get-DnsServerResourceRecord")
|
||||
// fmt.Fprintf(&b, ` -ZoneName "%s"`, domain)
|
||||
// fmt.Fprintf(&b, ` -Name "%s"`, old.GetLabel())
|
||||
// fmt.Fprintf(&b, ` -RRType "%s"`, old.Type)
|
||||
// fmt.Fprintf(&b, ` | Where-Object {$_.HostName eq "%s" -and -RRType -eq "%s" -and `, old.GetLabel(), rec.Type)
|
||||
// switch old.Type {
|
||||
// case "A":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.IPv4Address -eq "%s"`, old.GetTargetIP())
|
||||
// case "AAAA":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.IPv6Address -eq "%s"`, old.GetTargetIP())
|
||||
// //case "ATMA":
|
||||
// // fmt.Fprintf(&b, ` -Atma -Address <String> -AddressType {E164 | AESA}`, old.GetTargetField())
|
||||
// //case "AFSDB":
|
||||
// // fmt.Fprintf(&b, ` -Afsdb -ServerName <String> -SubType <UInt16>`, old.GetTargetField())
|
||||
// case "SRV":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.DomainName -eq "%s" -and $_.RecordData.Port -eq %d -and $_.RecordData.Priority -eq %d -and $_.RecordData.Weight -eq %d`, old.GetTargetField(), old.SrvPort, old.SrvPriority, old.SrvWeight)
|
||||
// case "CNAME":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.HostNameAlias -eq "%s"`, old.GetTargetField())
|
||||
// //case "X25":
|
||||
// // fmt.Fprintf(&b, ` -X25 -PsdnAddress <String>`, old.GetTargetField())
|
||||
// //case "WKS":
|
||||
// // fmt.Fprintf(&b, ` -Wks -InternetAddress <IPAddress> -InternetProtocol {UDP | TCP} -Service <String[]>`, old.GetTargetField())
|
||||
// case "TXT":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.DescriptiveText -eq "%s"`, old.GetTargetField())
|
||||
// //case "RT":
|
||||
// // fmt.Fprintf(&b, ` -RT -IntermediateHost <String> -Preference <UInt16>`, old.GetTargetField())
|
||||
// //case "RP":
|
||||
// // fmt.Fprintf(&b, ` -RP -Description <String> -ResponsiblePerson <String>`, old.GetTargetField())
|
||||
// case "PTR":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.PtrDomainName -eq "%s"`, old.GetTargetField())
|
||||
// case "NS":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.NameServer -eq "%s"`, old.GetTargetField())
|
||||
// case "MX":
|
||||
// fmt.Fprintf(&b, `$_.RecordData.MailExchange -eq "%s" -and $_.RecordData.Preference -eq %d`, old.GetTargetField(), old.MxPreference)
|
||||
// //case "ISDN":
|
||||
// // fmt.Fprintf(&b, ` -Isdn -IsdnNumber <String> -IsdnSubAddress <String>`, old.GetTargetField())
|
||||
// //case "HINFO":
|
||||
// // fmt.Fprintf(&b, ` -HInfo -Cpu <String> -OperatingSystem <String>`, old.GetTargetField())
|
||||
// //case "DNAME":
|
||||
// // fmt.Fprintf(&b, ` -DName -DomainNameAlias <String>`, old.GetTargetField())
|
||||
// //case "DHCID":
|
||||
// // fmt.Fprintf(&b, ` -DhcId -DhcpIdentifier <String>`, old.GetTargetField())
|
||||
// //case "TLSA":
|
||||
// // fmt.Fprintf(&b, ` -TLSA -CertificateAssociationData <System.String> -CertificateUsage {CAConstraint | ServiceCertificateConstraint | TrustAnchorAssertion | DomainIssuedCertificate} -MatchingType {ExactMatch | Sha256Hash | Sha512Hash} -Selector {FullCertificate | SubjectPublicKeyInfo}`, rec.GetTargetField())
|
||||
// default:
|
||||
// panic(fmt.Errorf("generatePSModify() has not implemented recType=%q recName=%q content=(%s))",
|
||||
// rec.Type, rec.GetLabel(), rec.GetTargetCombined()))
|
||||
// // We panic so that we quickly find any switch statements
|
||||
// // that have not been updated for a new RR type.
|
||||
// }
|
||||
// fmt.Fprintf(&b, "}")
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// fmt.Fprintf(&b, `if($OldObj.Length -ne 1){ throw "Error, multiple results for Get-DnsServerResourceRecord" }`)
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// fmt.Fprintf(&b, "$NewObj = $OldObj.Clone()")
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
//
|
||||
// if old.TTL != rec.TTL {
|
||||
// fmt.Fprintf(&b, `$NewObj.TimeToLive = New-TimeSpan -Seconds %d`, rec.TTL)
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// }
|
||||
// switch rec.Type {
|
||||
// case "A":
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.IPv4Address = "%s"`, rec.GetTargetIP())
|
||||
// case "AAAA":
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.IPv6Address = "%s"`, rec.GetTargetIP())
|
||||
// //case "ATMA":
|
||||
// // fmt.Fprintf(&b, ` -Atma -Address <String> -AddressType {E164 | AESA}`, rec.GetTargetField())
|
||||
// //case "AFSDB":
|
||||
// // fmt.Fprintf(&b, ` -Afsdb -ServerName <String> -SubType <UInt16>`, rec.GetTargetField())
|
||||
// case "SRV":
|
||||
// fmt.Fprintf(&b, ` -Srv -DomainName "%s" -Port %d -Priority %d -Weight %d`, rec.GetTargetField(), rec.SrvPort, rec.SrvPriority, rec.SrvWeight)
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.DomainName = "%s"`, rec.GetTargetField())
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.Port = %d`, rec.SrvPort)
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.Priority = %d`, rec.SrvPriority)
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.Weight = "%d"`, rec.SrvWeight)
|
||||
// case "CNAME":
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.HostNameAlias = "%s"`, rec.GetTargetField())
|
||||
// //case "X25":
|
||||
// // fmt.Fprintf(&b, ` -X25 -PsdnAddress <String>`, rec.GetTargetField())
|
||||
// //case "WKS":
|
||||
// // fmt.Fprintf(&b, ` -Wks -InternetAddress <IPAddress> -InternetProtocol {UDP | TCP} -Service <String[]>`, rec.GetTargetField())
|
||||
// case "TXT":
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.DescriptiveText = "%s"`, rec.GetTargetField())
|
||||
// //case "RT":
|
||||
// // fmt.Fprintf(&b, ` -RT -IntermediateHost <String> -Preference <UInt16>`, rec.GetTargetField())
|
||||
// //case "RP":
|
||||
// // fmt.Fprintf(&b, ` -RP -Description <String> -ResponsiblePerson <String>`, rec.GetTargetField())
|
||||
// case "PTR":
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.PtrDomainName = "%s"`, rec.GetTargetField())
|
||||
// case "NS":
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.NameServer = "%s"`, rec.GetTargetField())
|
||||
// case "MX":
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.MailExchange = "%s"`, rec.GetTargetField())
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// fmt.Fprintf(&b, `$NewObj.RecordData.Preference = "%d"`, rec.MxPreference)
|
||||
// //case "ISDN":
|
||||
// // fmt.Fprintf(&b, ` -Isdn -IsdnNumber <String> -IsdnSubAddress <String>`, rec.GetTargetField())
|
||||
// //case "HINFO":
|
||||
// // fmt.Fprintf(&b, ` -HInfo -Cpu <String> -OperatingSystem <String>`, rec.GetTargetField())
|
||||
// //case "DNAME":
|
||||
// // fmt.Fprintf(&b, ` -DName -DomainNameAlias <String>`, rec.GetTargetField())
|
||||
// //case "DHCID":
|
||||
// // fmt.Fprintf(&b, ` -DhcId -DhcpIdentifier <String>`, rec.GetTargetField())
|
||||
// //case "TLSA":
|
||||
// // fmt.Fprintf(&b, ` -TLSA -CertificateAssociationData <System.String> -CertificateUsage {CAConstraint | ServiceCertificateConstraint | TrustAnchorAssertion | DomainIssuedCertificate} -MatchingType {ExactMatch | Sha256Hash | Sha512Hash} -Selector {FullCertificate | SubjectPublicKeyInfo}`, rec.GetTargetField())
|
||||
// default:
|
||||
// panic(fmt.Errorf("generatePSModify() update has not implemented recType=%q recName=%q content=(%s))",
|
||||
// rec.Type, rec.GetLabel(), rec.GetTargetCombined()))
|
||||
// // We panic so that we quickly find any switch statements
|
||||
// // that have not been updated for a new RR type.
|
||||
// }
|
||||
// fmt.Fprintf(&b, " ; ")
|
||||
// //fmt.Printf("DEBUG CCMD: %s\n", b.String())
|
||||
//
|
||||
// fmt.Fprintf(&b, "Set-DnsServerResourceRecord")
|
||||
// fmt.Fprintf(&b, ` -ZoneName "%s"`, domain)
|
||||
// fmt.Fprintf(&b, ` -NewInputObject $NewObj -OldInputObject $OldObj`)
|
||||
//
|
||||
// fmt.Printf("DEBUG MCMD: %s", b.String())
|
||||
// return b.String()
|
169
providers/msdns/powershell_test.go
Normal file
169
providers/msdns/powershell_test.go
Normal file
@ -0,0 +1,169 @@
|
||||
package msdns
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
)
|
||||
|
||||
func Test_generatePSZoneAll(t *testing.T) {
|
||||
type args struct {
|
||||
dnsserver string
|
||||
domain string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "local",
|
||||
args: args{},
|
||||
want: `Get-DnsServerZone | ConvertTo-Json`,
|
||||
},
|
||||
{
|
||||
name: "remote",
|
||||
args: args{dnsserver: "mydnsserver"},
|
||||
want: `Get-DnsServerZone -ComputerName "mydnsserver" | ConvertTo-Json`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := generatePSZoneAll(tt.args.dnsserver); got != tt.want {
|
||||
t.Errorf("generatePSZoneAll() = got=(\n%s\n) want=(\n%s\n)", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_generatePSZoneDump(t *testing.T) {
|
||||
type args struct {
|
||||
domainname string
|
||||
dnsserver string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "local",
|
||||
args: args{domainname: "example.com"},
|
||||
want: `Get-DnsServerResourceRecord -ZoneName "example.com" | ConvertTo-Json -depth 4 > mytemp.json`,
|
||||
},
|
||||
{
|
||||
name: "remote",
|
||||
args: args{domainname: "example.com", dnsserver: "mydnsserver"},
|
||||
want: `Get-DnsServerResourceRecord -ComputerName "mydnsserver" -ZoneName "example.com" | ConvertTo-Json -depth 4 > mytemp.json`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := generatePSZoneDump(tt.args.dnsserver, tt.args.domainname, "mytemp.json"); got != tt.want {
|
||||
t.Errorf("generatePSZoneDump() = got=(\n%s\n) want=(\n%s\n)", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//func Test_generatePSDelete(t *testing.T) {
|
||||
// type args struct {
|
||||
// domain string
|
||||
// rec *models.RecordConfig
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// args args
|
||||
// want string
|
||||
// }{
|
||||
// // TODO: Add test cases.
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// if got := generatePSDelete(tt.args.domain, tt.args.rec); got != tt.want {
|
||||
// t.Errorf("generatePSDelete() = %v, want %v", got, tt.want)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//}
|
||||
|
||||
// func Test_generatePSCreate(t *testing.T) {
|
||||
// type args struct {
|
||||
// domain string
|
||||
// rec *models.RecordConfig
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// args args
|
||||
// want string
|
||||
// }{
|
||||
// // TODO: Add test cases.
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// if got := generatePSCreate(tt.args.domain, tt.args.rec); got != tt.want {
|
||||
// t.Errorf("generatePSCreate() = %v, want %v", got, tt.want)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
func Test_generatePSModify(t *testing.T) {
|
||||
|
||||
recA1 := &models.RecordConfig{
|
||||
Type: "A",
|
||||
Name: "@",
|
||||
Target: "1.2.3.4",
|
||||
}
|
||||
recA2 := &models.RecordConfig{
|
||||
Type: "A",
|
||||
Name: "@",
|
||||
Target: "10.20.30.40",
|
||||
}
|
||||
|
||||
recMX1 := &models.RecordConfig{
|
||||
Type: "MX",
|
||||
Name: "@",
|
||||
Target: "foo.com.",
|
||||
MxPreference: 5,
|
||||
}
|
||||
recMX2 := &models.RecordConfig{
|
||||
Type: "MX",
|
||||
Name: "@",
|
||||
Target: "foo2.com.",
|
||||
MxPreference: 50,
|
||||
}
|
||||
|
||||
type args struct {
|
||||
domain string
|
||||
dnsserver string
|
||||
old *models.RecordConfig
|
||||
rec *models.RecordConfig
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{name: "A", args: args{domain: "example.com", dnsserver: "", old: recA1, rec: recA2},
|
||||
want: `echo DELETE "A" "@" "1.2.3.4" ; Remove-DnsServerResourceRecord -Force -ZoneName "example.com" -Name "@" -RRType "A" -RecordData "1.2.3.4" ; echo CREATE "A" "@" "10.20.30.40" ; Add-DnsServerResourceRecord -ZoneName "example.com" -Name "@" -TimeToLive $(New-TimeSpan -Seconds 0) -A -IPv4Address "10.20.30.40"`,
|
||||
},
|
||||
{name: "MX1", args: args{domain: "example.com", dnsserver: "", old: recMX1, rec: recMX2},
|
||||
want: `echo DELETE "MX" "@" "5 foo.com." ; Remove-DnsServerResourceRecord -Force -ZoneName "example.com" -Name "@" -RRType "MX" -RecordData 5,"foo.com." ; echo CREATE "MX" "@" "50 foo2.com." ; Add-DnsServerResourceRecord -ZoneName "example.com" -Name "@" -TimeToLive $(New-TimeSpan -Seconds 0) -MX -MailExchange "foo2.com." -Preference 50`,
|
||||
},
|
||||
{name: "A-remote", args: args{domain: "example.com", dnsserver: "myremote", old: recA1, rec: recA2},
|
||||
want: `echo DELETE "A" "@" "1.2.3.4" ; Remove-DnsServerResourceRecord -ComputerName "myremote" -Force -ZoneName "example.com" -Name "@" -RRType "A" -RecordData "1.2.3.4" ; echo CREATE "A" "@" "10.20.30.40" ; Add-DnsServerResourceRecord -ComputerName "myremote" -ZoneName "example.com" -Name "@" -TimeToLive $(New-TimeSpan -Seconds 0) -A -IPv4Address "10.20.30.40"`,
|
||||
},
|
||||
{name: "MX1-remote", args: args{domain: "example.com", dnsserver: "yourremote", old: recMX1, rec: recMX2},
|
||||
want: `echo DELETE "MX" "@" "5 foo.com." ; Remove-DnsServerResourceRecord -ComputerName "yourremote" -Force -ZoneName "example.com" -Name "@" -RRType "MX" -RecordData 5,"foo.com." ; echo CREATE "MX" "@" "50 foo2.com." ; Add-DnsServerResourceRecord -ComputerName "yourremote" -ZoneName "example.com" -Name "@" -TimeToLive $(New-TimeSpan -Seconds 0) -MX -MailExchange "foo2.com." -Preference 50`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := generatePSModify(tt.args.dnsserver, tt.args.domain, tt.args.old, tt.args.rec); strings.TrimSpace(got) != strings.TrimSpace(tt.want) {
|
||||
t.Errorf("generatePSModify() = got=(\n%s\n) want=(\n%s\n)", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
48
providers/msdns/types.go
Normal file
48
providers/msdns/types.go
Normal file
@ -0,0 +1,48 @@
|
||||
package msdns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/StackExchange/dnscontrol/v3/models"
|
||||
)
|
||||
|
||||
// DNSAccessor describes a system that can access Microsoft DNS.
|
||||
type DNSAccessor interface {
|
||||
Exit()
|
||||
GetDNSServerZoneAll(dnsserver string) ([]string, error)
|
||||
GetDNSZoneRecords(dnsserver, domain string) ([]nativeRecord, error)
|
||||
RecordCreate(dnsserver, domain string, rec *models.RecordConfig) error
|
||||
RecordDelete(dnsserver, domain string, rec *models.RecordConfig) error
|
||||
RecordModify(dnsserver, domain string, old, rec *models.RecordConfig) error
|
||||
}
|
||||
|
||||
// nativeRecord the JSON received from PowerShell when listing all DNS
|
||||
// records in a zone.
|
||||
type nativeRecord struct {
|
||||
//CimClass interface{} `json:"CimClass"`
|
||||
//CimInstanceProperties interface{} `json:"CimInstanceProperties"`
|
||||
//CimSystemProperties interface{} `json:"CimSystemProperties"`
|
||||
//DistinguishedName string `json:"DistinguishedName"`
|
||||
//RecordClass string `json:"RecordClass"`
|
||||
RecordType string `json:"RecordType"`
|
||||
HostName string `json:"HostName"`
|
||||
RecordData struct {
|
||||
CimInstanceProperties []ciProperty `json:"CimInstanceProperties"`
|
||||
} `json:"RecordData"`
|
||||
TimeToLive struct {
|
||||
TotalSeconds float64 `json:"TotalSeconds"`
|
||||
} `json:"TimeToLive"`
|
||||
}
|
||||
|
||||
type ciProperty struct {
|
||||
Name string `json:"Name"`
|
||||
Value json.RawMessage `json:"Value,omitempty"`
|
||||
}
|
||||
|
||||
type ciValueDuration struct {
|
||||
TotalSeconds float64 `json:"TotalSeconds"`
|
||||
}
|
||||
|
||||
// NB(tlim): The above structs were created using the help of:
|
||||
// Get-DnsServerResourceRecord -ZoneName example.com | where { $_.RecordType -eq "SOA" } | select $_.RecordData | ConvertTo-Json -depth 10
|
||||
// and pass it to https://mholt.github.io/json-to-go/
|
Reference in New Issue
Block a user