diff options
Diffstat (limited to 'plugin/reverse/setup.go')
-rw-r--r-- | plugin/reverse/setup.go | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/plugin/reverse/setup.go b/plugin/reverse/setup.go new file mode 100644 index 000000000..26e21eea9 --- /dev/null +++ b/plugin/reverse/setup.go @@ -0,0 +1,147 @@ +package reverse + +import ( + "net" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/plugin" + + "github.com/mholt/caddy" +) + +func init() { + caddy.RegisterPlugin("reverse", caddy.Plugin{ + ServerType: "dns", + Action: setupReverse, + }) +} + +func setupReverse(c *caddy.Controller) error { + networks, fallThrough, err := reverseParse(c) + if err != nil { + return plugin.Error("reverse", err) + } + + dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { + return Reverse{Next: next, Networks: networks, Fallthrough: fallThrough} + }) + + return nil +} + +func reverseParse(c *caddy.Controller) (nets networks, fall bool, err error) { + zones := make([]string, len(c.ServerBlockKeys)) + wildcard := false + + // We copy from the serverblock, these contains Hosts. + for i, str := range c.ServerBlockKeys { + zones[i] = plugin.Host(str).Normalize() + } + + for c.Next() { + var cidrs []*net.IPNet + + // parse all networks + for _, cidr := range c.RemainingArgs() { + if cidr == "{" { + break + } + _, ipnet, err := net.ParseCIDR(cidr) + if err != nil { + return nil, false, c.Errf("network needs to be CIDR formatted: %q\n", cidr) + } + cidrs = append(cidrs, ipnet) + } + if len(cidrs) == 0 { + return nil, false, c.ArgErr() + } + + // set defaults + var ( + template = "ip-" + templateNameIP + ".{zone[1]}" + ttl = 60 + ) + for c.NextBlock() { + switch c.Val() { + case "hostname": + if !c.NextArg() { + return nil, false, c.ArgErr() + } + template = c.Val() + + case "ttl": + if !c.NextArg() { + return nil, false, c.ArgErr() + } + ttl, err = strconv.Atoi(c.Val()) + if err != nil { + return nil, false, err + } + + case "wildcard": + wildcard = true + + case "fallthrough": + fall = true + + default: + return nil, false, c.ArgErr() + } + } + + // prepare template + // replace {zone[index]} by the listen zone/domain of this config block + for i, zone := range zones { + // 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, ".") { + template += "." + } + + // extract zone from template + templateZone := strings.SplitAfterN(template, ".", 2) + if len(templateZone) != 2 || templateZone[1] == "" { + return nil, false, c.Errf("cannot find domain in template '%v'", template) + } + + // Create for each configured network in this stanza + for _, ipnet := range cidrs { + // precompile regex for hostname to ip matching + regexIP := regexMatchV4 + if ipnet.IP.To4() == nil { + regexIP = regexMatchV6 + } + prefix := "^" + if wildcard { + prefix += ".*" + } + regex, err := regexp.Compile( + prefix + strings.Replace( // inject ip regex into template + regexp.QuoteMeta(template), // escape dots + regexp.QuoteMeta(templateNameIP), + regexIP, + 1) + "$") + if err != nil { + return nil, false, err + } + + nets = append(nets, network{ + IPnet: ipnet, + Zone: templateZone[1], + Template: template, + RegexMatchIP: regex, + TTL: uint32(ttl), + }) + } + } + + // sort by cidr + sort.Sort(nets) + return nets, fall, nil +} |