middleware/reverse: random updates (#516)
* middleware/reverse: random updates Make the documentation somewhat shorter (and hopefully clearer in the process). Also to be on-par with the *auto* middleware, start counting the referenced zones from 1 (instead of 0). Some variable cleanups and use the NextOrFailure in the ServeDNS function. * More TODOs
This commit is contained in:
parent
87a39a6353
commit
3e196a6d57
5 changed files with 123 additions and 154 deletions
|
@ -1,42 +1,43 @@
|
||||||
# reverse
|
# reverse
|
||||||
|
|
||||||
The *reverse* middleware allows CoreDNS to respond dynamic to an PTR request and the related A/AAAA request.
|
The *reverse* middleware allows CoreDNS to respond dynamicly to an PTR request and the related A/AAAA request.
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
reverse NETWORK.. {
|
reverse NETWORK... {
|
||||||
hostname TEMPLATE
|
hostname TEMPLATE
|
||||||
[ttl TTL]
|
[ttl TTL]
|
||||||
[fallthrough]
|
[fallthrough]
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
* **NETWORK** one or more CIDR formatted networks to respond on.
|
* **NETWORK** one or more CIDR formatted networks to respond on.
|
||||||
* `hostname` inject the ip and zone to an template for the hostname. Defaults to "ip-{ip}.{zone[0]}". See below for template.
|
* `hostname` inject the IP and zone to an template for the hostname. Defaults to "ip-{IP}.{zone[1]}". See below for template.
|
||||||
* `ttl` defaults to 60
|
* `ttl` defaults to 60
|
||||||
* `fallthrough` If zone matches and no record can be generated, pass request to the next middleware.
|
* `fallthrough` If zone matches and no record can be generated, pass request to the next middleware.
|
||||||
|
|
||||||
### Template Syntax
|
### Template Syntax
|
||||||
The template for the hostname is used for generating the PTR for an reverse lookup and matching the forward lookup back to an ip.
|
|
||||||
|
The template for the hostname is used for generating the PTR for an reverse lookup and matching the
|
||||||
|
forward lookup back to an IP.
|
||||||
|
|
||||||
#### `{ip}`
|
#### `{ip}`
|
||||||
This symbol is **required** to work.
|
|
||||||
V4 network replaces the "." with an "-". 10.1.1.1 results in "10-1-1-1"
|
The `{ip}` symbol is **required** to make reverse work.
|
||||||
V6 network removes the ":" and fills the zeros. "ffff::ffff" results in "ffff000000000000000000000000ffff"
|
For IPv4 lookups the "." is replaced with an "-", i.e.: 10.1.1.1 results in "10-1-1-1"
|
||||||
|
With IPv6 lookups the ":" is removed, and any zero ranged are expanded, i.e.:
|
||||||
|
"ffff::ffff" results in "ffff000000000000000000000000ffff"
|
||||||
|
|
||||||
#### `{zone[i]}`
|
#### `{zone[i]}`
|
||||||
This symbol is **optional** to use and can be replaced by a fix zone string.
|
|
||||||
The zone will be matched by the configured listener on the server block key.
|
|
||||||
`i` needs to be replaced to the index of the configured listener zones, starting with 0.
|
|
||||||
|
|
||||||
`arpa.:53 domain.com.:8053` will resolve `zone{0}` to `arpa.` and `zone{1}` to `domain.com.`
|
The `{zone[i]}` symbol is **optional** and can be replaced by a fixed (zone) string.
|
||||||
|
The zone will be matched by the zones listed in *this* configuration stanza.
|
||||||
|
`i` needs to be replaced to the index of the configured listener zones, starting with 1.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
~~~
|
~~~ txt
|
||||||
# Serve on port 53
|
arpa compute.internal {
|
||||||
# match arpa. and compute.internal. to resolv reverse and forward lookup
|
|
||||||
.arpa.:53 compute.internal.:53 {
|
|
||||||
# proxy unmatched requests
|
# proxy unmatched requests
|
||||||
proxy . 8.8.8.8
|
proxy . 8.8.8.8
|
||||||
|
|
||||||
|
@ -48,32 +49,25 @@ The zone will be matched by the configured listener on the server block key.
|
||||||
# AAAA ip-fd010000000000000000000000000001.compute.internal. 3600 fd01::1
|
# AAAA ip-fd010000000000000000000000000001.compute.internal. 3600 fd01::1
|
||||||
reverse 10.32.0.0/16 fd01::/16 {
|
reverse 10.32.0.0/16 fd01::/16 {
|
||||||
# template of the ip injection to hostname, zone resolved to compute.internal.
|
# template of the ip injection to hostname, zone resolved to compute.internal.
|
||||||
hostname ip-{ip}.{zone[1]}
|
hostname ip-{ip}.{zone[2]}
|
||||||
|
|
||||||
# set time-to-live of the RR
|
|
||||||
ttl 3600
|
ttl 3600
|
||||||
|
|
||||||
# forward unanswered or unmatched requests to proxy
|
# Forward unanswered or unmatched requests to proxy # without this flag, requesting A/AAAA
|
||||||
# without this flag, requesting A/AAAA records on compute.internal. will end here
|
records on compute.internal. will end here.
|
||||||
fallthrough
|
fallthrough
|
||||||
}
|
}
|
||||||
|
|
||||||
# cache with ttl timeout
|
|
||||||
cache
|
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|
||||||
~~~
|
~~~ txt
|
||||||
# Serve on port 53
|
32.10.in-addr.arpa.arpa arpa.company.org {
|
||||||
# listen only on the specific network
|
|
||||||
32.10.in-addr.arpa.arpa.:53 arpa.company.org.:53 {
|
|
||||||
|
|
||||||
reverse 10.32.0.0/16 {
|
reverse 10.32.0.0/16 {
|
||||||
# template of the ip injection to hostname, zone resolved to arpa.company.org.
|
# template of the ip injection to hostname, zone resolved to arpa.company.org.
|
||||||
hostname "ip-{ip}.v4.{zone[1]}"
|
hostname "ip-{ip}.v4.{zone[2]}"
|
||||||
|
|
||||||
# set time-to-live of the RR
|
|
||||||
ttl 3600
|
ttl 3600
|
||||||
|
|
||||||
# fallthrough is not required, v4.arpa.company.org. will be only answered here
|
# fallthrough is not required, v4.arpa.company.org. will be only answered here
|
||||||
|
@ -84,14 +78,7 @@ The zone will be matched by the configured listener on the server block key.
|
||||||
# its also possible to set fix domain suffix
|
# its also possible to set fix domain suffix
|
||||||
hostname ip-{ip}.fix.arpa.company.org.
|
hostname ip-{ip}.fix.arpa.company.org.
|
||||||
|
|
||||||
# set time-to-live of the RR
|
|
||||||
ttl 3600
|
ttl 3600
|
||||||
}
|
}
|
||||||
|
|
||||||
# cache with ttl timeout
|
|
||||||
cache
|
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,47 +1,46 @@
|
||||||
package reverse
|
package reverse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"bytes"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type network struct {
|
type network struct {
|
||||||
IPnet *net.IPNet
|
IPnet *net.IPNet
|
||||||
Zone string // forward lookup zone
|
Zone string // forward lookup zone
|
||||||
Template string
|
Template string
|
||||||
TTL uint32
|
TTL uint32
|
||||||
RegexMatchIP *regexp.Regexp
|
RegexMatchIP *regexp.Regexp
|
||||||
Fallthrough bool
|
Fallthrough bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: we might want to get rid of these regexes.
|
||||||
const hexDigit = "0123456789abcdef"
|
const hexDigit = "0123456789abcdef"
|
||||||
const templateNameIP = "{ip}"
|
const templateNameIP = "{ip}"
|
||||||
const regexMatchV4 = "((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\-){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"
|
const regexMatchV4 = "((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\-){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"
|
||||||
const regexMatchV6 = "([0-9a-fA-F]{32})"
|
const regexMatchV6 = "([0-9a-fA-F]{32})"
|
||||||
|
|
||||||
// For forward lookup
|
// hostnameToIP converts the hostname back to an ip, based on the template
|
||||||
// converts the hostname back to an ip, based on the template
|
// returns nil if there is no IP found.
|
||||||
// returns nil if there is no ip found
|
|
||||||
func (network *network) hostnameToIP(rname string) net.IP {
|
func (network *network) hostnameToIP(rname string) net.IP {
|
||||||
var matchedIP net.IP
|
var matchedIP net.IP
|
||||||
|
|
||||||
// use precompiled regex by setup
|
|
||||||
match := network.RegexMatchIP.FindStringSubmatch(rname)
|
match := network.RegexMatchIP.FindStringSubmatch(rname)
|
||||||
// regex did not matched
|
if len(match) != 2 {
|
||||||
if (len(match) != 2) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if network.IPnet.IP.To4() != nil {
|
if network.IPnet.IP.To4() != nil {
|
||||||
matchedIP = net.ParseIP(strings.Replace(match[1], "-", ".", 4))
|
matchedIP = net.ParseIP(strings.Replace(match[1], "-", ".", 4))
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: can probably just allocate a []byte and use that.
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
// convert back to an valid ipv6 string with colons
|
// convert back to an valid ipv6 string with colons
|
||||||
for i := 0; i < 8 * 4; i += 4 {
|
for i := 0; i < 8*4; i += 4 {
|
||||||
buf.WriteString(match[1][i:i + 4])
|
buf.WriteString(match[1][i : i+4])
|
||||||
if (i < 28) {
|
if i < 28 {
|
||||||
buf.WriteString(":")
|
buf.WriteString(":")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,13 +55,9 @@ func (network *network) hostnameToIP(rname string) net.IP {
|
||||||
return matchedIP
|
return matchedIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// For reverse lookup
|
// ipToHostname converts an IP to an DNS compatible hostname and injects it into the template.domain.
|
||||||
// Converts an Ip to an dns compatible hostname and injects it into the template.domain
|
func (network *network) ipToHostname(ip net.IP) (name string) {
|
||||||
func (network *network) ipToHostname(ip net.IP) string {
|
if ipv4 := ip.To4(); ipv4 != nil {
|
||||||
var name string
|
|
||||||
|
|
||||||
ipv4 := ip.To4()
|
|
||||||
if ipv4 != nil {
|
|
||||||
// replace . to -
|
// replace . to -
|
||||||
name = uitoa(ipv4[0]) + "-" +
|
name = uitoa(ipv4[0]) + "-" +
|
||||||
uitoa(ipv4[1]) + "-" +
|
uitoa(ipv4[1]) + "-" +
|
||||||
|
@ -71,11 +66,11 @@ func (network *network) ipToHostname(ip net.IP) string {
|
||||||
} else {
|
} else {
|
||||||
// assume v6
|
// assume v6
|
||||||
// ensure zeros are present in string
|
// ensure zeros are present in string
|
||||||
buf := make([]byte, 0, len(ip) * 4)
|
buf := make([]byte, 0, len(ip)*4)
|
||||||
for i := 0; i < len(ip); i++ {
|
for i := 0; i < len(ip); i++ {
|
||||||
v := ip[i]
|
v := ip[i]
|
||||||
buf = append(buf, hexDigit[v >> 4])
|
buf = append(buf, hexDigit[v>>4])
|
||||||
buf = append(buf, hexDigit[v & 0xF])
|
buf = append(buf, hexDigit[v&0xF])
|
||||||
}
|
}
|
||||||
name = string(buf)
|
name = string(buf)
|
||||||
}
|
}
|
||||||
|
@ -93,7 +88,7 @@ func uitoa(val uint8) string {
|
||||||
i := len(buf) - 1
|
i := len(buf) - 1
|
||||||
for val >= 10 {
|
for val >= 10 {
|
||||||
q := val / 10
|
q := val / 10
|
||||||
buf[i] = byte('0' + val - q * 10)
|
buf[i] = byte('0' + val - q*10)
|
||||||
i--
|
i--
|
||||||
val = q
|
val = q
|
||||||
}
|
}
|
||||||
|
@ -104,20 +99,12 @@ func uitoa(val uint8) string {
|
||||||
|
|
||||||
type networks []network
|
type networks []network
|
||||||
|
|
||||||
// implements the sort interface
|
func (n networks) Len() int { return len(n) }
|
||||||
func (slice networks) Len() int {
|
func (n networks) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||||
return len(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
// implements the sort interface
|
|
||||||
// cidr closer to the ip wins (by netmask)
|
// cidr closer to the ip wins (by netmask)
|
||||||
func (slice networks) Less(i, j int) bool {
|
func (n networks) Less(i, j int) bool {
|
||||||
isize, _ := slice[i].IPnet.Mask.Size()
|
isize, _ := n[i].IPnet.Mask.Size()
|
||||||
jsize, _ := slice[j].IPnet.Mask.Size()
|
jsize, _ := n[j].IPnet.Mask.Size()
|
||||||
return isize > jsize
|
return isize > jsize
|
||||||
}
|
}
|
||||||
|
|
||||||
// implements the sort interface
|
|
||||||
func (slice networks) Swap(i, j int) {
|
|
||||||
slice[i], slice[j] = slice[j], slice[i]
|
|
||||||
}
|
|
|
@ -3,31 +3,33 @@ package reverse
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/miekg/coredns/request"
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
||||||
|
"github.com/miekg/coredns/request"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reverse provides dynamic reverse dns and the related forward rr
|
// Reverse provides dynamic reverse DNS and the related forward RR.
|
||||||
type Reverse struct {
|
type Reverse struct {
|
||||||
Next middleware.Handler
|
Next middleware.Handler
|
||||||
Networks networks
|
Networks networks
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeDNS implements the middleware.Handler interface.
|
// ServeDNS implements the middleware.Handler interface.
|
||||||
func (reverse Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
func (re Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||||
var rr dns.RR
|
var (
|
||||||
nextHandler := true
|
rr dns.RR
|
||||||
|
fallThrough bool
|
||||||
|
)
|
||||||
|
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetReply(r)
|
m.SetReply(r)
|
||||||
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
|
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
|
||||||
|
|
||||||
switch state.QType(){
|
switch state.QType() {
|
||||||
case dns.TypePTR:
|
case dns.TypePTR:
|
||||||
address := dnsutil.ExtractAddressFromReverse(state.Name())
|
address := dnsutil.ExtractAddressFromReverse(state.Name())
|
||||||
|
|
||||||
|
@ -38,11 +40,11 @@ func (reverse Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
||||||
|
|
||||||
ip := net.ParseIP(address)
|
ip := net.ParseIP(address)
|
||||||
// loop through the configured networks
|
// loop through the configured networks
|
||||||
for _, n := range reverse.Networks {
|
for _, n := range re.Networks {
|
||||||
if (n.IPnet.Contains(ip)) {
|
if n.IPnet.Contains(ip) {
|
||||||
nextHandler = n.Fallthrough
|
fallThrough = n.Fallthrough
|
||||||
rr = &dns.PTR{
|
rr = &dns.PTR{
|
||||||
Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: n.TTL},
|
Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: n.TTL},
|
||||||
Ptr: n.ipToHostname(ip),
|
Ptr: n.ipToHostname(ip),
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -50,9 +52,9 @@ func (reverse Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
||||||
}
|
}
|
||||||
|
|
||||||
case dns.TypeA:
|
case dns.TypeA:
|
||||||
for _, n := range reverse.Networks {
|
for _, n := range re.Networks {
|
||||||
if dns.IsSubDomain(n.Zone, state.Name()) {
|
if dns.IsSubDomain(n.Zone, state.Name()) {
|
||||||
nextHandler = n.Fallthrough
|
fallThrough = n.Fallthrough
|
||||||
|
|
||||||
// skip if requesting an v4 address and network is not v4
|
// skip if requesting an v4 address and network is not v4
|
||||||
if n.IPnet.IP.To4() == nil {
|
if n.IPnet.IP.To4() == nil {
|
||||||
|
@ -62,8 +64,8 @@ func (reverse Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
||||||
result := n.hostnameToIP(state.Name())
|
result := n.hostnameToIP(state.Name())
|
||||||
if result != nil {
|
if result != nil {
|
||||||
rr = &dns.A{
|
rr = &dns.A{
|
||||||
Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: n.TTL},
|
Hdr: dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: n.TTL},
|
||||||
A: result,
|
A: result,
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -71,9 +73,9 @@ func (reverse Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
||||||
}
|
}
|
||||||
|
|
||||||
case dns.TypeAAAA:
|
case dns.TypeAAAA:
|
||||||
for _, n := range reverse.Networks {
|
for _, n := range re.Networks {
|
||||||
if dns.IsSubDomain(n.Zone, state.Name()) {
|
if dns.IsSubDomain(n.Zone, state.Name()) {
|
||||||
nextHandler = n.Fallthrough
|
fallThrough = n.Fallthrough
|
||||||
|
|
||||||
// Do not use To16 which tries to make v4 in v6
|
// Do not use To16 which tries to make v4 in v6
|
||||||
if n.IPnet.IP.To4() != nil {
|
if n.IPnet.IP.To4() != nil {
|
||||||
|
@ -93,13 +95,8 @@ func (reverse Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if rr == nil {
|
if rr == nil && !fallThrough {
|
||||||
if reverse.Next == nil || !nextHandler {
|
return middleware.NextOrFailure(re.Name(), re.Next, ctx, w, r)
|
||||||
// could not resolv
|
|
||||||
w.WriteMsg(m)
|
|
||||||
return dns.RcodeNameError, nil
|
|
||||||
}
|
|
||||||
return reverse.Next.ServeDNS(ctx, w, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Answer = append(m.Answer, rr)
|
m.Answer = append(m.Answer, rr)
|
||||||
|
@ -109,6 +106,4 @@ func (reverse Reverse) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name implements the Handler interface.
|
// Name implements the Handler interface.
|
||||||
func (reverse Reverse) Name() string {
|
func (re Reverse) Name() string { return "reverse" }
|
||||||
return "reverse"
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ package reverse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"strconv"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/miekg/coredns/core/dnsserver"
|
"github.com/miekg/coredns/core/dnsserver"
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
|
@ -27,7 +27,7 @@ func setupReverse(c *caddy.Controller) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
|
dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
|
||||||
return Reverse{Next: next, Networks:networks}
|
return Reverse{Next: next, Networks: networks}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -50,13 +50,13 @@ func reverseParse(c *caddy.Controller) (networks, error) {
|
||||||
var cidrs []*net.IPNet
|
var cidrs []*net.IPNet
|
||||||
|
|
||||||
// parse all networks
|
// parse all networks
|
||||||
for _, cidr := range c.RemainingArgs() {
|
for _, cidr := range c.RemainingArgs() {
|
||||||
if cidr == "{" {
|
if cidr == "{" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, ipnet, err := net.ParseCIDR(cidr)
|
_, ipnet, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, c.Errf("%v needs to be an CIDR formatted Network\n", cidr)
|
return nil, c.Errf("network needs to be CIDR formatted: %q\n", cidr)
|
||||||
}
|
}
|
||||||
cidrs = append(cidrs, ipnet)
|
cidrs = append(cidrs, ipnet)
|
||||||
}
|
}
|
||||||
|
@ -66,9 +66,9 @@ func reverseParse(c *caddy.Controller) (networks, error) {
|
||||||
|
|
||||||
// set defaults
|
// set defaults
|
||||||
var (
|
var (
|
||||||
template = "ip-" + templateNameIP + ".{zone[0]}"
|
template = "ip-" + templateNameIP + ".{zone[1]}"
|
||||||
ttl = 60
|
ttl = 60
|
||||||
fall = false
|
fall = false
|
||||||
)
|
)
|
||||||
for c.NextBlock() {
|
for c.NextBlock() {
|
||||||
switch c.Val() {
|
switch c.Val() {
|
||||||
|
@ -98,7 +98,9 @@ func reverseParse(c *caddy.Controller) (networks, error) {
|
||||||
// prepare template
|
// prepare template
|
||||||
// replace {zone[index]} by the listen zone/domain of this config block
|
// replace {zone[index]} by the listen zone/domain of this config block
|
||||||
for i, zone := range zones {
|
for i, zone := range zones {
|
||||||
template = strings.Replace(template, "{zone[" + string(i + 48) + "]}", zone, 1)
|
// TODO: we should be smarter about actually replacing this. This works, but silently allows "zone[-1]"
|
||||||
|
// for instance.
|
||||||
|
template = strings.Replace(template, "{zone["+strconv.Itoa(i+1)+"]}", zone, 1)
|
||||||
}
|
}
|
||||||
if !strings.HasSuffix(template, ".") {
|
if !strings.HasSuffix(template, ".") {
|
||||||
template += "."
|
template += "."
|
||||||
|
@ -107,7 +109,7 @@ func reverseParse(c *caddy.Controller) (networks, error) {
|
||||||
// extract zone from template
|
// extract zone from template
|
||||||
templateZone := strings.SplitAfterN(template, ".", 2)
|
templateZone := strings.SplitAfterN(template, ".", 2)
|
||||||
if len(templateZone) != 2 || templateZone[1] == "" {
|
if len(templateZone) != 2 || templateZone[1] == "" {
|
||||||
return nil, c.Errf("Cannot find domain in template '%v'", template)
|
return nil, c.Errf("cannot find domain in template '%v'", template)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create for each configured network in this stanza
|
// Create for each configured network in this stanza
|
||||||
|
@ -122,19 +124,18 @@ func reverseParse(c *caddy.Controller) (networks, error) {
|
||||||
regexp.QuoteMeta(template), // escape dots
|
regexp.QuoteMeta(template), // escape dots
|
||||||
regexp.QuoteMeta(templateNameIP),
|
regexp.QuoteMeta(templateNameIP),
|
||||||
regexIP,
|
regexIP,
|
||||||
1, ) + "$")
|
1) + "$")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// invalid regex
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
networks = append(networks, network{
|
networks = append(networks, network{
|
||||||
IPnet: ipnet,
|
IPnet: ipnet,
|
||||||
Zone: templateZone[1],
|
Zone: templateZone[1],
|
||||||
Template: template,
|
Template: template,
|
||||||
RegexMatchIP: regex,
|
RegexMatchIP: regex,
|
||||||
TTL: uint32(ttl),
|
TTL: uint32(ttl),
|
||||||
Fallthrough: fall,
|
Fallthrough: fall,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package reverse
|
package reverse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,7 @@ func TestSetupParse(t *testing.T) {
|
||||||
regexIpv6dynamic, _ := regexp.Compile("^dynamic-" + regexMatchV6 + "-intern\\.dynamic\\.domain\\.com\\.$")
|
regexIpv6dynamic, _ := regexp.Compile("^dynamic-" + regexMatchV6 + "-intern\\.dynamic\\.domain\\.com\\.$")
|
||||||
regexIpv4vpndynamic, _ := regexp.Compile("^dynamic-" + regexMatchV4 + "-vpn\\.dynamic\\.domain\\.com\\.$")
|
regexIpv4vpndynamic, _ := regexp.Compile("^dynamic-" + regexMatchV4 + "-vpn\\.dynamic\\.domain\\.com\\.$")
|
||||||
|
|
||||||
serverBlockKeys := []string{"domain.com.:8053", "dynamic.domain.com.:8053" }
|
serverBlockKeys := []string{"domain.com.:8053", "dynamic.domain.com.:8053"}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
inputFileRules string
|
inputFileRules string
|
||||||
|
@ -31,12 +31,12 @@ func TestSetupParse(t *testing.T) {
|
||||||
`reverse fd01::/64`,
|
`reverse fd01::/64`,
|
||||||
false,
|
false,
|
||||||
networks{network{
|
networks{network{
|
||||||
IPnet: net6,
|
IPnet: net6,
|
||||||
Template: "ip-{ip}.domain.com.",
|
Template: "ip-{ip}.domain.com.",
|
||||||
Zone: "domain.com.",
|
Zone: "domain.com.",
|
||||||
TTL: 60,
|
TTL: 60,
|
||||||
RegexMatchIP: regexIP6,
|
RegexMatchIP: regexIP6,
|
||||||
Fallthrough: false,
|
Fallthrough: false,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -98,52 +98,52 @@ func TestSetupParse(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`reverse fd01::/64 {
|
`reverse fd01::/64 {
|
||||||
hostname dynamic-{ip}-intern.{zone[1]}
|
hostname dynamic-{ip}-intern.{zone[2]}
|
||||||
ttl 50
|
ttl 50
|
||||||
}
|
}
|
||||||
reverse 10.1.1.0/24 {
|
reverse 10.1.1.0/24 {
|
||||||
hostname dynamic-{ip}-vpn.{zone[1]}
|
hostname dynamic-{ip}-vpn.{zone[2]}
|
||||||
fallthrough
|
fallthrough
|
||||||
}`,
|
}`,
|
||||||
false,
|
false,
|
||||||
networks{network{
|
networks{network{
|
||||||
IPnet: net6,
|
IPnet: net6,
|
||||||
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
||||||
Zone: "dynamic.domain.com.",
|
Zone: "dynamic.domain.com.",
|
||||||
TTL: 50,
|
TTL: 50,
|
||||||
RegexMatchIP:regexIpv6dynamic,
|
RegexMatchIP: regexIpv6dynamic,
|
||||||
Fallthrough: false,
|
Fallthrough: false,
|
||||||
}, network{
|
}, network{
|
||||||
IPnet: net4,
|
IPnet: net4,
|
||||||
Template: "dynamic-{ip}-vpn.dynamic.domain.com.",
|
Template: "dynamic-{ip}-vpn.dynamic.domain.com.",
|
||||||
Zone: "dynamic.domain.com.",
|
Zone: "dynamic.domain.com.",
|
||||||
TTL: 60,
|
TTL: 60,
|
||||||
RegexMatchIP: regexIpv4vpndynamic,
|
RegexMatchIP: regexIpv4vpndynamic,
|
||||||
Fallthrough:true,
|
Fallthrough: true,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// multiple networks in one stanza
|
// multiple networks in one stanza
|
||||||
`reverse fd01::/64 10.1.1.0/24 {
|
`reverse fd01::/64 10.1.1.0/24 {
|
||||||
hostname dynamic-{ip}-intern.{zone[1]}
|
hostname dynamic-{ip}-intern.{zone[2]}
|
||||||
ttl 50
|
ttl 50
|
||||||
fallthrough
|
fallthrough
|
||||||
}`,
|
}`,
|
||||||
false,
|
false,
|
||||||
networks{network{
|
networks{network{
|
||||||
IPnet: net6,
|
IPnet: net6,
|
||||||
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
||||||
Zone: "dynamic.domain.com.",
|
Zone: "dynamic.domain.com.",
|
||||||
TTL: 50,
|
TTL: 50,
|
||||||
RegexMatchIP:regexIpv6dynamic,
|
RegexMatchIP: regexIpv6dynamic,
|
||||||
Fallthrough: true,
|
Fallthrough: true,
|
||||||
}, network{
|
}, network{
|
||||||
IPnet: net4,
|
IPnet: net4,
|
||||||
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
||||||
Zone: "dynamic.domain.com.",
|
Zone: "dynamic.domain.com.",
|
||||||
TTL: 50,
|
TTL: 50,
|
||||||
RegexMatchIP: regexIpv4dynamic,
|
RegexMatchIP: regexIpv4dynamic,
|
||||||
Fallthrough: true,
|
Fallthrough: true,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -155,15 +155,14 @@ func TestSetupParse(t *testing.T) {
|
||||||
}`,
|
}`,
|
||||||
false,
|
false,
|
||||||
networks{network{
|
networks{network{
|
||||||
IPnet: net6,
|
IPnet: net6,
|
||||||
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
Template: "dynamic-{ip}-intern.dynamic.domain.com.",
|
||||||
Zone: "dynamic.domain.com.",
|
Zone: "dynamic.domain.com.",
|
||||||
TTL: 300,
|
TTL: 300,
|
||||||
RegexMatchIP:regexIpv6dynamic,
|
RegexMatchIP: regexIpv6dynamic,
|
||||||
Fallthrough: true,
|
Fallthrough: true,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
c := caddy.NewTestController("dns", test.inputFileRules)
|
c := caddy.NewTestController("dns", test.inputFileRules)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue