Skip to content

Commit

Permalink
Add bufsize plugin for preparing the DNS Flag Day and avoiding IP fra…
Browse files Browse the repository at this point in the history
…gmentation (coredns#3401)

* add bufsize plugin

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>

* add docstring and comment

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>

* delete stdout messages when get an error

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>

* change to context.Background from TODO

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>

* define default bufsize as defaultBufSize constant

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>

* fix some comments

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>

* function name change: parse

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>

* function name change: parse

Signed-off-by: ykhr53 <yukihira.lab@gmail.com>
  • Loading branch information
ykhr53 authored and miekg committed Nov 10, 2019
1 parent 113783e commit e23a34a
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/dnsserver/zdirectives.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var Directives = []string{
"tls",
"reload",
"nsid",
"bufsize",
"root",
"bind",
"debug",
Expand Down
1 change: 1 addition & 0 deletions core/plugin/zplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
_ "github.com/coredns/coredns/plugin/autopath"
_ "github.com/coredns/coredns/plugin/azure"
_ "github.com/coredns/coredns/plugin/bind"
_ "github.com/coredns/coredns/plugin/bufsize"
_ "github.com/coredns/coredns/plugin/cache"
_ "github.com/coredns/coredns/plugin/cancel"
_ "github.com/coredns/coredns/plugin/chaos"
Expand Down
1 change: 1 addition & 0 deletions plugin.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ cancel:cancel
tls:tls
reload:reload
nsid:nsid
bufsize:bufsize
root:root
bind:bind
debug:debug
Expand Down
30 changes: 30 additions & 0 deletions plugin/bufsize/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# bufsize
## Name
*bufsize* - sizes EDNS0 buffer size to prevent IP fragmentation.

## Description
*bufsize* limits a requester's UDP payload size.
It prevents IP fragmentation so that to deal with DNS vulnerability.

## Syntax
```txt
bufsize [SIZE]
```

**[SIZE]** is an int value for setting the buffer size.
The default value is 512, and the value must be within 512 - 4096.
Only one argument is acceptable, and it covers both IPv4 and IPv6.

## Examples
```corefile
. {
bufsize 512
forward . 172.31.0.10
log
}
```

If you run a resolver on 172.31.0.10, the buffer size of incoming query on the resolver will be set to 512 bytes.

## Considerations
For now, if a client does not use EDNS, this plugin adds OPT RR.
31 changes: 31 additions & 0 deletions plugin/bufsize/bufsize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Package bufsize implements a plugin that modifies EDNS0 buffer size.
package bufsize

import (
"context"

"github.com/coredns/coredns/plugin"

"github.com/miekg/dns"
)

// Bufsize implements bufsize plugin.
type Bufsize struct {
Next plugin.Handler
Size int
}

// ServeDNS implements the plugin.Handler interface.
func (buf Bufsize) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
if option := r.IsEdns0(); option != nil {
option.SetUDPSize(uint16(buf.Size))
} else {
// If a client does not use EDNS, add it
r.SetEdns0(uint16(buf.Size), false)
}

return plugin.NextOrFailure(buf.Name(), buf.Next, ctx, w, r)
}

// Name implements the Handler interface.
func (buf Bufsize) Name() string { return "bufsize" }
72 changes: 72 additions & 0 deletions plugin/bufsize/bufsize_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package bufsize

import (
"context"
"testing"

"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/test"
"github.com/coredns/coredns/plugin/whoami"

"github.com/miekg/dns"
)

func TestBufsize(t *testing.T) {
em := Bufsize{
Size: 512,
}

tests := []struct {
next plugin.Handler
qname string
inputBufsize uint16
outgoingBufsize uint16
expectedErr error
}{
// This plugin is responsible for limiting outgoing query's bufize
{
next: whoami.Whoami{},
qname: ".",
inputBufsize: 1200,
outgoingBufsize: 512,
expectedErr: nil,
},
// If EDNS is not enabled, this plugin adds it
{
next: whoami.Whoami{},
qname: ".",
outgoingBufsize: 512,
expectedErr: nil,
},
}

for i, tc := range tests {
req := new(dns.Msg)
req.SetQuestion(dns.Fqdn(tc.qname), dns.TypeA)
req.Question[0].Qclass = dns.ClassINET
em.Next = tc.next

if tc.inputBufsize != 0 {
req.SetEdns0(tc.inputBufsize, false)
}

_, err := em.ServeDNS(context.Background(), &test.ResponseWriter{}, req)

if err != tc.expectedErr {
t.Errorf("Test %d: Expected error is %v, but got %v", i, tc.expectedErr, err)
}

if tc.outgoingBufsize != 0 {
for _, extra := range req.Extra {
if option, ok := extra.(*dns.OPT); ok {
b := option.UDPSize()
if b != tc.outgoingBufsize {
t.Errorf("Test %d: Expected outgoing bufsize is %d, but got %d", i, tc.outgoingBufsize, b)
}
} else {
t.Errorf("Test %d: Not found OPT RR.", i)
}
}
}
}
}
52 changes: 52 additions & 0 deletions plugin/bufsize/setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package bufsize

import (
"strconv"

"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"

"github.com/caddyserver/caddy"
)

func init() { plugin.Register("bufsize", setup) }

func setup(c *caddy.Controller) error {
bufsize, err := parse(c)
if err != nil {
return plugin.Error("bufsize", err)
}

dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
return Bufsize{Next: next, Size: bufsize}
})

return nil
}

func parse(c *caddy.Controller) (int, error) {
const defaultBufSize = 512
for c.Next() {
args := c.RemainingArgs()
switch len(args) {
case 0:
// Nothing specified; use 512 as default
return defaultBufSize, nil
case 1:
// Specified value is needed to verify
bufsize, err := strconv.Atoi(args[0])
if err != nil {
return -1, plugin.Error("bufsize", c.ArgErr())
}
// Follows RFC 6891
if bufsize < 512 || bufsize > 4096 {
return -1, plugin.Error("bufsize", c.ArgErr())
}
return bufsize, nil
default:
// Only 1 argument is acceptable
return -1, plugin.Error("bufsize", c.ArgErr())
}
}
return -1, plugin.Error("bufsize", c.ArgErr())
}
46 changes: 46 additions & 0 deletions plugin/bufsize/setup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package bufsize

import (
"strings"
"testing"

"github.com/caddyserver/caddy"
)

func TestSetupBufsize(t *testing.T) {
tests := []struct {
input string
shouldErr bool
expectedData int
expectedErrContent string // substring from the expected error. Empty for positive cases.
}{
{`bufsize`, false, 512, ""},
{`bufsize "1232"`, false, 1232, ""},
{`bufsize "5000"`, true, -1, "plugin"},
{`bufsize "512 512"`, true, -1, "plugin"},
{`bufsize "abc123"`, true, -1, "plugin"},
}

for i, test := range tests {
c := caddy.NewTestController("dns", test.input)
bufsize, err := parse(c)

if test.shouldErr && err == nil {
t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
}

if err != nil {
if !test.shouldErr {
t.Errorf("Test %d: Error found for input %s. Error: %v", i, test.input, err)
}

if !strings.Contains(err.Error(), test.expectedErrContent) {
t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
}
}

if !test.shouldErr && bufsize != test.expectedData {
t.Errorf("Test %d: Bufsize not correctly set for input %s. Expected: %d, actual: %d", i, test.input, test.expectedData, bufsize)
}
}
}

0 comments on commit e23a34a

Please sign in to comment.