Skip to content

Commit bd8474d

Browse files
committed
Add $OPTION directive and "fallthrough" option
Add the `$OPTION` directive to allow configuring the plugin. Implement an option, "fallthrough" which causes the plugin to continue to the next plugin in the chain when there is no match, instead of always returning NXDOMAIN. Signed-Off-By: Dan Fuhry <dan@fuhry.com>
1 parent e8b4cfd commit bd8474d

File tree

4 files changed

+116
-2
lines changed

4 files changed

+116
-2
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ This plugin can only be used once per Server Block.
3333

3434
~~~
3535
records [ZONES...] {
36+
$OPTION [OPTION]
3637
[INLINE]
3738
}
3839
~~~
@@ -42,6 +43,9 @@ records [ZONES...] {
4243
* **INLINE** the resource record that are to be served. These must be specified as the text
4344
represenation (as specifed in RFC 1035) of the record. See the examples below. Each record must
4445
be on a single line.
46+
* **OPTION** is a configuration option for the plugin. The following options are supported:
47+
* `fallthrough`: When no matching record is found, instead of returning NXDOMAIN, the plugin will
48+
call to the next plugin in the chain.
4549

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

80+
Override the record for `example.com`, without overriding anything else. Subdomains, like
81+
`foo.example.com`, will continue to be resolved normally (the `forward` plugin, in this case).
82+
83+
~~~
84+
. {
85+
records . {
86+
$OPTION fallthrough
87+
example.com. 300 IN A 127.0.0.1
88+
}
89+
90+
forward . 192.168.0.1 {
91+
except example.com
92+
}
93+
}
94+
~~~
95+
7696
## Bugs
7797

7898
DNSSEC, nor wildcards are implemented. The lookup algorithm is pretty basic. Future enhancements

records.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/coredns/coredns/plugin"
77
"github.com/coredns/coredns/request"
8+
"github.com/coredns/coredns/plugin/pkg/fall"
89

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

1718
Next plugin.Handler
19+
Fall *fall.F
1820
}
1921

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

4951
// handle NXDOMAIN, NODATA and normal response here.
5052
if nxdomain {
53+
if re.Fall.Through(qname) {
54+
return plugin.NextOrFailure(re.Name(), re.Next, ctx, w, r)
55+
}
5156
m.Rcode = dns.RcodeNameError
5257
if soa != nil {
5358
m.Ns = []dns.RR{soa}
@@ -73,5 +78,6 @@ func (re *Records) Name() string { return "records" }
7378
func New() *Records {
7479
re := new(Records)
7580
re.m = make(map[string][]dns.RR)
81+
re.Fall = &fall.F{}
7682
return re
7783
}

records_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package records
22

33
import (
44
"context"
5+
"errors"
56
"testing"
67

78
"github.com/coredns/coredns/plugin/pkg/dnstest"
@@ -175,3 +176,72 @@ var testCasesMultipleOrigins = []test.Case{
175176
},
176177
},
177178
}
179+
180+
func TestLookupFallThrough(t *testing.T) {
181+
const input = `
182+
records example.org {
183+
$OPTION fallthrough
184+
@ 60 IN A 127.0.0.1
185+
}
186+
`
187+
188+
c := caddy.NewTestController("dns", input)
189+
re, err := recordsParse(c)
190+
if err != nil {
191+
t.Fatal(err)
192+
}
193+
194+
tests:
195+
for i, tc := range testCasesFallThrough {
196+
m := tc.Msg()
197+
198+
rec := dnstest.NewRecorder(&test.ResponseWriter{})
199+
_, err := re.ServeDNS(context.Background(), rec, m)
200+
if tc.Error != nil && err == nil {
201+
t.Errorf("Test %d, expected error %q, no error returned", i, tc.Error.Error())
202+
return
203+
}
204+
205+
if tc.Error == nil && err != nil {
206+
t.Errorf("Test %d, expected no error, got %v", i, err)
207+
return
208+
}
209+
210+
if tc.Error != nil && err != nil {
211+
if tc.Error.Error() != err.Error() {
212+
t.Errorf("Test %d, expected error message %q, got %q", i, tc.Error.Error(), err.Error())
213+
return
214+
}
215+
continue tests
216+
}
217+
218+
if rec.Msg == nil {
219+
t.Errorf("Test %d, no message received", i)
220+
return
221+
}
222+
223+
if rec.Msg.Rcode != tc.Rcode {
224+
t.Errorf("Test %d, expected rcode is %d, but got %d", i, tc.Rcode, rec.Msg.Rcode)
225+
return
226+
}
227+
228+
if resp := rec.Msg; rec.Msg != nil {
229+
if err := test.SortAndCheck(resp, tc); err != nil {
230+
t.Errorf("Test %d: %v", i, err)
231+
}
232+
}
233+
}
234+
}
235+
236+
var testCasesFallThrough = []test.Case{
237+
{
238+
Qname: "example.org.", Qtype: dns.TypeA,
239+
Answer: []dns.RR{
240+
test.A("example.org. 60 IN A 127.0.0.1"),
241+
},
242+
},
243+
{
244+
Qname: "foo.example.net.", Qtype: dns.TypeA,
245+
Error: errors.New("plugin/records: no next plugin found"),
246+
},
247+
}

setup.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package records
22

33
import (
4+
"fmt"
45
"strings"
56

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

61+
parseBlocks:
6062
for c.NextBlock() {
61-
s := c.Val() + " "
62-
s += strings.Join(c.RemainingArgs(), " ")
63+
s := c.Val()
64+
if s == "$OPTION" {
65+
if !c.NextArg() {
66+
return nil, fmt.Errorf("parsing block failed: $OPTION missing argument")
67+
}
68+
opt := c.Val()
69+
switch opt {
70+
case "fallthrough":
71+
re.Fall.SetZonesFromArgs(re.origins)
72+
default:
73+
return nil, fmt.Errorf("parsing block failed: unknown option: %q", opt)
74+
}
75+
if len(c.RemainingArgs()) > 0 {
76+
return nil, fmt.Errorf("parsing block failed: extra arguments after option %q", opt)
77+
}
78+
continue parseBlocks
79+
}
80+
s += " " + strings.Join(c.RemainingArgs(), " ")
6381
for _, o := range re.origins {
6482
rr, err := dns.NewRR("$ORIGIN " + o + "\n" + s + "\n")
6583
if err != nil {

0 commit comments

Comments
 (0)