2022-06-27 15:48:34 -04:00
|
|
|
package tsig
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/coredns/caddy"
|
|
|
|
|
"github.com/coredns/coredns/plugin/test"
|
|
|
|
|
|
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestParse(t *testing.T) {
|
|
|
|
|
secrets := map[string]string{
|
|
|
|
|
"name.key.": "test-key",
|
|
|
|
|
"name2.key.": "test-key-2",
|
|
|
|
|
}
|
2026-03-31 00:39:09 +03:00
|
|
|
var secretConfig strings.Builder
|
2022-06-27 15:48:34 -04:00
|
|
|
for k, s := range secrets {
|
2026-03-31 00:39:09 +03:00
|
|
|
fmt.Fprintf(&secretConfig, "secret %s %s\n", k, s)
|
2022-06-27 15:48:34 -04:00
|
|
|
}
|
|
|
|
|
secretsFile, cleanup, err := test.TempFile(".", `key "name.key." {
|
|
|
|
|
secret "test-key";
|
|
|
|
|
};
|
|
|
|
|
key "name2.key." {
|
|
|
|
|
secret "test-key2";
|
|
|
|
|
};`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("failed to create temp file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer cleanup()
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
2026-03-27 20:05:49 +01:00
|
|
|
input string
|
|
|
|
|
shouldErr bool
|
|
|
|
|
expectedZones []string
|
|
|
|
|
expectedQTypes qTypes
|
|
|
|
|
expectedOpCodes opCodes
|
|
|
|
|
expectedSecrets map[string]string
|
|
|
|
|
expectedAllTypes bool
|
|
|
|
|
expectedAllOpcodes bool
|
2022-06-27 15:48:34 -04:00
|
|
|
}{
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + "}",
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: defaultQTypes,
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedOpCodes: defaultOpCodes,
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
input: "tsig {\n secrets " + secretsFile + "\n}",
|
|
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: defaultQTypes,
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedOpCodes: defaultOpCodes,
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig example.com {\n " + secretConfig.String() + "}",
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedZones: []string{"example.com."},
|
|
|
|
|
expectedQTypes: defaultQTypes,
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedOpCodes: defaultOpCodes,
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + " require all \n}",
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: qTypes{},
|
|
|
|
|
expectedOpCodes: defaultOpCodes,
|
|
|
|
|
expectedAllTypes: true,
|
|
|
|
|
expectedSecrets: secrets,
|
2022-06-27 15:48:34 -04:00
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + " require none \n}",
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: qTypes{},
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedOpCodes: defaultOpCodes,
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + " \n require A AAAA \n}",
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: qTypes{dns.TypeA: {}, dns.TypeAAAA: {}},
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedOpCodes: defaultOpCodes,
|
|
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + " \n require_opcode UPDATE NOTIFY \n}",
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: defaultQTypes,
|
|
|
|
|
expectedOpCodes: opCodes{dns.OpcodeUpdate: {}, dns.OpcodeNotify: {}},
|
|
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + " \n require_opcode all \n}",
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: defaultQTypes,
|
|
|
|
|
expectedOpCodes: opCodes{},
|
|
|
|
|
expectedAllOpcodes: true,
|
|
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + " \n require_opcode none \n}",
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: defaultQTypes,
|
|
|
|
|
expectedOpCodes: opCodes{},
|
|
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-03-31 00:39:09 +03:00
|
|
|
input: "tsig {\n " + secretConfig.String() + " \n require AXFR \n require_opcode UPDATE \n}",
|
2026-03-27 20:05:49 +01:00
|
|
|
expectedZones: []string{"."},
|
|
|
|
|
expectedQTypes: qTypes{dns.TypeAXFR: {}},
|
|
|
|
|
expectedOpCodes: opCodes{dns.OpcodeUpdate: {}},
|
2022-06-27 15:48:34 -04:00
|
|
|
expectedSecrets: secrets,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
input: "tsig {\n blah \n}",
|
|
|
|
|
shouldErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
input: "tsig {\n secret name. too many parameters \n}",
|
|
|
|
|
shouldErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
input: "tsig {\n require \n}",
|
|
|
|
|
shouldErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
input: "tsig {\n require invalid-qtype \n}",
|
|
|
|
|
shouldErr: true,
|
|
|
|
|
},
|
2026-03-27 20:05:49 +01:00
|
|
|
{
|
|
|
|
|
input: "tsig {\n require_opcode \n}",
|
|
|
|
|
shouldErr: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
input: "tsig {\n require_opcode INVALID \n}",
|
|
|
|
|
shouldErr: true,
|
|
|
|
|
},
|
2022-06-27 15:48:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
serverBlockKeys := []string{"."}
|
|
|
|
|
for i, test := range tests {
|
|
|
|
|
c := caddy.NewTestController("dns", test.input)
|
|
|
|
|
c.ServerBlockKeys = serverBlockKeys
|
|
|
|
|
ts, err := parse(c)
|
|
|
|
|
|
|
|
|
|
if err == nil && test.shouldErr {
|
|
|
|
|
t.Fatalf("Test %d expected errors, but got no error.", i)
|
|
|
|
|
} else if err != nil && !test.shouldErr {
|
|
|
|
|
t.Fatalf("Test %d expected no errors, but got '%v'", i, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if test.shouldErr {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(test.expectedZones) != len(ts.Zones) {
|
|
|
|
|
t.Fatalf("Test %d expected zones '%v', but got '%v'.", i, test.expectedZones, ts.Zones)
|
|
|
|
|
}
|
|
|
|
|
for j := range test.expectedZones {
|
|
|
|
|
if test.expectedZones[j] != ts.Zones[j] {
|
|
|
|
|
t.Errorf("Test %d expected zones '%v', but got '%v'.", i, test.expectedZones, ts.Zones)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 20:05:49 +01:00
|
|
|
if test.expectedAllTypes != ts.allTypes {
|
|
|
|
|
t.Errorf("Test %d expected require all types to be '%v', but got '%v'.", i, test.expectedAllTypes, ts.allTypes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if test.expectedAllOpcodes != ts.allOpcodes {
|
|
|
|
|
t.Errorf("Test %d expected require all opcodes to be '%v', but got '%v'.", i, test.expectedAllOpcodes, ts.allOpcodes)
|
2022-06-27 15:48:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(test.expectedQTypes) != len(ts.types) {
|
|
|
|
|
t.Fatalf("Test %d expected required types '%v', but got '%v'.", i, test.expectedQTypes, ts.types)
|
|
|
|
|
}
|
|
|
|
|
for qt := range test.expectedQTypes {
|
|
|
|
|
if _, ok := ts.types[qt]; !ok {
|
|
|
|
|
t.Errorf("Test %d required types '%v', but got '%v'.", i, test.expectedQTypes, ts.types)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 20:05:49 +01:00
|
|
|
if len(test.expectedOpCodes) != len(ts.opcodes) {
|
|
|
|
|
t.Fatalf("Test %d expected required opcodes '%v', but got '%v'.", i, test.expectedOpCodes, ts.opcodes)
|
|
|
|
|
}
|
|
|
|
|
for op := range test.expectedOpCodes {
|
|
|
|
|
if _, ok := ts.opcodes[op]; !ok {
|
|
|
|
|
t.Errorf("Test %d required opcodes '%v', but got '%v'.", i, test.expectedOpCodes, ts.opcodes)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-27 15:48:34 -04:00
|
|
|
if len(test.expectedSecrets) != len(ts.secrets) {
|
|
|
|
|
t.Fatalf("Test %d expected secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets)
|
|
|
|
|
}
|
|
|
|
|
for qt := range test.expectedSecrets {
|
|
|
|
|
secret, ok := ts.secrets[qt]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("Test %d required secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if secret != ts.secrets[qt] {
|
|
|
|
|
t.Errorf("Test %d required secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseKeyFile(t *testing.T) {
|
|
|
|
|
var reader = strings.NewReader(`key "foo" {
|
|
|
|
|
algorithm hmac-sha256;
|
|
|
|
|
secret "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ=";
|
|
|
|
|
};
|
|
|
|
|
key "bar" {
|
|
|
|
|
algorithm hmac-sha256;
|
|
|
|
|
secret "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus=";
|
|
|
|
|
};
|
|
|
|
|
key "baz" {
|
|
|
|
|
secret "BycDPXSx/5YCD44Q4g5Nd2QNxNRDKwWTXddrU/zpIQM=";
|
|
|
|
|
};`)
|
|
|
|
|
|
|
|
|
|
secrets, err := parseKeyFile(reader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("Unexpected error: %q", err)
|
|
|
|
|
}
|
|
|
|
|
expectedSecrets := map[string]string{
|
|
|
|
|
"foo.": "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ=",
|
|
|
|
|
"bar.": "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus=",
|
|
|
|
|
"baz.": "BycDPXSx/5YCD44Q4g5Nd2QNxNRDKwWTXddrU/zpIQM=",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(secrets) != len(expectedSecrets) {
|
|
|
|
|
t.Fatalf("result has %d keys. expected %d", len(secrets), len(expectedSecrets))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for k, sec := range secrets {
|
|
|
|
|
expectedSec, ok := expectedSecrets[k]
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Errorf("unexpected key in result. %q", k)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if sec != expectedSec {
|
|
|
|
|
t.Errorf("incorrect secret in result for key %q. expected %q got %q ", k, expectedSec, sec)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestParseKeyFileErrors(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
in string
|
|
|
|
|
err string
|
|
|
|
|
}{
|
|
|
|
|
{in: `key {`, err: "expected key name \"key {\""},
|
|
|
|
|
{in: `foo "key" {`, err: "unexpected token \"foo\""},
|
|
|
|
|
{
|
|
|
|
|
in: `key "foo" {
|
|
|
|
|
secret "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ=";
|
|
|
|
|
};
|
|
|
|
|
key "foo" {
|
|
|
|
|
secret "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus=";
|
|
|
|
|
}; `,
|
|
|
|
|
err: "key \"foo.\" redefined",
|
|
|
|
|
},
|
|
|
|
|
{in: `key "foo" {
|
|
|
|
|
schmalgorithm hmac-sha256;`,
|
|
|
|
|
err: "unexpected token \"schmalgorithm\"",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `key "foo" {
|
|
|
|
|
schmecret "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ=";`,
|
|
|
|
|
err: "unexpected token \"schmecret\"",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `key "foo" {
|
|
|
|
|
secret`,
|
|
|
|
|
err: "expected secret key \"\\tsecret\"",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `key "foo" {
|
|
|
|
|
secret ;`,
|
|
|
|
|
err: "expected secret key \"\\tsecret ;\"",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
in: `key "foo" {
|
|
|
|
|
};`,
|
|
|
|
|
err: "expected secret for key \"foo.\"",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for i, testcase := range tests {
|
|
|
|
|
_, err := parseKeyFile(strings.NewReader(testcase.in))
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Errorf("Test %d: expected error, got no error", i)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if err.Error() != testcase.err {
|
|
|
|
|
t.Errorf("Test %d: Expected error: %q, got %q", i, testcase.err, err.Error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|