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

NEW PROVIDER: Packetframe (#1347)

* Implement Packetframe provider

* Packetframe: Move to authorization and fix multiple TXT records

* AKAMAIEDGEDNS: fix staticcheck warnings/errors (#1346)

* downcase TLSA

* Akamai provider

* Akamai provider

* EdgeDNS provider

* AkamaiEdgeDNS provider

* AkamaiEdgeDNS provider

* AkamaiEdgeDNS provider

* AKAMAIEDGEDNS: fix staticcheck warnings/errors

Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>

* PacketframeProvider minor cleanup

* Packetframe v4 moved to production

* Packetframe Provider: Finish the rest of provider steps

* Packetframe: Make stylistic changes, update nameservers, apikey -> token

Co-authored-by: Steven Vernick <78868407+svernick@users.noreply.github.com>
Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
Hampton
2022-01-05 12:09:46 -05:00
committed by GitHub
parent 368be4e57a
commit ea20c13e67
10 changed files with 515 additions and 0 deletions

1
OWNERS
View File

@ -31,4 +31,5 @@ providers/oracle @kallsyms
providers/vultr @pgaskin providers/vultr @pgaskin
providers/ovh @masterzen providers/ovh @masterzen
providers/powerdns @jpbede providers/powerdns @jpbede
providers/packetframe @hamptonmoore
providers/transip @blackshadev providers/transip @blackshadev

View File

@ -44,6 +44,7 @@ Currently supported DNS providers:
- OVH - OVH
- OctoDNS - OctoDNS
- Oracle Cloud - Oracle Cloud
- Packetframe
- PowerDNS - PowerDNS
- SoftLayer - SoftLayer
- TransIP - TransIP

View File

@ -39,6 +39,7 @@
<th class="rotate"><div><span>OPENSRS</span></div></th> <th class="rotate"><div><span>OPENSRS</span></div></th>
<th class="rotate"><div><span>ORACLE</span></div></th> <th class="rotate"><div><span>ORACLE</span></div></th>
<th class="rotate"><div><span>OVH</span></div></th> <th class="rotate"><div><span>OVH</span></div></th>
<th class="rotate"><div><span>PACKETFRAME</span></div></th>
<th class="rotate"><div><span>POWERDNS</span></div></th> <th class="rotate"><div><span>POWERDNS</span></div></th>
<th class="rotate"><div><span>ROUTE53</span></div></th> <th class="rotate"><div><span>ROUTE53</span></div></th>
<th class="rotate"><div><span>SOFTLAYER</span></div></th> <th class="rotate"><div><span>SOFTLAYER</span></div></th>
@ -151,6 +152,9 @@
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -280,6 +284,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr> </tr>
<tr> <tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="The provider has registrar capabilities to set nameservers for zones">Registrar</th> <th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="The provider has registrar capabilities to set nameservers for zones">Registrar</th>
@ -385,6 +392,9 @@
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -475,6 +485,7 @@
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success" data-toggle="tooltip" data-container="body" data-placement="top" title="Needs to be enabled in PowerDNS first"> <td class="success" data-toggle="tooltip" data-container="body" data-placement="top" title="Needs to be enabled in PowerDNS first">
<a href="https://doc.powerdns.com/authoritative/guides/alias.html"><i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i></a> <a href="https://doc.powerdns.com/authoritative/guides/alias.html"><i class="fa has-tooltip fa-check text-success" aria-hidden="true"></i></a>
</td> </td>
@ -540,6 +551,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -635,6 +647,7 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -742,6 +755,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td class="danger"> <td class="danger">
@ -805,6 +821,7 @@
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -859,6 +876,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
</tr> </tr>
<tr> <tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Driver has explicitly implemented SRV record management">SRV</th> <th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Driver has explicitly implemented SRV record management">SRV</th>
@ -964,6 +982,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr> </tr>
<tr> <tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage SSHFP records">SSHFP</th> <th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider can manage SSHFP records">SSHFP</th>
@ -1034,6 +1055,7 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -1123,6 +1145,7 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -1175,6 +1198,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
</tr> </tr>
<tr> <tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports Route 53 limited ALIAS">R53_ALIAS</th> <th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports Route 53 limited ALIAS">R53_ALIAS</th>
@ -1216,6 +1240,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -1269,6 +1294,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
</tr> </tr>
<tr> <tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding DS records">DS</th> <th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding DS records">DS</th>
@ -1332,6 +1358,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
<td class="danger"> <td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i> <i class="fa fa-times text-danger" aria-hidden="true"></i>
</td> </td>
@ -1379,6 +1406,7 @@
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td> <td><i class="fa fa-minus dim"></i></td>
<td><i class="fa fa-minus dim"></i></td>
</tr> </tr>
<tr> <tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding AKAMAICDN records">AKAMAICDN</th> <th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="Provider supports adding AKAMAICDN records">AKAMAICDN</th>
@ -1508,6 +1536,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -1619,6 +1650,9 @@
<td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="New domains require registration"> <td class="danger" data-toggle="tooltip" data-container="body" data-placement="top" title="New domains require registration">
<i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i> <i class="fa has-tooltip fa-times text-danger" aria-hidden="true"></i>
</td> </td>
<td class="danger">
<i class="fa fa-times text-danger" aria-hidden="true"></i>
</td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
@ -1751,6 +1785,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i>
</td>
</tr> </tr>
<tr> <tr>
<th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="indicates the dnscontrol get-zones subcommand is implemented.">get-zones</th> <th class="row-header" style="text-decoration: underline;" data-toggle="tooltip" data-container="body" data-placement="top" title="indicates the dnscontrol get-zones subcommand is implemented.">get-zones</th>
@ -1841,6 +1878,9 @@
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>
<td class="info">
<i class="fa fa-circle-o text-info" aria-hidden="true"></i>
</td>
<td class="success"> <td class="success">
<i class="fa fa-check text-success" aria-hidden="true"></i> <i class="fa fa-check text-success" aria-hidden="true"></i>
</td> </td>

View File

@ -0,0 +1,33 @@
---
name: Packetframe
title: Packetframe Provider
layout: default
jsId: PACKETFRAME
---
# Packetframe Provider
## Configuration
In your credentials file, you must provide your Packetframe Token which can be extracted from the `token` cookie on packetframe.com
{% highlight json %}
{
"packetframe": {
"token": "your-packetframe-token"
}
}
{% endhighlight %}
## Metadata
This provider does not recognize any special metadata fields unique to Packetframe.
## Usage
Example Javascript:
{% highlight js %}
var REG_NONE = NewRegistrar('none', 'NONE')
var PACKETFRAME = NewDnsProvider("packetframe", "PACKETFRAME");
D("example.tld", REG_NONE, DnsProvider(PACKETFRAME),
A("test","1.2.3.4")
);
{%endhighlight%}

View File

@ -100,6 +100,7 @@ Providers in this category and their maintainers are:
* `OPENSRS` @pierre-emmanuelJ * `OPENSRS` @pierre-emmanuelJ
* `ORACLE` @kallsyms * `ORACLE` @kallsyms
* `OVH` @masterzen * `OVH` @masterzen
* `PACKETFRAME` @hamptonmoore
* `POWERDNS` @jpbede * `POWERDNS` @jpbede
* `SOFTLAYER`@jamielennox * `SOFTLAYER`@jamielennox
* `TRANSIP` @blackshadev * `TRANSIP` @blackshadev

View File

@ -171,6 +171,10 @@
"serverName": "$POWERDNS_SERVERNAME", "serverName": "$POWERDNS_SERVERNAME",
"domain": "$POWERDNS_DOMAIN" "domain": "$POWERDNS_DOMAIN"
}, },
"PACKETFRAME": {
"token": "$PACKETFRAME_TOKEN",
"domain": "$PACKETFRAME_DOMAIN"
},
"ROUTE53": { "ROUTE53": {
"KeyId": "$ROUTE53_KEY_ID", "KeyId": "$ROUTE53_KEY_ID",
"SecretKey": "$ROUTE53_KEY", "SecretKey": "$ROUTE53_KEY",

View File

@ -36,6 +36,7 @@ import (
_ "github.com/StackExchange/dnscontrol/v3/providers/opensrs" _ "github.com/StackExchange/dnscontrol/v3/providers/opensrs"
_ "github.com/StackExchange/dnscontrol/v3/providers/oracle" _ "github.com/StackExchange/dnscontrol/v3/providers/oracle"
_ "github.com/StackExchange/dnscontrol/v3/providers/ovh" _ "github.com/StackExchange/dnscontrol/v3/providers/ovh"
_ "github.com/StackExchange/dnscontrol/v3/providers/packetframe"
_ "github.com/StackExchange/dnscontrol/v3/providers/powerdns" _ "github.com/StackExchange/dnscontrol/v3/providers/powerdns"
_ "github.com/StackExchange/dnscontrol/v3/providers/route53" _ "github.com/StackExchange/dnscontrol/v3/providers/route53"
_ "github.com/StackExchange/dnscontrol/v3/providers/softlayer" _ "github.com/StackExchange/dnscontrol/v3/providers/softlayer"

View File

@ -0,0 +1,186 @@
package packetframe
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
const (
mediaType = "application/json"
defaultBaseURL = "https://packetframe.com/api/"
)
type zone struct {
ID string `json:"id"`
Zone string `json:"zone"`
Users []string `json:"users"`
UserEmails []string `json:"user_emails"`
}
type domainResponse struct {
Data struct {
Zones []zone `json:"zones"`
} `json:"data"`
Message string `json:"message"`
Success bool `json:"success"`
}
type deleteRequest struct {
Record string `json:"record"`
Zone string `json:"zone"`
}
type recordResponse struct {
Data struct {
Records []domainRecord `json:"records"`
} `json:"data"`
Message string `json:"message"`
Success bool `json:"success"`
}
type domainRecord struct {
ID string `json:"id"`
Type string `json:"type"`
Label string `json:"label"`
Value string `json:"value"`
TTL int `json:"ttl"`
Proxy bool `json:"proxy"`
Zone string `json:"zone"`
}
func (c *packetframeProvider) fetchDomainList() error {
c.domainIndex = map[string]zone{}
dr := &domainResponse{}
endpoint := "dns/zones"
if err := c.get(endpoint, dr); err != nil {
return fmt.Errorf("failed fetching domain list (Packetframe): %w", err)
}
for _, zone := range dr.Data.Zones {
c.domainIndex[zone.Zone] = zone
}
return nil
}
func (c *packetframeProvider) getRecords(zoneID string) ([]domainRecord, error) {
var records []domainRecord
dr := &recordResponse{}
endpoint := "dns/records/" + zoneID
if err := c.get(endpoint, dr); err != nil {
return records, fmt.Errorf("failed fetching domain list (Packetframe): %w", err)
}
records = append(records, dr.Data.Records...)
return records, nil
}
func (c *packetframeProvider) createRecord(rec *domainRecord) (*domainRecord, error) {
endpoint := "dns/records"
req, err := c.newRequest(http.MethodPost, endpoint, rec)
if err != nil {
return nil, err
}
_, err = c.client.Do(req)
if err != nil {
return nil, err
}
return rec, nil
}
func (c *packetframeProvider) modifyRecord(rec *domainRecord) error {
endpoint := "dns/records"
req, err := c.newRequest(http.MethodPut, endpoint, rec)
if err != nil {
return err
}
_, err = c.client.Do(req)
if err != nil {
return err
}
return nil
}
func (c *packetframeProvider) deleteRecord(zoneID string, recordID string) error {
endpoint := "dns/records"
req, err := c.newRequest(http.MethodDelete, endpoint, deleteRequest{Zone: zoneID, Record: recordID})
if err != nil {
return err
}
resp, err := c.client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return c.handleErrors(resp)
}
return nil
}
func (c *packetframeProvider) newRequest(method, endpoint string, body interface{}) (*http.Request, error) {
rel, err := url.Parse(endpoint)
if err != nil {
return nil, err
}
u := c.baseURL.ResolveReference(rel)
buf := new(bytes.Buffer)
if body != nil {
err = json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", mediaType)
req.Header.Add("Accept", mediaType)
req.Header.Add("Authorization", "Token "+c.token)
return req, nil
}
func (c *packetframeProvider) get(endpoint string, target interface{}) error {
req, err := c.newRequest(http.MethodGet, endpoint, nil)
if err != nil {
return err
}
resp, err := c.client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return c.handleErrors(resp)
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
return decoder.Decode(target)
}
func (c *packetframeProvider) handleErrors(resp *http.Response) error {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
dr := &domainResponse{}
json.Unmarshal(body, &dr)
return fmt.Errorf("packetframe API error: %s", dr.Message)
}

View File

@ -0,0 +1,11 @@
package packetframe
import (
"github.com/StackExchange/dnscontrol/v3/models"
)
// AuditRecords returns an error if any records are not
// supportable by this provider.
func AuditRecords(records []*models.RecordConfig) error {
return nil
}

View File

@ -0,0 +1,237 @@
package packetframe
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/StackExchange/dnscontrol/v3/pkg/diff"
"github.com/StackExchange/dnscontrol/v3/providers"
)
// packetframeProvider is the handle for this provider.
type packetframeProvider struct {
client *http.Client
baseURL *url.URL
token string
domainIndex map[string]zone
}
var defaultNameServerNames = []string{
"ns1.packetframe.com",
"ns2.packetframe.com",
}
// newPacketframe creates the provider.
func newPacketframe(m map[string]string, metadata json.RawMessage) (providers.DNSServiceProvider, error) {
if m["token"] == "" {
return nil, fmt.Errorf("missing Packetframe token")
}
baseURL, err := url.Parse(defaultBaseURL)
if err != nil {
return nil, fmt.Errorf("invalid base URL for Packetframe")
}
client := http.Client{}
api := &packetframeProvider{client: &client, baseURL: baseURL, token: m["token"]}
return api, nil
}
var features = providers.DocumentationNotes{
providers.DocDualHost: providers.Cannot(),
providers.DocOfficiallySupported: providers.Cannot(),
providers.CanUseSRV: providers.Can(),
providers.CanUsePTR: providers.Can(),
providers.CanGetZones: providers.Unimplemented(),
}
func init() {
fns := providers.DspFuncs{
Initializer: newPacketframe,
RecordAuditor: AuditRecords,
}
providers.RegisterDomainServiceProviderType("PACKETFRAME", fns, features)
}
// GetNameservers returns the nameservers for a domain.
func (api *packetframeProvider) GetNameservers(domain string) ([]*models.Nameserver, error) {
return models.ToNameservers(defaultNameServerNames)
}
// GetZoneRecords gets the records of a zone and returns them in RecordConfig format.
func (api *packetframeProvider) GetZoneRecords(domain string) (models.Records, error) {
if api.domainIndex == nil {
if err := api.fetchDomainList(); err != nil {
return nil, err
}
}
zone, ok := api.domainIndex[domain+"."]
if !ok {
return nil, fmt.Errorf("%q not a zone in Packetframe account", domain)
}
records, err := api.getRecords(zone.ID)
if err != nil {
return nil, fmt.Errorf("could not load records for domain %q", domain)
}
existingRecords := make([]*models.RecordConfig, len(records))
dc := models.DomainConfig{
Name: domain,
}
for i := range records {
existingRecords[i] = toRc(&dc, &records[i])
}
return existingRecords, nil
}
// GetDomainCorrections returns the corrections for a domain.
func (api *packetframeProvider) GetDomainCorrections(dc *models.DomainConfig) ([]*models.Correction, error) {
dc, err := dc.Copy()
if err != nil {
return nil, err
}
dc.Punycode()
if api.domainIndex == nil {
if err := api.fetchDomainList(); err != nil {
return nil, err
}
}
zone, ok := api.domainIndex[dc.Name+"."]
if !ok {
return nil, fmt.Errorf("no such zone %q in Packetframe account", dc.Name)
}
records, err := api.getRecords(zone.ID)
if err != nil {
return nil, fmt.Errorf("could not load records for domain %q", dc.Name)
}
existingRecords := make([]*models.RecordConfig, len(records))
for i := range records {
existingRecords[i] = toRc(dc, &records[i])
}
// Normalize
models.PostProcessRecords(existingRecords)
differ := diff.New(dc)
_, create, delete, modify, err := differ.IncrementalDiff(existingRecords)
if err != nil {
return nil, err
}
var corrections []*models.Correction
for _, m := range create {
req, err := toReq(zone.ID, dc, m.Desired)
if err != nil {
return nil, err
}
corr := &models.Correction{
Msg: m.String(),
F: func() error {
_, err := api.createRecord(req)
return err
},
}
corrections = append(corrections, corr)
}
for _, m := range delete {
original := m.Existing.Original.(*domainRecord)
corr := &models.Correction{
Msg: fmt.Sprintf("Deleting record %q from %q", original.ID, zone.Zone),
F: func() error {
err := api.deleteRecord(zone.ID, original.ID)
return err
},
}
corrections = append(corrections, corr)
}
for _, m := range modify {
original := m.Existing.Original.(*domainRecord)
req, _ := toReq(zone.ID, dc, m.Desired)
req.ID = original.ID
corr := &models.Correction{
Msg: fmt.Sprintf("Modifying record %q from %q", original.ID, zone.Zone),
F: func() error {
err := api.modifyRecord(req)
return err
},
}
corrections = append(corrections, corr)
}
return corrections, nil
}
func toReq(zoneID string, dc *models.DomainConfig, rc *models.RecordConfig) (*domainRecord, error) {
req := &domainRecord{
Type: rc.Type,
TTL: int(rc.TTL),
Label: rc.GetLabel(),
Zone: zoneID,
}
switch rc.Type { // #rtype_variations
case "A", "AAAA", "PTR", "TXT", "CNAME", "NS":
req.Value = rc.GetTargetField()
case "MX":
req.Value = fmt.Sprintf("%d %s", rc.MxPreference, rc.GetTargetField())
case "SRV":
req.Value = fmt.Sprintf("%d %d %d %s", rc.SrvPriority, rc.SrvWeight, rc.SrvPort, rc.GetTargetField())
default:
return nil, fmt.Errorf("packetframe.toReq rtype %q unimplemented", rc.Type)
}
return req, nil
}
func toRc(dc *models.DomainConfig, r *domainRecord) *models.RecordConfig {
rc := &models.RecordConfig{
Type: r.Type,
TTL: uint32(r.TTL),
Original: r,
}
label := strings.TrimSuffix(r.Label, dc.Name+".")
label = strings.TrimSuffix(label, ".")
if label == "" {
label = "@"
}
rc.SetLabel(label, dc.Name)
switch rtype := r.Type; rtype { // #rtype_variations
case "TXT":
rc.SetTargetTXTString(r.Value)
case "SRV":
spl := strings.Split(r.Value, " ")
prio, _ := strconv.ParseUint(spl[0], 10, 16)
weight, _ := strconv.ParseUint(spl[1], 10, 16)
port, _ := strconv.ParseUint(spl[2], 10, 16)
rc.SetTargetSRV(uint16(prio), uint16(weight), uint16(port), spl[3])
case "MX":
spl := strings.Split(r.Value, " ")
prio, _ := strconv.ParseUint(spl[0], 10, 16)
rc.SetTargetMX(uint16(prio), spl[1])
default:
rc.SetTarget(r.Value)
}
return rc
}