Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 87 additions & 11 deletions conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,71 @@ package rfc2136

import (
"fmt"
"github.com/libdns/libdns"
"github.com/miekg/dns"
"reflect"
"strings"
"time"

"github.com/libdns/libdns"
"github.com/miekg/dns"
)

func recordToRR(rec libdns.Record, zone string) (dns.RR, error) {
str := fmt.Sprintf(`%s %d IN %s %s`, rec.Name,
int(rec.TTL.Seconds()), rec.Type, rec.Value)
zp := dns.NewZoneParser(strings.NewReader(str), zone, "")
rr, _ := zp.Next()
rrType := dns.StringToType[rec.Type]
rrConstructor := dns.TypeToRR[rrType]
var rr dns.RR
if rrConstructor == nil {
rr = new(dns.RFC3597)
} else {
rr = rrConstructor()
}

// Create a zone file line representing the record. We're using reflection
// so that we can automatically support any new RR types that define these
// fields.
name := rec.Name
if name == "" {
name = "@"
}

zoneLine := fmt.Sprintf("%s %d IN %s ", name, int(rec.TTL.Seconds()), rec.Type)

priority := reflect.ValueOf(rr).Elem().FieldByName("Priority")
if !priority.IsValid() {
priority = reflect.ValueOf(rr).Elem().FieldByName("Preference")
}

if priority.IsValid() {
zoneLine += fmt.Sprintf("%d ", rec.Priority)
}

weight := reflect.ValueOf(rr).Elem().FieldByName("Weight")
if weight.IsValid() {
zoneLine += fmt.Sprintf("%d ", rec.Weight)
}

target := reflect.ValueOf(rr).Elem().FieldByName("Target")
if target.IsValid() {
zoneLine += fmt.Sprintf("%s ", rec.Target)
}

if rec.Value != "" {
zoneLine += rec.Value
}
zoneLine = strings.TrimSuffix(zoneLine, " ") + "\n"

zp := dns.NewZoneParser(strings.NewReader(zoneLine), zone, "")
rr, _ = zp.Next()
return rr, zp.Err()
}

