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
121 changes: 104 additions & 17 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (c *client) updateRRs(ctx context.Context, zoneID string, recs []zones.Reso
return nil
}

func mergeRRecs(fullZone *zones.Zone, records []libdns.Record) ([]zones.ResourceRecordSet, error) {
func mergeRRecs(fullZone *zones.Zone, records []libdns.RR) ([]zones.ResourceRecordSet, error) {
// pdns doesn't really have an append functionality, so we have to fake it by
// fetching existing rrsets for the zone and see if any already exist. If so,
// merge those with the existing data. Otherwise just add the record.
Expand All @@ -72,11 +72,11 @@ func mergeRRecs(fullZone *zones.Zone, records []libdns.Record) ([]zones.Resource
}
// now for our additions
for _, rec := range recs {
if !dupes[rec.Value] {
if !dupes[rec.Data] {
rr.Records = append(rr.Records, zones.Record{
Content: rec.Value,
Content: rec.Data,
})
dupes[rec.Value] = true
dupes[rec.Data] = true
}
}
rrsets = append(rrsets, rr)
Expand All @@ -89,7 +89,7 @@ func mergeRRecs(fullZone *zones.Zone, records []libdns.Record) ([]zones.Resource
}

// generate RessourceRecordSets that will delete records from zone
func cullRRecs(fullZone *zones.Zone, records []libdns.Record) []zones.ResourceRecordSet {
func cullRRecs(fullZone *zones.Zone, records []libdns.RR) []zones.ResourceRecordSet {
inHash := makeLDRecHash(records)
var rRSets []zones.ResourceRecordSet
for _, t := range fullZone.ResourceRecordSets {
Expand All @@ -109,24 +109,23 @@ func cullRRecs(fullZone *zones.Zone, records []libdns.Record) []zones.ResourceRe
}

// remove culls from rRSet record values
func removeRecords(rRSet zones.ResourceRecordSet, culls []libdns.Record) zones.ResourceRecordSet {
func removeRecords(rRSet zones.ResourceRecordSet, culls []libdns.RR) zones.ResourceRecordSet {
deleteItem := func(item string) []zones.Record {
recs := rRSet.Records
for i := len(recs) - 1; i >= 0; i-- {
if recs[i].Content == item {
copy(recs[i:], recs[:i+1])
recs = recs[:len(recs)-1]
recs = append(recs[:i], recs[i+1:]...)
}
}
return recs
}
for _, c := range culls {
rRSet.Records = deleteItem(c.Value)
rRSet.Records = deleteItem(c.Data)
}
return rRSet
}

func convertLDHash(inHash map[string][]libdns.Record) []zones.ResourceRecordSet {
func convertLDHash(inHash map[string][]libdns.RR) []zones.ResourceRecordSet {
var rrsets []zones.ResourceRecordSet
for _, recs := range inHash {
if len(recs) == 0 {
Expand All @@ -141,7 +140,7 @@ func convertLDHash(inHash map[string][]libdns.Record) []zones.ResourceRecordSet
}
for _, rec := range recs {
rr.Records = append(rr.Records, zones.Record{
Content: rec.Value,
Content: rec.Data,
})
}
rrsets = append(rrsets, rr)
Expand All @@ -153,9 +152,9 @@ func key(Name, Type string) string {
return Name + ":" + Type
}

func makeLDRecHash(records []libdns.Record) map[string][]libdns.Record {
func makeLDRecHash(records []libdns.RR) map[string][]libdns.RR {
// Keep track of records grouped by name + type
inHash := make(map[string][]libdns.Record)
inHash := make(map[string][]libdns.RR)

for _, r := range records {
k := key(r.Name, r.Type)
Expand Down Expand Up @@ -197,18 +196,106 @@ func (c *client) zoneID(ctx context.Context, zoneName string) (string, error) {
return shortZone.ID, nil
}

func convertNamesToAbsolute(zone string, records []libdns.Record) []libdns.Record {
out := make([]libdns.Record, len(records))
copy(out, records)
func convertNamesToAbsolute(zone string, records []libdns.Record) []libdns.RR {
out := make([]libdns.RR, len(records))
for i, r := range records {
svcb, ok := r.(libdns.ServiceBinding)
if ok {
out[i] = svcbToRr(svcb)
} else {
out[i] = r.RR()
}
}
for i := range out {
name := libdns.AbsoluteName(out[i].Name, zone)
if !strings.HasSuffix(name, ".") {
name = name + "."
}
out[i].Name = name
if out[i].Type == "TXT" {
out[i].Value = txtsanitize.TXTSanitize(out[i].Value)
out[i].Data = txtsanitize.TXTSanitize(out[i].Data)
}
}
return out
}

// This function is taken from libdns itself.
func svcbToRr(s libdns.ServiceBinding) libdns.RR {
var name string
var recType string
if s.Scheme == "https" || s.Scheme == "http" || s.Scheme == "wss" || s.Scheme == "ws" {
recType = "HTTPS"
name = s.Name
if s.URLSchemePort == 443 || s.URLSchemePort == 80 {
// Ok, we'll correct your mistake for you.
s.URLSchemePort = 0
}
} else {
recType = "SVCB"
name = fmt.Sprintf("_%s.%s", s.Scheme, s.Name)
}

if s.URLSchemePort != 0 {
name = fmt.Sprintf("_%d.%s", s.URLSchemePort, name)
}

var params string
if s.Priority == 0 && len(s.Params) != 0 {
// The SvcParams should be empty in AliasMode, so we'll fix that for
// you.
params = ""
} else {
params = paramsToString(s.Params)
}

return libdns.RR{
Name: name,
TTL: s.TTL,
Type: recType,
Data: fmt.Sprintf("%d %s %s", s.Priority, s.Target, params),
}
}

// This function is taken from libdns itself and modified to quote ECH params.
func paramsToString(params libdns.SvcParams) string {
var sb strings.Builder
for key, vals := range params {
if sb.Len() > 0 {
sb.WriteRune(' ')
}
sb.WriteString(key)
var hasVal, needsQuotes bool
if key == "ech" {
needsQuotes = true
}
for _, val := range vals {
if len(val) > 0 {
hasVal = true
}
if strings.ContainsAny(val, `" `) {
needsQuotes = true
}
if hasVal && needsQuotes {
break
}
}
if hasVal {
sb.WriteRune('=')
}
if needsQuotes {
sb.WriteRune('"')
}
for i, val := range vals {
if i > 0 {
sb.WriteRune(',')
}
val = strings.ReplaceAll(val, `"`, `\"`)
val = strings.ReplaceAll(val, `,`, `\,`)
sb.WriteString(val)
}
if needsQuotes {
sb.WriteRune('"')
}
}
return sb.String()
}
79 changes: 35 additions & 44 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package powerdns
import (
"context"
"fmt"
"net/netip"
"os"
"os/exec"
"reflect"
Expand Down Expand Up @@ -135,10 +136,9 @@ func TestPDNSClient(t *testing.T) {
zone: "example.org.",
Type: "A",
records: []libdns.Record{
{
Name: "2",
Type: "A",
Value: "127.0.0.7",
libdns.Address{
Name: "2",
IP: netip.MustParseAddr("127.0.0.7"),
},
},
want: []string{"1:127.0.0.1", "1:127.0.0.2", "1:127.0.0.3",
Expand All @@ -150,10 +150,9 @@ func TestPDNSClient(t *testing.T) {
zone: "example.org.",
Type: "TXT",
records: []libdns.Record{
{
Name: "1",
Type: "TXT",
Value: "\"This is also some text\"",
libdns.TXT{
Name: "1",
Text: "\"This is also some text\"",
},
},
want: []string{
Expand All @@ -167,10 +166,9 @@ func TestPDNSClient(t *testing.T) {
zone: "example.org.",
Type: "TXT",
records: []libdns.Record{
{
Name: "1",
Type: "TXT",
Value: "This is some weird text that isn't quoted",
libdns.TXT{
Name: "1",
Text: "This is some weird text that isn't quoted",
},
},
want: []string{
Expand All @@ -185,10 +183,9 @@ func TestPDNSClient(t *testing.T) {
zone: "example.org.",
Type: "TXT",
records: []libdns.Record{
{
Name: "1",
Type: "TXT",
Value: `This is some weird text that "has embedded quoting"`,
libdns.TXT{
Name: "1",
Text: `This is some weird text that "has embedded quoting"`,
},
},
want: []string{`1:"This is text"`, `1:"This is also some text"`,
Expand All @@ -201,10 +198,9 @@ func TestPDNSClient(t *testing.T) {
zone: "example.org.",
Type: "TXT",
records: []libdns.Record{
{
Name: "1",
Type: "TXT",
Value: `ç is equal to \195\167`,
libdns.TXT{
Name: "1",
Text: `ç is equal to \195\167`,
},
},
want: []string{`1:"This is text"`, `1:"This is also some text"`,
Expand All @@ -219,33 +215,30 @@ func TestPDNSClient(t *testing.T) {
zone: "example.org.",
Type: "A",
records: []libdns.Record{
{
Name: "2",
Type: "A",
Value: "127.0.0.7",
libdns.Address{
Name: "2",
IP: netip.MustParseAddr("127.0.0.5"),
},
},
want: []string{"1:127.0.0.1", "1:127.0.0.2", "1:127.0.0.3", "2:127.0.0.4", "2:127.0.0.5", "2:127.0.0.6"},
want: []string{"1:127.0.0.1", "1:127.0.0.2", "1:127.0.0.3", "2:127.0.0.4", "2:127.0.0.6", "2:127.0.0.7"},
},
{
name: "Test Append and Add Zone",
operation: "append",
zone: "example.org.",
Type: "A",
records: []libdns.Record{
{
Name: "2",
Type: "A",
Value: "127.0.0.8",
libdns.Address{
Name: "2",
IP: netip.MustParseAddr("127.0.0.8"),
},
{
Name: "3",
Type: "A",
Value: "127.0.0.9",
libdns.Address{
Name: "3",
IP: netip.MustParseAddr("127.0.0.9"),
},
},
want: []string{"1:127.0.0.1", "1:127.0.0.2", "1:127.0.0.3",
"2:127.0.0.4", "2:127.0.0.5", "2:127.0.0.6", "2:127.0.0.8",
"2:127.0.0.4", "2:127.0.0.6", "2:127.0.0.7", "2:127.0.0.8",
"3:127.0.0.9"},
},
{
Expand All @@ -254,15 +247,13 @@ func TestPDNSClient(t *testing.T) {
zone: "example.org.",
Type: "A",
records: []libdns.Record{
{
Name: "2",
Type: "A",
Value: "127.0.0.1",
libdns.Address{
Name: "2",
IP: netip.MustParseAddr("127.0.0.1"),
},
{
Name: "1",
Type: "A",
Value: "127.0.0.1",
libdns.Address{
Name: "1",
IP: netip.MustParseAddr("127.0.0.1"),
},
},
want: []string{"1:127.0.0.1", "2:127.0.0.1", "3:127.0.0.9"},
Expand Down Expand Up @@ -294,10 +285,10 @@ func TestPDNSClient(t *testing.T) {
}
var have []string
for _, rr := range recs {
if rr.Type != table.Type {
if rr.RR().Type != table.Type {
continue
}
have = append(have, fmt.Sprintf("%s:%s", rr.Name, rr.Value))
have = append(have, fmt.Sprintf("%s:%s", rr.RR().Name, rr.RR().Data))
}

sort.Strings(have)
Expand Down
23 changes: 2 additions & 21 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
version: "3"
services:
mysql:
image: mariadb:10.6
environment:
MYSQL_ROOT_PASSWORD: immelting
MYSQL_USER: powerdns
MYSQL_PASSWORD: secret
MYSQL_DATABASE: powerdns
volumes:
- database:/var/lib/mysql
networks:
- backend
powerdns:
image: psitrax/powerdns
environment:
MYSQL_HOST: mysql
MYSQL_USER: powerdns
MYSQL_PASS: secret
MYSQL_DB: powerdns
image: powerdns/pdns-auth-49
networks:
- backend
volumes:
- ./.docker/pdns:/etc/pdns/conf.d
- ./.docker/pdns:/etc/powerdns/pdns.d
ports:
- 8081:8081
volumes:
database: {}
networks:
backend: {}
Loading