-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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 fallback plugin (DNS endpoints only) #1398
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# fallback | ||
|
||
## Name | ||
|
||
*fallback* - send failed DNS queries to fallback endpoints. | ||
|
||
## Description | ||
|
||
This plugin sends failed DNS quieres (e.g., NXDOMAIN, SERVFAIL, etc) | ||
to fallback endpoints. | ||
|
||
## Syntax | ||
|
||
~~~ txt | ||
fallback [ZONE] { | ||
on [FAILURE] [ENDPOINTS...] | ||
} | ||
~~~ | ||
|
||
* **ZONE** the name of the domain to be accessed. | ||
* **FAILURE** the failure code of the DNS queries (NXDOMAIN, SERVFAIL, etc.). | ||
* **ENDPOINTS** the fallback endpoints to send to. | ||
|
||
## Examples | ||
|
||
~~~ corefile | ||
. { | ||
fallback example.org { | ||
on NXDOMAIN 10.10.10.10:53 | ||
} | ||
} | ||
~~~ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package fallback | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/coredns/coredns/plugin" | ||
"github.com/coredns/coredns/request" | ||
|
||
"github.com/miekg/dns" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
type processor struct { | ||
rcode int | ||
endpoint string | ||
} | ||
|
||
func (p *processor) ErrorFunc(w dns.ResponseWriter, r *dns.Msg, rc int) error { | ||
if rc == p.rcode { | ||
state := request.Request{W: w, Req: r} | ||
qname := state.Name() | ||
log.Printf("[INFO] Send fallback %q to %q", qname, p.endpoint) | ||
_, err := dns.Exchange(r, p.endpoint) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand we use directly Exchange as most of plugin are using Proxy.newLookup(...). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Knowing that this ErrorFunc can now be called whenever in the ServeDNS chain, including before any plugin is called, would it be safe to verify that the request is correctly defined ? see in server.ServeDNS:
|
||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// Fallback is a plugin that provide fallback in case of error | ||
type Fallback struct { | ||
Next plugin.Handler | ||
zones []string | ||
funcs []processor | ||
} | ||
|
||
// ServeDNS implements the plugin.Handler interface. | ||
func (f Fallback) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { | ||
return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r) | ||
} | ||
|
||
// Name implements the Handler interface. | ||
func (f Fallback) Name() string { return "fallback" } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package fallback | ||
|
||
import ( | ||
"github.com/coredns/coredns/core/dnsserver" | ||
"github.com/coredns/coredns/plugin" | ||
"github.com/coredns/coredns/request" | ||
|
||
"github.com/mholt/caddy" | ||
"github.com/miekg/dns" | ||
) | ||
|
||
func init() { | ||
caddy.RegisterPlugin("fallback", caddy.Plugin{ | ||
ServerType: "dns", | ||
Action: setup, | ||
}) | ||
} | ||
|
||
func setup(c *caddy.Controller) error { | ||
f := Fallback{} | ||
|
||
for c.Next() { | ||
f.zones = c.RemainingArgs() | ||
if len(f.zones) == 0 { | ||
f.zones = make([]string, len(c.ServerBlockKeys)) | ||
copy(f.zones, c.ServerBlockKeys) | ||
} | ||
plugin.Zones(f.zones).Normalize() | ||
|
||
for c.NextBlock() { | ||
switch c.Val() { | ||
case "on": | ||
args := c.RemainingArgs() | ||
if len(args) < 2 { | ||
return c.Errf("unknown property '%v'", args) | ||
} | ||
rcode := 0 | ||
switch args[0] { | ||
case "SERVFAIL": | ||
rcode = dns.RcodeServerFailure | ||
case "NXDOMAIN": | ||
rcode = dns.RcodeNameError | ||
case "REFUSED": | ||
rcode = dns.RcodeRefused | ||
default: | ||
return c.Errf("unknown property '%v'", args) | ||
} | ||
for _, arg := range args[1:] { | ||
f.funcs = append(f.funcs, processor{ | ||
rcode: rcode, | ||
endpoint: arg, | ||
}) | ||
} | ||
|
||
default: | ||
return c.Errf("unknown property '%s'", c.Val()) | ||
} | ||
} | ||
} | ||
|
||
c.OnStartup(func() error { | ||
defaultErrorFunc := dnsserver.DefaultErrorFunc | ||
dnsserver.DefaultErrorFunc = func(w dns.ResponseWriter, r *dns.Msg, rc int) { | ||
state := request.Request{W: w, Req: r} | ||
qname := state.Name() | ||
|
||
zone := plugin.Zones(f.zones).Matches(qname) | ||
if zone != "" { | ||
for i := range f.funcs { | ||
if err := f.funcs[i].ErrorFunc(w, r, rc); err == nil { | ||
break | ||
} | ||
} | ||
} | ||
defaultErrorFunc(w, r, rc) | ||
} | ||
return nil | ||
}) | ||
|
||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { | ||
f.Next = next | ||
return f | ||
}) | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package fallback | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/mholt/caddy" | ||
) | ||
|
||
func TestSetupFallback(t *testing.T) { | ||
c := caddy.NewTestController("dns", `fallback`) | ||
if err := setup(c); err != nil { | ||
t.Fatalf("Expected no errors, but got: %v", err) | ||
} | ||
|
||
c = caddy.NewTestController("dns", `fallback example.org { | ||
on NXDOMAIN 10.10.10.10:100 8.8.8.8:53 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test with several "on XXXX" ? |
||
}`) | ||
if err := setup(c); err != nil { | ||
t.Fatalf("Expected no errors, but got: %v", err) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package test | ||
|
||
import ( | ||
"bytes" | ||
"log" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/coredns/coredns/plugin/proxy" | ||
"github.com/coredns/coredns/plugin/test" | ||
"github.com/coredns/coredns/request" | ||
|
||
"github.com/miekg/dns" | ||
) | ||
|
||
func TestFallbackLookup(t *testing.T) { | ||
corefile := `fallback.org:0 { | ||
fallback fallback.org { | ||
on SERVFAIL 127.0.0.1:999 | ||
} | ||
}` | ||
|
||
i, udp, _, err := CoreDNSServerAndPorts(corefile) | ||
if err != nil { | ||
t.Fatalf("Could not get CoreDNS serving instance: %s", err) | ||
} | ||
defer i.Stop() | ||
|
||
var b bytes.Buffer | ||
log.SetOutput(&b) | ||
|
||
p := proxy.NewLookup([]string{udp}) | ||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)} | ||
|
||
resp, err := p.Lookup(state, "nop.fallback.org.", dns.TypeA) | ||
if err != nil { | ||
t.Fatal("Expected to receive reply, but didn't") | ||
} | ||
// expect no answer section | ||
if len(resp.Answer) != 0 { | ||
t.Fatalf("Expected no RR in the answer section, got %d", len(resp.Answer)) | ||
} | ||
if !strings.Contains(b.String(), `[INFO] Send fallback "nop.fallback.org." to "127.0.0.1:999"`) { | ||
t.Fatal("Expected to receive log but didn't") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe exemple with several failures cases - to show the full usage.
on NXDOMAIN
on SERVFAIL
on .. ???
is there a list of all events that can happen and we can use ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes : SERVFAIL, NXDOMAIN, ERROR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should NXDOMAIN be DENIAL ? or is there a NODATA ?