mirror of
https://github.com/coredns/coredns.git
synced 2026-04-05 11:45:33 -04:00
plugin/tsig: add require_opcode directive for opcode-based TSIG (#7828)
Extend the tsig plugin to require TSIG signatures based on DNS opcodes,
similar to the existing qtype-based requirement.
The new require_opcode directive accepts opcode names (QUERY, IQUERY,
STATUS, NOTIFY, UPDATE) or the special values "all" and "none".
This is useful for requiring TSIG on dynamic update (UPDATE) or zone
transfer notification (NOTIFY) requests while allowing unsigned queries.
Example:
```
tsig {
secret key. NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk=
require_opcode UPDATE NOTIFY
}
```
Signed-off-by: Seena Fallah <seenafallah@gmail.com>
This commit is contained in:
@@ -19,6 +19,7 @@ tsig [ZONE...] {
|
|||||||
secret NAME KEY
|
secret NAME KEY
|
||||||
secrets FILE
|
secrets FILE
|
||||||
require [QTYPE...]
|
require [QTYPE...]
|
||||||
|
require_opcode [OPCODE...]
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
@@ -37,9 +38,14 @@ tsig [ZONE...] {
|
|||||||
Each key may also specify an `algorithm` e.g. `algorithm hmac-sha256;`, but this is currently ignored by the plugin.
|
Each key may also specify an `algorithm` e.g. `algorithm hmac-sha256;`, but this is currently ignored by the plugin.
|
||||||
|
|
||||||
* `require` **QTYPE...** - the query types that must be TSIG'd. Requests of the specified types
|
* `require` **QTYPE...** - the query types that must be TSIG'd. Requests of the specified types
|
||||||
will be `REFUSED` if they are not signed.`require all` will require requests of all types to be
|
will be `REFUSED` if they are not signed. `require all` will require requests of all types to be
|
||||||
signed. `require none` will not require requests any types to be signed. Default behavior is to not require.
|
signed. `require none` will not require requests any types to be signed. Default behavior is to not require.
|
||||||
|
|
||||||
|
* `require_opcode` **OPCODE...** - the opcodes that must be TSIG'd. Requests with the specified opcodes
|
||||||
|
will be `REFUSED` if they are not signed. Valid opcodes are: `QUERY`, `IQUERY`, `STATUS`, `NOTIFY`, `UPDATE`.
|
||||||
|
`require_opcode all` will require requests with all opcodes to be signed. `require_opcode none` will not
|
||||||
|
require requests with any opcode to be signed. Default behavior is to not require.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Require TSIG signed transactions for transfer requests to `example.zone`.
|
Require TSIG signed transactions for transfer requests to `example.zone`.
|
||||||
@@ -68,6 +74,17 @@ auth.zone {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Require TSIG signed transactions for UPDATE and NOTIFY operations to `dynamic.zone`.
|
||||||
|
|
||||||
|
```
|
||||||
|
dynamic.zone {
|
||||||
|
tsig {
|
||||||
|
secret dynamic.zone.key. NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk=
|
||||||
|
require_opcode UPDATE NOTIFY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
### Secondary
|
### Secondary
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ func parse(c *caddy.Controller) (*TSIGServer, error) {
|
|||||||
t := &TSIGServer{
|
t := &TSIGServer{
|
||||||
secrets: make(map[string]string),
|
secrets: make(map[string]string),
|
||||||
types: defaultQTypes,
|
types: defaultQTypes,
|
||||||
|
opcodes: defaultOpCodes,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; c.Next(); i++ {
|
for i := 0; c.Next(); i++ {
|
||||||
@@ -89,7 +90,7 @@ func parse(c *caddy.Controller) (*TSIGServer, error) {
|
|||||||
return nil, c.ArgErr()
|
return nil, c.ArgErr()
|
||||||
}
|
}
|
||||||
if args[0] == "all" {
|
if args[0] == "all" {
|
||||||
t.all = true
|
t.allTypes = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if args[0] == "none" {
|
if args[0] == "none" {
|
||||||
@@ -102,6 +103,26 @@ func parse(c *caddy.Controller) (*TSIGServer, error) {
|
|||||||
}
|
}
|
||||||
t.types[qt] = struct{}{}
|
t.types[qt] = struct{}{}
|
||||||
}
|
}
|
||||||
|
case "require_opcode":
|
||||||
|
t.opcodes = opCodes{}
|
||||||
|
args := c.RemainingArgs()
|
||||||
|
if len(args) == 0 {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
if args[0] == "all" {
|
||||||
|
t.allOpcodes = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if args[0] == "none" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, str := range args {
|
||||||
|
op, ok := dns.StringToOpcode[str]
|
||||||
|
if !ok {
|
||||||
|
return nil, c.Errf("unknown opcode '%s'", str)
|
||||||
|
}
|
||||||
|
t.opcodes[op] = struct{}{}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, c.Errf("unknown property '%s'", c.Val())
|
return nil, c.Errf("unknown property '%s'", c.Val())
|
||||||
}
|
}
|
||||||
@@ -166,3 +187,4 @@ func parseKeyFile(f io.Reader) (map[string]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var defaultQTypes = qTypes{}
|
var defaultQTypes = qTypes{}
|
||||||
|
var defaultOpCodes = opCodes{}
|
||||||
|
|||||||
@@ -36,45 +36,81 @@ key "name2.key." {
|
|||||||
shouldErr bool
|
shouldErr bool
|
||||||
expectedZones []string
|
expectedZones []string
|
||||||
expectedQTypes qTypes
|
expectedQTypes qTypes
|
||||||
|
expectedOpCodes opCodes
|
||||||
expectedSecrets map[string]string
|
expectedSecrets map[string]string
|
||||||
expectedAll bool
|
expectedAllTypes bool
|
||||||
|
expectedAllOpcodes bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: "tsig {\n " + secretConfig + "}",
|
input: "tsig {\n " + secretConfig + "}",
|
||||||
expectedZones: []string{"."},
|
expectedZones: []string{"."},
|
||||||
expectedQTypes: defaultQTypes,
|
expectedQTypes: defaultQTypes,
|
||||||
|
expectedOpCodes: defaultOpCodes,
|
||||||
expectedSecrets: secrets,
|
expectedSecrets: secrets,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tsig {\n secrets " + secretsFile + "\n}",
|
input: "tsig {\n secrets " + secretsFile + "\n}",
|
||||||
expectedZones: []string{"."},
|
expectedZones: []string{"."},
|
||||||
expectedQTypes: defaultQTypes,
|
expectedQTypes: defaultQTypes,
|
||||||
|
expectedOpCodes: defaultOpCodes,
|
||||||
expectedSecrets: secrets,
|
expectedSecrets: secrets,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tsig example.com {\n " + secretConfig + "}",
|
input: "tsig example.com {\n " + secretConfig + "}",
|
||||||
expectedZones: []string{"example.com."},
|
expectedZones: []string{"example.com."},
|
||||||
expectedQTypes: defaultQTypes,
|
expectedQTypes: defaultQTypes,
|
||||||
|
expectedOpCodes: defaultOpCodes,
|
||||||
expectedSecrets: secrets,
|
expectedSecrets: secrets,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tsig {\n " + secretConfig + " require all \n}",
|
input: "tsig {\n " + secretConfig + " require all \n}",
|
||||||
expectedZones: []string{"."},
|
expectedZones: []string{"."},
|
||||||
expectedQTypes: qTypes{},
|
expectedQTypes: qTypes{},
|
||||||
expectedAll: true,
|
expectedOpCodes: defaultOpCodes,
|
||||||
|
expectedAllTypes: true,
|
||||||
expectedSecrets: secrets,
|
expectedSecrets: secrets,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tsig {\n " + secretConfig + " require none \n}",
|
input: "tsig {\n " + secretConfig + " require none \n}",
|
||||||
expectedZones: []string{"."},
|
expectedZones: []string{"."},
|
||||||
expectedQTypes: qTypes{},
|
expectedQTypes: qTypes{},
|
||||||
expectedAll: false,
|
expectedOpCodes: defaultOpCodes,
|
||||||
expectedSecrets: secrets,
|
expectedSecrets: secrets,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "tsig {\n " + secretConfig + " \n require A AAAA \n}",
|
input: "tsig {\n " + secretConfig + " \n require A AAAA \n}",
|
||||||
expectedZones: []string{"."},
|
expectedZones: []string{"."},
|
||||||
expectedQTypes: qTypes{dns.TypeA: {}, dns.TypeAAAA: {}},
|
expectedQTypes: qTypes{dns.TypeA: {}, dns.TypeAAAA: {}},
|
||||||
|
expectedOpCodes: defaultOpCodes,
|
||||||
|
expectedSecrets: secrets,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "tsig {\n " + secretConfig + " \n require_opcode UPDATE NOTIFY \n}",
|
||||||
|
expectedZones: []string{"."},
|
||||||
|
expectedQTypes: defaultQTypes,
|
||||||
|
expectedOpCodes: opCodes{dns.OpcodeUpdate: {}, dns.OpcodeNotify: {}},
|
||||||
|
expectedSecrets: secrets,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "tsig {\n " + secretConfig + " \n require_opcode all \n}",
|
||||||
|
expectedZones: []string{"."},
|
||||||
|
expectedQTypes: defaultQTypes,
|
||||||
|
expectedOpCodes: opCodes{},
|
||||||
|
expectedAllOpcodes: true,
|
||||||
|
expectedSecrets: secrets,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "tsig {\n " + secretConfig + " \n require_opcode none \n}",
|
||||||
|
expectedZones: []string{"."},
|
||||||
|
expectedQTypes: defaultQTypes,
|
||||||
|
expectedOpCodes: opCodes{},
|
||||||
|
expectedSecrets: secrets,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "tsig {\n " + secretConfig + " \n require AXFR \n require_opcode UPDATE \n}",
|
||||||
|
expectedZones: []string{"."},
|
||||||
|
expectedQTypes: qTypes{dns.TypeAXFR: {}},
|
||||||
|
expectedOpCodes: opCodes{dns.OpcodeUpdate: {}},
|
||||||
expectedSecrets: secrets,
|
expectedSecrets: secrets,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -93,6 +129,14 @@ key "name2.key." {
|
|||||||
input: "tsig {\n require invalid-qtype \n}",
|
input: "tsig {\n require invalid-qtype \n}",
|
||||||
shouldErr: true,
|
shouldErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: "tsig {\n require_opcode \n}",
|
||||||
|
shouldErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "tsig {\n require_opcode INVALID \n}",
|
||||||
|
shouldErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
serverBlockKeys := []string{"."}
|
serverBlockKeys := []string{"."}
|
||||||
@@ -121,8 +165,12 @@ key "name2.key." {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if test.expectedAll != ts.all {
|
if test.expectedAllTypes != ts.allTypes {
|
||||||
t.Errorf("Test %d expected require all to be '%v', but got '%v'.", i, test.expectedAll, ts.all)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(test.expectedQTypes) != len(ts.types) {
|
if len(test.expectedQTypes) != len(ts.types) {
|
||||||
@@ -135,6 +183,16 @@ key "name2.key." {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(test.expectedSecrets) != len(ts.secrets) {
|
if len(test.expectedSecrets) != len(ts.secrets) {
|
||||||
t.Fatalf("Test %d expected secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets)
|
t.Fatalf("Test %d expected secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ type TSIGServer struct {
|
|||||||
Zones []string
|
Zones []string
|
||||||
secrets map[string]string // [key-name]secret
|
secrets map[string]string // [key-name]secret
|
||||||
types qTypes
|
types qTypes
|
||||||
all bool
|
opcodes opCodes
|
||||||
|
allTypes bool
|
||||||
|
allOpcodes bool
|
||||||
Next plugin.Handler
|
Next plugin.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type qTypes map[uint16]struct{}
|
type qTypes map[uint16]struct{}
|
||||||
|
type opCodes map[int]struct{}
|
||||||
|
|
||||||
// Name implements plugin.Handler
|
// Name implements plugin.Handler
|
||||||
func (t TSIGServer) Name() string { return pluginName }
|
func (t TSIGServer) Name() string { return pluginName }
|
||||||
@@ -37,7 +40,7 @@ func (t *TSIGServer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.
|
|||||||
|
|
||||||
var tsigRR = r.IsTsig()
|
var tsigRR = r.IsTsig()
|
||||||
rcode := dns.RcodeSuccess
|
rcode := dns.RcodeSuccess
|
||||||
if !t.tsigRequired(state.QType()) && tsigRR == nil {
|
if !t.tsigRequired(state.QType(), r.Opcode) && tsigRR == nil {
|
||||||
return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
|
return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,14 +91,18 @@ func (t *TSIGServer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.
|
|||||||
return dns.RcodeSuccess, nil
|
return dns.RcodeSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TSIGServer) tsigRequired(qtype uint16) bool {
|
func (t *TSIGServer) tsigRequired(qtype uint16, opcode int) bool {
|
||||||
if t.all {
|
typeMatches := t.allTypes
|
||||||
return true
|
if !typeMatches {
|
||||||
|
_, typeMatches = t.types[qtype]
|
||||||
}
|
}
|
||||||
if _, ok := t.types[qtype]; ok {
|
|
||||||
return true
|
opcodeMatches := t.allOpcodes
|
||||||
|
if !opcodeMatches {
|
||||||
|
_, opcodeMatches = t.opcodes[opcode]
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
|
return typeMatches || opcodeMatches
|
||||||
}
|
}
|
||||||
|
|
||||||
// restoreTsigWriter Implement Response Writer, and adds a TSIG RR to a response
|
// restoreTsigWriter Implement Response Writer, and adds a TSIG RR to a response
|
||||||
|
|||||||
@@ -17,40 +17,48 @@ func TestServeDNS(t *testing.T) {
|
|||||||
cases := []struct {
|
cases := []struct {
|
||||||
zones []string
|
zones []string
|
||||||
reqTypes qTypes
|
reqTypes qTypes
|
||||||
|
reqOpCodes opCodes
|
||||||
qType uint16
|
qType uint16
|
||||||
qTsig, all bool
|
opcode int
|
||||||
|
qTsig bool
|
||||||
|
allTypes bool
|
||||||
|
allOpcodes bool
|
||||||
expectRcode int
|
expectRcode int
|
||||||
expectTsig bool
|
expectTsig bool
|
||||||
statusError bool
|
statusError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
zones: []string{"."},
|
zones: []string{"."},
|
||||||
all: true,
|
allTypes: true,
|
||||||
qType: dns.TypeA,
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: true,
|
qTsig: true,
|
||||||
expectRcode: dns.RcodeSuccess,
|
expectRcode: dns.RcodeSuccess,
|
||||||
expectTsig: true,
|
expectTsig: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
zones: []string{"."},
|
zones: []string{"."},
|
||||||
all: true,
|
allTypes: true,
|
||||||
qType: dns.TypeA,
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: false,
|
qTsig: false,
|
||||||
expectRcode: dns.RcodeRefused,
|
expectRcode: dns.RcodeRefused,
|
||||||
expectTsig: false,
|
expectTsig: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
zones: []string{"another.domain."},
|
zones: []string{"another.domain."},
|
||||||
all: true,
|
allTypes: true,
|
||||||
qType: dns.TypeA,
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: false,
|
qTsig: false,
|
||||||
expectRcode: dns.RcodeSuccess,
|
expectRcode: dns.RcodeSuccess,
|
||||||
expectTsig: false,
|
expectTsig: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
zones: []string{"another.domain."},
|
zones: []string{"another.domain."},
|
||||||
all: true,
|
allTypes: true,
|
||||||
qType: dns.TypeA,
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: true,
|
qTsig: true,
|
||||||
expectRcode: dns.RcodeSuccess,
|
expectRcode: dns.RcodeSuccess,
|
||||||
expectTsig: false,
|
expectTsig: false,
|
||||||
@@ -59,6 +67,7 @@ func TestServeDNS(t *testing.T) {
|
|||||||
zones: []string{"."},
|
zones: []string{"."},
|
||||||
reqTypes: qTypes{dns.TypeAXFR: {}},
|
reqTypes: qTypes{dns.TypeAXFR: {}},
|
||||||
qType: dns.TypeAXFR,
|
qType: dns.TypeAXFR,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: true,
|
qTsig: true,
|
||||||
expectRcode: dns.RcodeSuccess,
|
expectRcode: dns.RcodeSuccess,
|
||||||
expectTsig: true,
|
expectTsig: true,
|
||||||
@@ -67,6 +76,7 @@ func TestServeDNS(t *testing.T) {
|
|||||||
zones: []string{"."},
|
zones: []string{"."},
|
||||||
reqTypes: qTypes{},
|
reqTypes: qTypes{},
|
||||||
qType: dns.TypeA,
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: false,
|
qTsig: false,
|
||||||
expectRcode: dns.RcodeSuccess,
|
expectRcode: dns.RcodeSuccess,
|
||||||
expectTsig: false,
|
expectTsig: false,
|
||||||
@@ -75,27 +85,98 @@ func TestServeDNS(t *testing.T) {
|
|||||||
zones: []string{"."},
|
zones: []string{"."},
|
||||||
reqTypes: qTypes{},
|
reqTypes: qTypes{},
|
||||||
qType: dns.TypeA,
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: true,
|
qTsig: true,
|
||||||
expectRcode: dns.RcodeSuccess,
|
expectRcode: dns.RcodeSuccess,
|
||||||
expectTsig: true,
|
expectTsig: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
zones: []string{"."},
|
zones: []string{"."},
|
||||||
all: true,
|
allTypes: true,
|
||||||
qType: dns.TypeA,
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
qTsig: true,
|
qTsig: true,
|
||||||
expectRcode: dns.RcodeNotAuth,
|
expectRcode: dns.RcodeNotAuth,
|
||||||
expectTsig: true,
|
expectTsig: true,
|
||||||
statusError: true,
|
statusError: true,
|
||||||
},
|
},
|
||||||
|
// Opcode-based tests
|
||||||
|
{
|
||||||
|
zones: []string{"."},
|
||||||
|
reqOpCodes: opCodes{dns.OpcodeUpdate: {}},
|
||||||
|
qType: dns.TypeSOA,
|
||||||
|
opcode: dns.OpcodeUpdate,
|
||||||
|
qTsig: true,
|
||||||
|
expectRcode: dns.RcodeSuccess,
|
||||||
|
expectTsig: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
zones: []string{"."},
|
||||||
|
reqOpCodes: opCodes{dns.OpcodeUpdate: {}},
|
||||||
|
qType: dns.TypeSOA,
|
||||||
|
opcode: dns.OpcodeUpdate,
|
||||||
|
qTsig: false,
|
||||||
|
expectRcode: dns.RcodeRefused,
|
||||||
|
expectTsig: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
zones: []string{"."},
|
||||||
|
reqOpCodes: opCodes{dns.OpcodeUpdate: {}},
|
||||||
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
|
qTsig: false,
|
||||||
|
expectRcode: dns.RcodeSuccess,
|
||||||
|
expectTsig: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
zones: []string{"."},
|
||||||
|
reqOpCodes: opCodes{dns.OpcodeNotify: {}},
|
||||||
|
qType: dns.TypeSOA,
|
||||||
|
opcode: dns.OpcodeNotify,
|
||||||
|
qTsig: true,
|
||||||
|
expectRcode: dns.RcodeSuccess,
|
||||||
|
expectTsig: true,
|
||||||
|
},
|
||||||
|
// Combined qtype and opcode requirement
|
||||||
|
{
|
||||||
|
zones: []string{"."},
|
||||||
|
reqTypes: qTypes{dns.TypeAXFR: {}},
|
||||||
|
reqOpCodes: opCodes{dns.OpcodeUpdate: {}},
|
||||||
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeUpdate,
|
||||||
|
qTsig: true,
|
||||||
|
expectRcode: dns.RcodeSuccess,
|
||||||
|
expectTsig: true,
|
||||||
|
},
|
||||||
|
// allOpcodes test
|
||||||
|
{
|
||||||
|
zones: []string{"."},
|
||||||
|
allOpcodes: true,
|
||||||
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
|
qTsig: true,
|
||||||
|
expectRcode: dns.RcodeSuccess,
|
||||||
|
expectTsig: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
zones: []string{"."},
|
||||||
|
allOpcodes: true,
|
||||||
|
qType: dns.TypeA,
|
||||||
|
opcode: dns.OpcodeQuery,
|
||||||
|
qTsig: false,
|
||||||
|
expectRcode: dns.RcodeRefused,
|
||||||
|
expectTsig: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
tsig := TSIGServer{
|
tsig := TSIGServer{
|
||||||
Zones: tc.zones,
|
Zones: tc.zones,
|
||||||
all: tc.all,
|
allTypes: tc.allTypes,
|
||||||
|
allOpcodes: tc.allOpcodes,
|
||||||
types: tc.reqTypes,
|
types: tc.reqTypes,
|
||||||
|
opcodes: tc.reqOpCodes,
|
||||||
Next: testHandler(),
|
Next: testHandler(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +190,7 @@ func TestServeDNS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
r := new(dns.Msg)
|
r := new(dns.Msg)
|
||||||
r.SetQuestion("test.example.", tc.qType)
|
r.SetQuestion("test.example.", tc.qType)
|
||||||
|
r.Opcode = tc.opcode
|
||||||
if tc.qTsig {
|
if tc.qTsig {
|
||||||
r.SetTsig("test.key.", dns.HmacSHA256, 300, time.Now().Unix())
|
r.SetTsig("test.key.", dns.HmacSHA256, 300, time.Now().Unix())
|
||||||
}
|
}
|
||||||
@@ -171,7 +253,7 @@ func TestServeDNSTsigErrors(t *testing.T) {
|
|||||||
|
|
||||||
tsig := TSIGServer{
|
tsig := TSIGServer{
|
||||||
Zones: []string{"."},
|
Zones: []string{"."},
|
||||||
all: true,
|
allTypes: true,
|
||||||
Next: testHandler(),
|
Next: testHandler(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user