mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
Merge branch 'master' into tlim_corrector
This commit is contained in:
1
OWNERS
1
OWNERS
@ -25,6 +25,7 @@ providers/internetbs @pragmaton
|
|||||||
providers/inwx @patschi
|
providers/inwx @patschi
|
||||||
providers/msdns @tlimoncelli
|
providers/msdns @tlimoncelli
|
||||||
providers/linode @koesie10
|
providers/linode @koesie10
|
||||||
|
providers/luadns @riku22
|
||||||
providers/namecheap @willpower232
|
providers/namecheap @willpower232
|
||||||
# providers/namedotcom NEEDS VOLUNTEER
|
# providers/namedotcom NEEDS VOLUNTEER
|
||||||
providers/netcup @kordianbruck
|
providers/netcup @kordianbruck
|
||||||
|
@ -42,6 +42,7 @@ Currently supported DNS providers:
|
|||||||
- Hurricane Electric DNS
|
- Hurricane Electric DNS
|
||||||
- INWX
|
- INWX
|
||||||
- Linode
|
- Linode
|
||||||
|
- LuaDNS
|
||||||
- Microsoft Windows Server DNS Server
|
- Microsoft Windows Server DNS Server
|
||||||
- NS1
|
- NS1
|
||||||
- Name.com
|
- Name.com
|
||||||
|
@ -111,6 +111,7 @@
|
|||||||
* [Internet.bs](providers/internetbs.md)
|
* [Internet.bs](providers/internetbs.md)
|
||||||
* [INWX](providers/inwx.md)
|
* [INWX](providers/inwx.md)
|
||||||
* [Linode](providers/linode.md)
|
* [Linode](providers/linode.md)
|
||||||
|
* [LuaDNS](providers/luadns.md)
|
||||||
* [Microsoft DNS Server on Microsoft Windows Server](providers/msdns.md)
|
* [Microsoft DNS Server on Microsoft Windows Server](providers/msdns.md)
|
||||||
* [Namecheap](providers/namecheap.md)
|
* [Namecheap](providers/namecheap.md)
|
||||||
* [Name.com](providers/namedotcom.md)
|
* [Name.com](providers/namedotcom.md)
|
||||||
|
@ -40,6 +40,7 @@ If a feature is definitively not supported for whatever reason, we would also li
|
|||||||
| `INTERNETBS` | ❌ | ❌ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | ❔ |
|
| `INTERNETBS` | ❌ | ❌ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ✅ | ❔ |
|
||||||
| `INWX` | ❌ | ✅ | ✅ | ❌ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ |
|
| `INWX` | ❌ | ✅ | ✅ | ❌ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| `LINODE` | ❌ | ✅ | ❌ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | ✅ |
|
| `LINODE` | ❌ | ✅ | ❌ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | ✅ |
|
||||||
|
| `LUADNS` | ✅ | ✅ | ❌ | ✅ | ❔ | ✅ | ✅ | ❔ | ❔ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| `MSDNS` | ✅ | ✅ | ❌ | ❌ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | ✅ |
|
| `MSDNS` | ✅ | ✅ | ❌ | ❌ | ❔ | ❌ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | ✅ |
|
||||||
| `NAMECHEAP` | ❌ | ✅ | ✅ | ✅ | ❔ | ✅ | ❌ | ❔ | ❔ | ❌ | ❔ | ❌ | ❔ | ❌ | ❌ | ❌ | ✅ |
|
| `NAMECHEAP` | ❌ | ✅ | ✅ | ✅ | ❔ | ✅ | ❌ | ❔ | ❔ | ❌ | ❔ | ❌ | ❔ | ❌ | ❌ | ❌ | ✅ |
|
||||||
| `NAMEDOTCOM` | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ❌ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | ✅ |
|
| `NAMEDOTCOM` | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ❌ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ✅ | ❌ | ✅ | ✅ |
|
||||||
@ -120,6 +121,7 @@ Providers in this category and their maintainers are:
|
|||||||
|`INTERNETBS`|@pragmaton|
|
|`INTERNETBS`|@pragmaton|
|
||||||
|`INWX`|@svenpeter42|
|
|`INWX`|@svenpeter42|
|
||||||
|`LINODE`|@koesie10|
|
|`LINODE`|@koesie10|
|
||||||
|
|`LUADNS`|@riku22|
|
||||||
|`NAMECHEAP`|@willpower232|
|
|`NAMECHEAP`|@willpower232|
|
||||||
|`NETCUP`|@kordianbruck|
|
|`NETCUP`|@kordianbruck|
|
||||||
|`NETLIFY`|@SphericalKat|
|
|`NETLIFY`|@SphericalKat|
|
||||||
|
42
documentation/providers/luadns.md
Normal file
42
documentation/providers/luadns.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
## Configuration
|
||||||
|
|
||||||
|
To use this provider, add an entry to `creds.json` with `TYPE` set to `LUADNS`
|
||||||
|
along with your [email and API key](https://www.luadns.com/api.html#authentication).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{% code title="creds.json" %}
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"luadns": {
|
||||||
|
"TYPE": "LUADNS",
|
||||||
|
"email": "your-email",
|
||||||
|
"apikey": "your-api-key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% endcode %}
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
This provider does not recognize any special metadata fields unique to LuaDNS.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
An example `dnsconfig.js` configuration:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var REG_NONE = NewRegistrar("none");
|
||||||
|
var DSP_LUADNS = NewDnsProvider("luadns");
|
||||||
|
|
||||||
|
D("example.tld", REG_NONE, DnsProvider(DSP_LUADNS),
|
||||||
|
A("test", "1.2.3.4")
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Activation
|
||||||
|
[Create API key](https://api.luadns.com/api_keys).
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
- LuaDNS cannot change the default nameserver TTL in `nameserver_ttl`, it is forced to fixed at 86400("1d").
|
||||||
|
This is not the case if you are using vanity nameservers.
|
||||||
|
- This provider does not currently support the "FORWARD" and "REDIRECT" record types.
|
||||||
|
- The API is available on the LuaDNS free plan, but due to the limit of 30 records, some tests will fail when doing integration tests.
|
@ -134,6 +134,11 @@
|
|||||||
"domain": "$LINODE_DOMAIN",
|
"domain": "$LINODE_DOMAIN",
|
||||||
"token": "$LINODE_TOKEN"
|
"token": "$LINODE_TOKEN"
|
||||||
},
|
},
|
||||||
|
"LUADNS": {
|
||||||
|
"domain": "$LUADNS_DOMAIN",
|
||||||
|
"email": "$LUADNS_EMAIL",
|
||||||
|
"apikey": "$LUADNS_APIKEY"
|
||||||
|
},
|
||||||
"MSDNS": {
|
"MSDNS": {
|
||||||
"domain": "$MSDNS_DOMAIN",
|
"domain": "$MSDNS_DOMAIN",
|
||||||
"dnsserver": "$MSDNS_DNSSERVER",
|
"dnsserver": "$MSDNS_DNSSERVER",
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
_ "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/inwx"
|
||||||
_ "github.com/StackExchange/dnscontrol/v3/providers/linode"
|
_ "github.com/StackExchange/dnscontrol/v3/providers/linode"
|
||||||
|
_ "github.com/StackExchange/dnscontrol/v3/providers/luadns"
|
||||||
_ "github.com/StackExchange/dnscontrol/v3/providers/msdns"
|
_ "github.com/StackExchange/dnscontrol/v3/providers/msdns"
|
||||||
_ "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"
|
||||||
|
251
providers/luadns/api.go
Normal file
251
providers/luadns/api.go
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
package luadns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Api layer for LuaDNS
|
||||||
|
|
||||||
|
const (
|
||||||
|
apiURL = "https://api.luadns.com/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type luadnsProvider struct {
|
||||||
|
domainIndex map[string]uint32
|
||||||
|
nameserversNames []string
|
||||||
|
creds struct {
|
||||||
|
email string
|
||||||
|
apikey string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorResponse struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
RequestID string `json:"request_id"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type userInfoResponse struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TTL uint32 `json:"ttl"`
|
||||||
|
NameServers []string `json:"name_servers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type zoneRecord struct {
|
||||||
|
ID uint32 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type zoneResponse []zoneRecord
|
||||||
|
|
||||||
|
type domainRecord struct {
|
||||||
|
ID uint32 `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
TTL uint32 `json:"ttl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type recordResponse []domainRecord
|
||||||
|
|
||||||
|
type requestParams map[string]string
|
||||||
|
type jsonRequestParams map[string]any
|
||||||
|
|
||||||
|
func (l *luadnsProvider) fetchAvailableNameservers() error {
|
||||||
|
l.nameserversNames = nil
|
||||||
|
var bodyString, err = l.get("/users/me", "GET", requestParams{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed fetching available nameservers list from LuaDNS: %s", err)
|
||||||
|
}
|
||||||
|
var ui userInfoResponse
|
||||||
|
json.Unmarshal(bodyString, &ui)
|
||||||
|
l.nameserversNames = ui.NameServers
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) fetchDomainList() error {
|
||||||
|
l.domainIndex = map[string]uint32{}
|
||||||
|
var bodyString, err = l.get("/zones", "GET", requestParams{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed fetching domain list from LuaDNS: %s", err)
|
||||||
|
}
|
||||||
|
var dr zoneResponse
|
||||||
|
json.Unmarshal(bodyString, &dr)
|
||||||
|
for _, domain := range dr {
|
||||||
|
l.domainIndex[domain.Name] = domain.ID
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) getDomainID(name string) (uint32, error) {
|
||||||
|
if l.domainIndex == nil {
|
||||||
|
if err := l.fetchDomainList(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id, ok := l.domainIndex[name]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("'%s' not a zone in luadns account", name)
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) createDomain(domain string) error {
|
||||||
|
params := jsonRequestParams{
|
||||||
|
"name": domain,
|
||||||
|
}
|
||||||
|
if _, err := l.get("/zones", "POST", params); err != nil {
|
||||||
|
return fmt.Errorf("failed create domain (LuaDNS): %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) createRecord(domainID uint32, rec jsonRequestParams) error {
|
||||||
|
if _, err := l.get(fmt.Sprintf("/zones/%d/records", domainID), "POST", rec); err != nil {
|
||||||
|
return fmt.Errorf("failed create record (LuaDNS): %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) deleteRecord(domainID uint32, recordID uint32) error {
|
||||||
|
if _, err := l.get(fmt.Sprintf("/zones/%d/records/%d", domainID, recordID), "DELETE", requestParams{}); err != nil {
|
||||||
|
return fmt.Errorf("failed delete record (LuaDNS): %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) modifyRecord(domainID uint32, recordID uint32, rec jsonRequestParams) error {
|
||||||
|
if _, err := l.get(fmt.Sprintf("/zones/%d/records/%d", domainID, recordID), "PUT", rec); err != nil {
|
||||||
|
return fmt.Errorf("failed update (LuaDNS): %s", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) getRecords(domainID uint32) ([]domainRecord, error) {
|
||||||
|
var bodyString, err = l.get(fmt.Sprintf("/zones/%d/records", domainID), "GET", requestParams{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed fetching record list from LuaDNS: %s", err)
|
||||||
|
}
|
||||||
|
var dr recordResponse
|
||||||
|
json.Unmarshal(bodyString, &dr)
|
||||||
|
var records []domainRecord
|
||||||
|
for _, rec := range dr {
|
||||||
|
if rec.Type == "SOA" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
records = append(records, rec)
|
||||||
|
}
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) get(endpoint string, method string, params any) ([]byte, error) {
|
||||||
|
client := &http.Client{}
|
||||||
|
var req, err = l.makeRequest(endpoint, method, params)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.SetBasicAuth(l.creds.email, l.creds.apikey)
|
||||||
|
// LuaDNS has a rate limit of 1200 request per 5 minute.
|
||||||
|
// So we do a very primitive rate limiting here - delay every request for 250ms - so max. 4 requests/second.
|
||||||
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == 200 {
|
||||||
|
bodyString, _ := io.ReadAll(resp.Body)
|
||||||
|
return bodyString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyString, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
|
var errResp errorResponse
|
||||||
|
err = json.Unmarshal(bodyString, &errResp)
|
||||||
|
if err != nil {
|
||||||
|
return bodyString, fmt.Errorf("LuaDNS API Error: %s URL:%s%s", string(bodyString), req.Host, req.URL.RequestURI())
|
||||||
|
}
|
||||||
|
return bodyString, fmt.Errorf("LuaDNS API error: %s URL:%s%s", errResp.Message, req.Host, req.URL.RequestURI())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) makeRequest(endpoint string, method string, params any) (*http.Request, error) {
|
||||||
|
switch v := params.(type) {
|
||||||
|
case requestParams:
|
||||||
|
req, err := http.NewRequest(method, apiURL+endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
q := req.URL.Query()
|
||||||
|
for pName, pValue := range v {
|
||||||
|
q.Add(pName, pValue)
|
||||||
|
}
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
return req, nil
|
||||||
|
case jsonRequestParams:
|
||||||
|
requestJSON, err := json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(method, apiURL+endpoint, bytes.NewBuffer(requestJSON))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
return req, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid request type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nativeToRecord(domain string, r *domainRecord) *models.RecordConfig {
|
||||||
|
rc := &models.RecordConfig{
|
||||||
|
Type: r.Type,
|
||||||
|
TTL: r.TTL,
|
||||||
|
Original: r,
|
||||||
|
}
|
||||||
|
rc.SetLabelFromFQDN(r.Name, domain)
|
||||||
|
switch rtype := rc.Type; rtype {
|
||||||
|
case "TXT":
|
||||||
|
rc.SetTargetTXT(r.Content)
|
||||||
|
default:
|
||||||
|
rc.PopulateFromString(rtype, r.Content, domain)
|
||||||
|
}
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
|
||||||
|
func recordsToNative(rc *models.RecordConfig) jsonRequestParams {
|
||||||
|
r := jsonRequestParams{
|
||||||
|
"name": fmt.Sprintf("%s.", rc.GetLabelFQDN()),
|
||||||
|
"type": rc.Type,
|
||||||
|
"ttl": rc.TTL,
|
||||||
|
}
|
||||||
|
switch rtype := rc.Type; rtype {
|
||||||
|
case "TXT":
|
||||||
|
r["content"] = rc.GetTargetTXTJoined()
|
||||||
|
default:
|
||||||
|
r["content"] = rc.GetTargetCombined()
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNS(dc *models.DomainConfig) {
|
||||||
|
newList := make([]*models.RecordConfig, 0, len(dc.Records))
|
||||||
|
for _, rec := range dc.Records {
|
||||||
|
// LuaDNS does not support changing the TTL of the default nameservers, so forcefully change the TTL to 86400.
|
||||||
|
if rec.Type == "NS" && strings.HasSuffix(rec.GetTargetField(), ".luadns.net.") && rec.TTL != 86400 {
|
||||||
|
rec.TTL = 86400
|
||||||
|
}
|
||||||
|
newList = append(newList, rec)
|
||||||
|
}
|
||||||
|
dc.Records = newList
|
||||||
|
}
|
15
providers/luadns/auditrecords.go
Normal file
15
providers/luadns/auditrecords.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package luadns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/models"
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/pkg/rejectif"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuditRecords returns a list of errors corresponding to the records
|
||||||
|
// that aren't supported by this provider. If all records are
|
||||||
|
// supported, an empty list is returned.
|
||||||
|
func AuditRecords(records []*models.RecordConfig) []error {
|
||||||
|
a := rejectif.Auditor{}
|
||||||
|
a.Add("TXT", rejectif.TxtIsEmpty) // Last verified 2023-03-03
|
||||||
|
return a.Audit(records)
|
||||||
|
}
|
212
providers/luadns/luadnsProvider.go
Normal file
212
providers/luadns/luadnsProvider.go
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
package luadns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/models"
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/pkg/diff2"
|
||||||
|
// "github.com/StackExchange/dnscontrol/v3/pkg/transform"
|
||||||
|
"github.com/StackExchange/dnscontrol/v3/providers"
|
||||||
|
// "github.com/miekg/dns/dnsutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
LuaDNS API DNS provider:
|
||||||
|
|
||||||
|
Info required in `creds.json`:
|
||||||
|
- email
|
||||||
|
- apikey
|
||||||
|
*/
|
||||||
|
|
||||||
|
var features = providers.DocumentationNotes{
|
||||||
|
providers.CanGetZones: providers.Can(),
|
||||||
|
providers.CanUseAlias: providers.Can(),
|
||||||
|
providers.CanUseCAA: providers.Can(),
|
||||||
|
providers.CanUsePTR: providers.Can(),
|
||||||
|
providers.CanUseSRV: providers.Can(),
|
||||||
|
providers.CanUseSSHFP: providers.Can(),
|
||||||
|
providers.CanUseTLSA: providers.Can(),
|
||||||
|
providers.DocCreateDomains: providers.Can(),
|
||||||
|
providers.DocDualHost: providers.Can(),
|
||||||
|
providers.DocOfficiallySupported: providers.Can(),
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
fns := providers.DspFuncs{
|
||||||
|
Initializer: NewLuaDNS,
|
||||||
|
RecordAuditor: AuditRecords,
|
||||||
|
}
|
||||||
|
providers.RegisterDomainServiceProviderType("LUADNS", fns, features)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLuaDNS creates the provider.
|
||||||
|
func NewLuaDNS(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
|
||||||
|
l := &luadnsProvider{}
|
||||||
|
l.creds.email, l.creds.apikey = m["email"], m["apikey"]
|
||||||
|
if l.creds.email == "" || l.creds.apikey == "" {
|
||||||
|
return nil, fmt.Errorf("missing LuaDNS email or apikey")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a domain to validate authentication
|
||||||
|
if err := l.fetchDomainList(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNameservers returns the nameservers for a domain.
|
||||||
|
func (l *luadnsProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
|
||||||
|
if len(l.nameserversNames) == 0 {
|
||||||
|
l.fetchAvailableNameservers()
|
||||||
|
}
|
||||||
|
return models.ToNameserversStripTD(l.nameserversNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListZones returns a list of the DNS zones.
|
||||||
|
func (l *luadnsProvider) ListZones() ([]string, error) {
|
||||||
|
if err := l.fetchDomainList(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
zones := make([]string, 0, len(l.domainIndex))
|
||||||
|
for d := range l.domainIndex {
|
||||||
|
zones = append(zones, d)
|
||||||
|
}
|
||||||
|
return zones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
|
||||||
|
func (l *luadnsProvider) GetZoneRecords(domain string) (models.Records, error) {
|
||||||
|
domainID, err := l.getDomainID(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
records, err := l.getRecords(domainID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
existingRecords := make([]*models.RecordConfig, len(records))
|
||||||
|
for i := range records {
|
||||||
|
existingRecords[i] = nativeToRecord(domain, &records[i])
|
||||||
|
}
|
||||||
|
return existingRecords, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDomainCorrections returns a list of corrections to update a domain.
|
||||||
|
func (l *luadnsProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
|
||||||
|
err := dc.Punycode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
domainID, err := l.getDomainID(dc.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
records, err := l.GetZoneRecords(dc.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNS(dc)
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
models.PostProcessRecords(records)
|
||||||
|
|
||||||
|
var corrections []*models.Correction
|
||||||
|
var corrs []*models.Correction
|
||||||
|
if !diff2.EnableDiff2 {
|
||||||
|
differ := diff.New(dc)
|
||||||
|
_, create, del, mod, err := differ.IncrementalDiff(records)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
corrections := []*models.Correction{}
|
||||||
|
for _, d := range del {
|
||||||
|
corrs := l.makeDeleteCorrection(d.Existing, domainID, d.String())
|
||||||
|
corrections = append(corrections, corrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range create {
|
||||||
|
corrs := l.makeCreateCorrection(d.Desired, domainID, d.String())
|
||||||
|
corrections = append(corrections, corrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range mod {
|
||||||
|
corrs := l.makeChangeCorrection(d.Existing, d.Desired, domainID, d.String())
|
||||||
|
corrections = append(corrections, corrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return corrections, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
changes, err := diff2.ByRecord(records, dc, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, change := range changes {
|
||||||
|
msg := change.Msgs[0]
|
||||||
|
switch change.Type {
|
||||||
|
case diff2.REPORT:
|
||||||
|
corrs = []*models.Correction{{Msg: change.MsgsJoined}}
|
||||||
|
case diff2.CREATE:
|
||||||
|
corrs = l.makeCreateCorrection(change.New[0], domainID, msg)
|
||||||
|
case diff2.CHANGE:
|
||||||
|
corrs = l.makeChangeCorrection(change.Old[0], change.New[0], domainID, msg)
|
||||||
|
case diff2.DELETE:
|
||||||
|
corrs = l.makeDeleteCorrection(change.Old[0], domainID, msg)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unhandled inst.Type %s", change.Type))
|
||||||
|
}
|
||||||
|
corrections = append(corrections, corrs...)
|
||||||
|
}
|
||||||
|
return corrections, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) makeCreateCorrection(newrec *models.RecordConfig, domainID uint32, msg string) []*models.Correction {
|
||||||
|
req := recordsToNative(newrec)
|
||||||
|
return []*models.Correction{{
|
||||||
|
Msg: msg,
|
||||||
|
F: func() error {
|
||||||
|
return l.createRecord(domainID, req)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) makeChangeCorrection(oldrec *models.RecordConfig, newrec *models.RecordConfig, domainID uint32, msg string) []*models.Correction {
|
||||||
|
recordID := oldrec.Original.(*domainRecord).ID
|
||||||
|
req := recordsToNative(newrec)
|
||||||
|
return []*models.Correction{{
|
||||||
|
Msg: fmt.Sprintf("%s, LuaDNS ID: %d", msg, recordID),
|
||||||
|
F: func() error {
|
||||||
|
return l.modifyRecord(domainID, recordID, req)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *luadnsProvider) makeDeleteCorrection(deleterec *models.RecordConfig, domainID uint32, msg string) []*models.Correction {
|
||||||
|
recordID := deleterec.Original.(*domainRecord).ID
|
||||||
|
return []*models.Correction{{
|
||||||
|
Msg: fmt.Sprintf("%s, LuaDNS ID: %d", msg, recordID),
|
||||||
|
F: func() error {
|
||||||
|
return l.deleteRecord(domainID, recordID)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureZoneExists creates a zone if it does not exist
|
||||||
|
func (l *luadnsProvider) EnsureZoneExists(domain string) error {
|
||||||
|
if l.domainIndex == nil {
|
||||||
|
if err := l.fetchDomainList(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := l.domainIndex[domain]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.createDomain(domain)
|
||||||
|
}
|
Reference in New Issue
Block a user