Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add $OPTION directive and "fallthrough" option #6

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ This plugin can only be used once per Server Block.

~~~
records [ZONES...] {
$OPTION [OPTION]
fuhry marked this conversation as resolved.
Show resolved Hide resolved
[INLINE]
}
~~~
Expand All @@ -42,6 +43,9 @@ records [ZONES...] {
* **INLINE** the resource record that are to be served. These must be specified as the text
represenation (as specifed in RFC 1035) of the record. See the examples below. Each record must
be on a single line.
* **OPTION** is a configuration option for the plugin. The following options are supported:
* `fallthrough`: When no matching record is found, instead of returning NXDOMAIN, the plugin will
call to the next plugin in the chain.

If domain name in **INLINE** are not fully qualifed each of the **ZONES** are used as the origin and
added to the names.
Expand Down Expand Up @@ -73,6 +77,22 @@ RFC 1035 zone file and everything after it will be ignored, hence the need for q
}
~~~

Override the record for `example.com`, without overriding anything else. Subdomains, like
`foo.example.com`, will continue to be resolved normally (the `forward` plugin, in this case).

~~~
. {
records . {
$OPTION fallthrough
example.com. 300 IN A 127.0.0.1
}

forward . 192.168.0.1 {
except example.com
}
}
~~~

## Bugs

DNSSEC, nor wildcards are implemented. The lookup algorithm is pretty basic. Future enhancements
Expand Down
6 changes: 6 additions & 0 deletions records.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/request"
"github.com/coredns/coredns/plugin/pkg/fall"

"github.com/miekg/dns"
)
Expand All @@ -15,6 +16,7 @@ type Records struct {
m map[string][]dns.RR

Next plugin.Handler
Fall *fall.F
}

// ServeDNS implements the plugin.Handle interface.
Expand Down Expand Up @@ -48,6 +50,9 @@ func (re *Records) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms

// handle NXDOMAIN, NODATA and normal response here.
if nxdomain {
if re.Fall.Through(qname) {
return plugin.NextOrFailure(re.Name(), re.Next, ctx, w, r)
}
m.Rcode = dns.RcodeNameError
if soa != nil {
m.Ns = []dns.RR{soa}
Expand All @@ -73,5 +78,6 @@ func (re *Records) Name() string { return "records" }
func New() *Records {
re := new(Records)
re.m = make(map[string][]dns.RR)
re.Fall = &fall.F{}
return re
}
70 changes: 70 additions & 0 deletions records_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package records

import (
"context"
"errors"
"testing"

"github.com/coredns/coredns/plugin/pkg/dnstest"
Expand Down Expand Up @@ -175,3 +176,72 @@ var testCasesMultipleOrigins = []test.Case{
},
},
}

func TestLookupFallThrough(t *testing.T) {
const input = `
records example.org {
$OPTION fallthrough
@ 60 IN A 127.0.0.1
}
`

c := caddy.NewTestController("dns", input)
re, err := recordsParse(c)
if err != nil {
t.Fatal(err)
}

tests:
for i, tc := range testCasesFallThrough {
m := tc.Msg()

rec := dnstest.NewRecorder(&test.ResponseWriter{})
_, err := re.ServeDNS(context.Background(), rec, m)
if tc.Error != nil && err == nil {
t.Errorf("Test %d, expected error %q, no error returned", i, tc.Error.Error())
return
}

if tc.Error == nil && err != nil {
t.Errorf("Test %d, expected no error, got %v", i, err)
return
}

if tc.Error != nil && err != nil {
if tc.Error.Error() != err.Error() {
t.Errorf("Test %d, expected error message %q, got %q", i, tc.Error.Error(), err.Error())
return
}
continue tests
}

if rec.Msg == nil {
t.Errorf("Test %d, no message received", i)
return
}

if rec.Msg.Rcode != tc.Rcode {
t.Errorf("Test %d, expected rcode is %d, but got %d", i, tc.Rcode, rec.Msg.Rcode)
return
}

if resp := rec.Msg; rec.Msg != nil {
if err := test.SortAndCheck(resp, tc); err != nil {
t.Errorf("Test %d: %v", i, err)
}
}
}
}

var testCasesFallThrough = []test.Case{
{
Qname: "example.org.", Qtype: dns.TypeA,
Answer: []dns.RR{
test.A("example.org. 60 IN A 127.0.0.1"),
},
},
{
Qname: "foo.example.net.", Qtype: dns.TypeA,
Error: errors.New("plugin/records: no next plugin found"),
},
}
22 changes: 20 additions & 2 deletions setup.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package records

import (
"fmt"
"strings"

"github.com/coredns/caddy"
Expand Down Expand Up @@ -57,9 +58,26 @@ func recordsParse(c *caddy.Controller) (*Records, error) {
// c.Val() + c.RemainingArgs() is the record we need to parse (for each zone given; now tracked in re.origins). When parsing
// the record we just set the ORIGIN to the correct value and magic will happen. If no origin we set it to "."

parseBlocks:
for c.NextBlock() {
s := c.Val() + " "
s += strings.Join(c.RemainingArgs(), " ")
s := c.Val()
if s == "$OPTION" {
if !c.NextArg() {
return nil, fmt.Errorf("parsing block failed: $OPTION missing argument")
}
opt := c.Val()
switch opt {
case "fallthrough":
re.Fall.SetZonesFromArgs(re.origins)
default:
return nil, fmt.Errorf("parsing block failed: unknown option: %q", opt)
}
if len(c.RemainingArgs()) > 0 {
return nil, fmt.Errorf("parsing block failed: extra arguments after option %q", opt)
}
continue parseBlocks
}
s += " " + strings.Join(c.RemainingArgs(), " ")
for _, o := range re.origins {
rr, err := dns.NewRR("$ORIGIN " + o + "\n" + s + "\n")
if err != nil {
Expand Down