mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-12-09 13:46:07 +08:00
# Issue Fixes https://github.com/StackExchange/dnscontrol/issues/3842 CC @das7pad # Resolution Convert domain.Name to IDN earlier in the pipeline. Hack the --domains processing to convert everything to IDN. * Domain names are now stored 3 ways: The original input from dnsconfig.js, canonical IDN format (`xn--...`), and Unicode format. All are downcased. Providers that haven't been updated will receive the IDN format instead of the original input format. This might break some providers but only for users with unicode in their D("domain.tld"). PLEASE TEST YOUR PROVIDER. * BIND filename formatting options have been added to access the new formats. # Breaking changes * BIND zonefiles may change. The default used the name input in the D() statement. It now defaults to the IDN name + "!tag" if there is a tag. * Providers that are not IDN-aware may break (hopefully only if they weren't processing IDN already) --------- Co-authored-by: Jakob Ackermann <das7pad@outlook.com>
173 lines
5.1 KiB
Go
173 lines
5.1 KiB
Go
package js
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"unicode"
|
|
|
|
"github.com/StackExchange/dnscontrol/v4/models"
|
|
"github.com/StackExchange/dnscontrol/v4/pkg/normalize"
|
|
"github.com/StackExchange/dnscontrol/v4/pkg/prettyzone"
|
|
"github.com/StackExchange/dnscontrol/v4/providers"
|
|
_ "github.com/StackExchange/dnscontrol/v4/providers/_all"
|
|
testifyrequire "github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
testDir = "pkg/js/parse_tests"
|
|
)
|
|
|
|
func init() {
|
|
// go up a directory so we helpers.js is in a consistent place.
|
|
if err := os.Chdir("../.."); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func TestParsedFiles(t *testing.T) {
|
|
files, err := os.ReadDir(testDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, f := range files {
|
|
name := f.Name()
|
|
|
|
// run all js files that start with a number. Skip others.
|
|
if filepath.Ext(name) != ".js" || !unicode.IsNumber(rune(name[0])) {
|
|
continue
|
|
}
|
|
t.Run(name, func(t *testing.T) {
|
|
var err error
|
|
|
|
// Compile the .js file:
|
|
conf, err := ExecuteJavaScript(string(filepath.Join(testDir, name)), true, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
errs := normalize.ValidateAndNormalizeConfig(conf)
|
|
if len(errs) != 0 {
|
|
t.Fatal(errs[0])
|
|
}
|
|
|
|
for _, dc := range conf.Domains {
|
|
// fmt.Printf("DEBUG: PrettySort: domain=%q #rec=%d\n", dc.Name, len(dc.Records))
|
|
// fmt.Printf("DEBUG: records = %d %v\n", len(dc.Records), dc.Records)
|
|
ps := prettyzone.PrettySort(dc.Records, dc.Name, 0, nil)
|
|
dc.Records = ps.Records
|
|
if len(dc.Records) == 0 {
|
|
dc.Records = models.Records{}
|
|
}
|
|
}
|
|
|
|
// Initialize any DNS providers mentioned.
|
|
for _, dProv := range conf.DNSProviders {
|
|
pcfg := map[string]string{}
|
|
|
|
if dProv.Type == "-" {
|
|
// Pretend any "look up provider type in creds.json" results
|
|
// in a provider type that actually exists.
|
|
dProv.Type = "CLOUDFLAREAPI"
|
|
}
|
|
|
|
// Fake out any provider's validation tests.
|
|
switch dProv.Type {
|
|
case "CLOUDFLAREAPI":
|
|
pcfg["apitoken"] = "fake"
|
|
default:
|
|
}
|
|
_, err := providers.CreateDNSProvider(dProv.Type, pcfg, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Test the JS compiled as expected (compare to the .json file)
|
|
actualJSON, err := json.MarshalIndent(conf, "", " ")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testName := name[:len(name)-3]
|
|
expectedFile := filepath.Join(testDir, testName+".json")
|
|
expectedJSON, err := os.ReadFile(expectedFile)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
es := string(expectedJSON)
|
|
as := string(actualJSON)
|
|
_, _ = es, as
|
|
// Leave behind the actual result:
|
|
if err := os.WriteFile(expectedFile+".ACTUAL", []byte(as), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testifyrequire.JSONEqf(t, es, as, "EXPECTING %q = \n```\n%s\n```", expectedFile, as)
|
|
|
|
// For each domain, if there is a zone file, test against it:
|
|
|
|
var dCount int
|
|
for _, dc := range conf.Domains {
|
|
var zoneFile string
|
|
if dc.Tag != "" {
|
|
zoneFile = filepath.Join(testDir, testName, dc.GetUniqueName()+".zone")
|
|
} else {
|
|
zoneFile = filepath.Join(testDir, testName, dc.Name+".zone")
|
|
}
|
|
// fmt.Printf("DEBUG: zonefile = %q\n", zoneFile)
|
|
expectedZone, err := os.ReadFile(zoneFile)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
dCount++
|
|
|
|
// Generate the zonefile
|
|
var buf bytes.Buffer
|
|
err = prettyzone.WriteZoneFileRC(&buf, dc.Records, dc.Name, 300, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
actualZone := buf.String()
|
|
|
|
es := string(expectedZone)
|
|
as := actualZone
|
|
if es != as {
|
|
// On failure, leave behind the .ACTUAL file.
|
|
if err := os.WriteFile(zoneFile+".ACTUAL", []byte(actualZone), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
testifyrequire.Equal(t, es, as, "EXPECTING %q =\n```\n%s```", zoneFile, as)
|
|
}
|
|
if dCount > 0 && (len(conf.Domains) != dCount) {
|
|
t.Fatal(fmt.Errorf("only %d of %d domains in %q have zonefiles", dCount, len(conf.Domains), name))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestErrors(t *testing.T) {
|
|
tests := []struct{ desc, text string }{
|
|
{"old dsp style", `D("foo.com","reg","dsp")`},
|
|
{"MX no priority", `D("foo.com","reg",MX("@","test."))`},
|
|
{"MX reversed", `D("foo.com","reg",MX("@","test.", 5))`},
|
|
{"CF_REDIRECT With comma", `D("foo.com","reg",CF_REDIRECT("foo.com,","baaa"))`},
|
|
{"CF_TEMP_REDIRECT With comma", `D("foo.com","reg",CF_TEMP_REDIRECT("foo.com","baa,a"))`},
|
|
{"CF_WORKER_ROUTE With comma", `D("foo.com","reg",CF_WORKER_ROUTE("foo.com","baa,a"))`},
|
|
{"ADGUARDHOME_A_PASSTHROUGH With non-empty value", `D("foo.com","reg",ADGUARDHOME_A_PASSTHROUGH("foo","baaa"))`},
|
|
{"ADGUARDHOME_AAAA_PASSTHROUGH With non-empty value", `D("foo.com","reg",ADGUARDHOME_AAAA_PASSTHROUGH("foo,","baaa"))`},
|
|
{"Bad cidr", `D(reverse("foo.com"), "reg")`},
|
|
{"Dup domains", `D("example.org", "reg"); D("example.org", "reg")`},
|
|
{"Bad NAMESERVER", `D("example.com","reg", NAMESERVER("@","ns1.foo.com."))`},
|
|
{"Bad Hash function", `D(HASH("123", "abc"),"reg")`},
|
|
}
|
|
for _, tst := range tests {
|
|
t.Run(tst.desc, func(t *testing.T) {
|
|
if _, err := ExecuteJavaScript(tst.text, true, nil); err == nil {
|
|
t.Fatal("Expected error but found none")
|
|
}
|
|
})
|
|
}
|
|
}
|