mirror of
https://github.com/coredns/coredns.git
synced 2026-07-10 17:50:12 -04:00
plugin/transfer: configure notify source address (#8192)
Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>
This commit is contained in:
@@ -22,6 +22,7 @@ use this plugin.
|
|||||||
~~~
|
~~~
|
||||||
transfer [ZONE...] {
|
transfer [ZONE...] {
|
||||||
to ADDRESS...
|
to ADDRESS...
|
||||||
|
source ADDRESS
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
@@ -35,6 +36,10 @@ transfer [ZONE...] {
|
|||||||
an IP address and port e.g. `1.2.3.4`, `12:34::56`, `1.2.3.4:5300`, `[12:34::56]:5300`.
|
an IP address and port e.g. `1.2.3.4`, `12:34::56`, `1.2.3.4:5300`, `[12:34::56]:5300`.
|
||||||
`to` may be specified multiple times.
|
`to` may be specified multiple times.
|
||||||
|
|
||||||
|
* `source` **ADDRESS** is the local IP address to use when sending zone change
|
||||||
|
notifications to the configured `to` addresses. It does not change which
|
||||||
|
clients are allowed to request AXFR or IXFR transfers.
|
||||||
|
|
||||||
You can use the _acl_ plugin to further restrict hosts permitted to receive a zone transfer.
|
You can use the _acl_ plugin to further restrict hosts permitted to receive a zone transfer.
|
||||||
See example below.
|
See example below.
|
||||||
|
|
||||||
@@ -56,4 +61,15 @@ Use in conjunction with the _acl_ plugin to restrict access to subnet 10.1.0.0/1
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Each plugin that can use _transfer_ includes an example of use in their respective documentation.
|
Send NOTIFY messages from a specific local address.
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
transfer {
|
||||||
|
to 2001:db8::1
|
||||||
|
source 2001:db8::53
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Each plugin that can use _transfer_ includes an example of use in their respective documentation.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package transfer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/coredns/coredns/plugin/pkg/rcode"
|
"github.com/coredns/coredns/plugin/pkg/rcode"
|
||||||
|
|
||||||
@@ -16,13 +17,13 @@ func (t *Transfer) Notify(zone string) error {
|
|||||||
|
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetNotify(zone)
|
m.SetNotify(zone)
|
||||||
c := new(dns.Client)
|
|
||||||
|
|
||||||
x := longestMatch(t.xfrs, zone)
|
x := longestMatch(t.xfrs, zone)
|
||||||
if x == nil {
|
if x == nil {
|
||||||
// return without error if there is no matching zone
|
// return without error if there is no matching zone
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
c := notifyClient(x)
|
||||||
|
|
||||||
var err1 error
|
var err1 error
|
||||||
for _, t := range x.to {
|
for _, t := range x.to {
|
||||||
@@ -37,6 +38,14 @@ func (t *Transfer) Notify(zone string) error {
|
|||||||
return err1 // this only captures the last error
|
return err1 // this only captures the last error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func notifyClient(x *xfr) *dns.Client {
|
||||||
|
c := new(dns.Client)
|
||||||
|
if x != nil && x.source != nil {
|
||||||
|
c.Dialer = &net.Dialer{LocalAddr: &net.UDPAddr{IP: x.source}}
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func sendNotify(c *dns.Client, m *dns.Msg, s string) error {
|
func sendNotify(c *dns.Client, m *dns.Msg, s string) error {
|
||||||
var err error
|
var err error
|
||||||
var ret *dns.Msg
|
var ret *dns.Msg
|
||||||
|
|||||||
27
plugin/transfer/notify_test.go
Normal file
27
plugin/transfer/notify_test.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package transfer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNotifyClientSource(t *testing.T) {
|
||||||
|
c := notifyClient(&xfr{})
|
||||||
|
if c.Dialer != nil {
|
||||||
|
t.Fatalf("expected no dialer without a source address, got %#v", c.Dialer)
|
||||||
|
}
|
||||||
|
|
||||||
|
source := net.ParseIP("2001:db8::53")
|
||||||
|
c = notifyClient(&xfr{source: source})
|
||||||
|
if c.Dialer == nil {
|
||||||
|
t.Fatal("expected dialer for source address")
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, ok := c.Dialer.LocalAddr.(*net.UDPAddr)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected UDP local address, got %T", c.Dialer.LocalAddr)
|
||||||
|
}
|
||||||
|
if !addr.IP.Equal(source) {
|
||||||
|
t.Fatalf("expected source address %s, got %s", source, addr.IP)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package transfer
|
package transfer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/coredns/caddy"
|
"github.com/coredns/caddy"
|
||||||
"github.com/coredns/coredns/core/dnsserver"
|
"github.com/coredns/coredns/core/dnsserver"
|
||||||
"github.com/coredns/coredns/plugin"
|
"github.com/coredns/coredns/plugin"
|
||||||
@@ -68,6 +70,19 @@ func parseTransfer(c *caddy.Controller) (*Transfer, error) {
|
|||||||
}
|
}
|
||||||
x.to = append(x.to, normalized)
|
x.to = append(x.to, normalized)
|
||||||
}
|
}
|
||||||
|
case "source":
|
||||||
|
args := c.RemainingArgs()
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
if x.source != nil {
|
||||||
|
return nil, plugin.Error("transfer", c.Err("source already specified"))
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(args[0])
|
||||||
|
if ip == nil {
|
||||||
|
return nil, plugin.Error("transfer", c.Errf("invalid source address %q", args[0]))
|
||||||
|
}
|
||||||
|
x.source = ip
|
||||||
default:
|
default:
|
||||||
return nil, plugin.Error("transfer", c.Errf("unknown property %q", c.Val()))
|
return nil, plugin.Error("transfer", c.Errf("unknown property %q", c.Val()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package transfer
|
package transfer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coredns/caddy"
|
"github.com/coredns/caddy"
|
||||||
@@ -15,6 +16,7 @@ func TestParse(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{`transfer example.net example.org {
|
{`transfer example.net example.org {
|
||||||
to 1.2.3.4 5.6.7.8:1053 [1::2]:34 [fe80::1%eth0]:53
|
to 1.2.3.4 5.6.7.8:1053 [1::2]:34 [fe80::1%eth0]:53
|
||||||
|
source 2001:db8::53
|
||||||
}
|
}
|
||||||
transfer example.com example.edu {
|
transfer example.com example.edu {
|
||||||
to * 1.2.3.4
|
to * 1.2.3.4
|
||||||
@@ -23,8 +25,9 @@ func TestParse(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
&Transfer{
|
&Transfer{
|
||||||
xfrs: []*xfr{{
|
xfrs: []*xfr{{
|
||||||
Zones: []string{"example.net.", "example.org."},
|
Zones: []string{"example.net.", "example.org."},
|
||||||
to: []string{"1.2.3.4:53", "5.6.7.8:1053", "[1::2]:34", "[fe80::1%eth0]:53"},
|
to: []string{"1.2.3.4:53", "5.6.7.8:1053", "[1::2]:34", "[fe80::1%eth0]:53"},
|
||||||
|
source: net.ParseIP("2001:db8::53"),
|
||||||
}, {
|
}, {
|
||||||
Zones: []string{"example.com.", "example.edu."},
|
Zones: []string{"example.com.", "example.edu."},
|
||||||
to: []string{"*", "1.2.3.4:53"},
|
to: []string{"*", "1.2.3.4:53"},
|
||||||
@@ -45,6 +48,23 @@ func TestParse(t *testing.T) {
|
|||||||
true,
|
true,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
{`transfer example.net example.org {
|
||||||
|
to 1.2.3.4
|
||||||
|
source example.org
|
||||||
|
}`,
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{`transfer example.net example.org {
|
||||||
|
to 1.2.3.4
|
||||||
|
source 192.0.2.53
|
||||||
|
source 2001:db8::53
|
||||||
|
}`,
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`
|
`
|
||||||
transfer example.com example.edu {
|
transfer example.com example.edu {
|
||||||
@@ -109,10 +129,20 @@ func TestParse(t *testing.T) {
|
|||||||
t.Errorf("Test %d expected %v in 'to', got %v", i, tc.exp.xfrs[j].to[k], to)
|
t.Errorf("Test %d expected %v in 'to', got %v", i, tc.exp.xfrs[j].to[k], to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !sameIP(tc.exp.xfrs[j].source, x.source) {
|
||||||
|
t.Errorf("Test %d expected source %v, got %v", i, tc.exp.xfrs[j].source, x.source)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sameIP(a, b net.IP) bool {
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return a == nil && b == nil
|
||||||
|
}
|
||||||
|
return a.Equal(b)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSetup(t *testing.T) {
|
func TestSetup(t *testing.T) {
|
||||||
c := caddy.NewTestController("dns", "transfer")
|
c := caddy.NewTestController("dns", "transfer")
|
||||||
if err := setup(c); err == nil {
|
if err := setup(c); err == nil {
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ type Transfer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type xfr struct {
|
type xfr struct {
|
||||||
Zones []string
|
Zones []string
|
||||||
to []string
|
to []string
|
||||||
|
source net.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transferer may be implemented by plugins to enable zone transfers
|
// Transferer may be implemented by plugins to enable zone transfers
|
||||||
|
|||||||
Reference in New Issue
Block a user