func recordFromRR(rr dns.RR, zone string) libdns.Record {
hdr := rr.Header()

rec := libdns.Record{
Name: libdns.RelativeName(hdr.Name, zone),
TTL: time.Duration(hdr.Ttl) * time.Second,
}

// The record value is the full record string representation with the header string
// prefix stripped. Package dns represents private-use and unknown records as
// RFC3597 records. When those are formatted as string, their header prefix has a
Expand All @@ -31,10 +79,38 @@ func recordFromRR(rr dns.RR, zone string) libdns.Record {
typ = fmt.Sprintf("TYPE%d", hdr.Rrtype)
hdrStr = fmt.Sprintf("%s\t%d\tCLASS%d\t%s\t", name, hdr.Ttl, hdr.Class, typ)
}
return libdns.Record{
Type: typ,
Name: libdns.RelativeName(hdr.Name, zone),
TTL: time.Duration(hdr.Ttl) * time.Second,
Value: strings.TrimPrefix(rr.String(), hdrStr),

rec.Type = typ

// Parse priority, weight, and target from the record value. We're using
// reflection so that we can automatically support any new RR types that
// define these fields.
priority := reflect.ValueOf(rr).Elem().FieldByName("Priority")
if !priority.IsValid() {
priority = reflect.ValueOf(rr).Elem().FieldByName("Preference")
}

if priority.IsValid() {
priority := priority.Uint()
rec.Priority = uint(priority)
hdrStr += fmt.Sprintf("%d ", priority)
}

weight := reflect.ValueOf(rr).Elem().FieldByName("Weight")
if weight.IsValid() {
weight := weight.Uint()
rec.Weight = uint(weight)
hdrStr += fmt.Sprintf("%d ", weight)
}

target := reflect.ValueOf(rr).Elem().FieldByName("Target")
if target.IsValid() && typ != "SRV" {
rec.Target = target.String()
hdrStr += fmt.Sprintf("%s ", rec.Target)
}

// Get the value from the record string representation.
rec.Value = strings.TrimPrefix(rr.String(), hdrStr)

return rec
}
98 changes: 96 additions & 2 deletions conversion_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package rfc2136

import (
"github.com/libdns/libdns"
"github.com/miekg/dns"
"encoding/base64"
"fmt"
"net"
"testing"
"time"

"github.com/libdns/libdns"
"github.com/miekg/dns"
)

const zone = "example.com."

var echString = "AEj+DQBEAQAgACAdd+scUi0IYFsXnUIU7ko2Nd9+F8M26pAGZVpz/KrWPgAEAAEAAWQVZWNoLXNpdGVzLmV4YW1wbGUubmV0AAA="
var echBytes, _ = base64.StdEncoding.DecodeString(echString)

var testCases = map[dns.RR]libdns.Record{
&dns.TXT{
Hdr: dns.RR_Header{
Expand All @@ -25,6 +31,7 @@ var testCases = map[dns.RR]libdns.Record{
Value: "\"hello world\"",
TTL: 220 * time.Second,
},

&dns.TXT{
Hdr: dns.RR_Header{
Name: "txt.example.com.",
Expand Down Expand Up @@ -84,6 +91,93 @@ var testCases = map[dns.RR]libdns.Record{
Value: `\# 5 0d10480001`,
TTL: 150 * time.Second,
},

&dns.HTTPS{
SVCB: dns.SVCB{
Hdr: dns.RR_Header{
Name: "https.example.com.",
Rrtype: dns.TypeHTTPS,
Class: dns.ClassINET,
Ttl: 150,
},
Priority: 2,
Target: "target.example.com.",
Value: []dns.SVCBKeyValue{
&dns.SVCBAlpn{
Alpn: []string{"h3", "h2"},
},
&dns.SVCBIPv4Hint{
Hint: []net.IP{net.ParseIP("127.0.0.1")},
},
&dns.SVCBIPv6Hint{
Hint: []net.IP{net.ParseIP("::1")},
},
&dns.SVCBECHConfig{
ECH: echBytes,
},
},
},
}: {
Type: "HTTPS",
Name: "https",
TTL: 150 * time.Second,
Priority: 2,
Target: "target.example.com.",
Value: fmt.Sprintf(`alpn="h3,h2" ipv4hint="127.0.0.1" ipv6hint="::1" ech="%s"`, echString),
},

&dns.MX{
Hdr: dns.RR_Header{
Name: "mx.example.com.",
Rrtype: dns.TypeMX,
Class: dns.ClassINET,
Ttl: 150,
},
Preference: 10,
Mx: "mail.example.com.",
}: {
Type: "MX",
Name: "mx",
Value: "mail.example.com.",
TTL: 150 * time.Second,
Priority: 10,
},

&dns.SRV{
Hdr: dns.RR_Header{
Name: "srv.example.com.",
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
Ttl: 150,
},
Priority: 10,
Weight: 20,
Port: 443,
Target: "service.example.com.",
}: {
Type: "SRV",
Name: "srv",
Value: "443 service.example.com.",
TTL: 150 * time.Second,
Priority: 10,
Weight: 20,
},

// Bare name
&dns.A{
Hdr: dns.RR_Header{
Name: "example.com.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 300,
},
A: net.ParseIP("127.0.0.1"),
}: {
Type: "A",
Name: "",
Value: "127.0.0.1",
TTL: 300 * time.Second,
},
}

func TestRecordFromRR(t *testing.T) {
Expand Down
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ module github.com/libdns/rfc2136
go 1.18

require (
github.com/libdns/libdns v0.2.1
github.com/miekg/dns v1.1.49
github.com/libdns/libdns v0.2.3
github.com/miekg/dns v1.1.63
)

require (
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/tools v0.24.0 // indirect
)
51 changes: 14 additions & 37 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,37 +1,14 @@
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8=
github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8=
github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
33 changes: 29 additions & 4 deletions provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package rfc2136
import (
"context"
"fmt"
"time"

"github.com/libdns/libdns"
"github.com/miekg/dns"
"time"
)

type Provider struct {
Expand Down Expand Up @@ -66,11 +67,35 @@ func (p *Provider) GetRecords(ctx context.Context, zone string) ([]libdns.Record
return records, nil
}

func (p *Provider) AppendRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
return p.SetRecords(ctx, zone, records)
func (p *Provider) SetRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
zone = dns.Fqdn(zone)

msg := dns.Msg{}
msg.SetUpdate(zone)

rrs := make([]dns.RR, 0, len(records))
for _, rec := range records {
rr, err := recordToRR(rec, zone)
if err != nil {
return nil, fmt.Errorf("invalid record %s: %w", rec.Name, err)
}
rrs = append(rrs, rr)
}

msg.RemoveRRset(rrs)
msg.Insert(rrs)

p.setTsig(&msg)

_, _, err := p.client().ExchangeContext(ctx, &msg, p.Server)
if err != nil {
return nil, err
}

return records, nil
}

func (p *Provider) SetRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
func (p *Provider) AppendRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
zone = dns.Fqdn(zone)

msg := dns.Msg{}
Expand Down