Commit 3654361b authored by Miek Gieben's avatar Miek Gieben Committed by GitHub

core: small cleanup (#877)

Add some docs about normalize.Host and normalize.Name. They are used
correctly in the middleware even though they are somewhat confusing,
esp when you copy from ServerBlockKeys in your middleware.
parent 28447234
...@@ -81,91 +81,88 @@ func autoParse(c *caddy.Controller) (Auto, error) { ...@@ -81,91 +81,88 @@ func autoParse(c *caddy.Controller) (Auto, error) {
config := dnsserver.GetConfig(c) config := dnsserver.GetConfig(c)
for c.Next() { for c.Next() {
if c.Val() == "auto" { // auto [ZONES...]
// auto [ZONES...] a.Zones.origins = make([]string, len(c.ServerBlockKeys))
a.Zones.origins = make([]string, len(c.ServerBlockKeys)) copy(a.Zones.origins, c.ServerBlockKeys)
copy(a.Zones.origins, c.ServerBlockKeys)
args := c.RemainingArgs()
if len(args) > 0 {
a.Zones.origins = args
}
for i := range a.Zones.origins {
a.Zones.origins[i] = middleware.Host(a.Zones.origins[i]).Normalize()
}
for c.NextBlock() { args := c.RemainingArgs()
switch c.Val() { if len(args) > 0 {
case "directory": // directory DIR [REGEXP [TEMPLATE] [DURATION]] a.Zones.origins = args
if !c.NextArg() { }
return a, c.ArgErr() for i := range a.Zones.origins {
} a.Zones.origins[i] = middleware.Host(a.Zones.origins[i]).Normalize()
a.loader.directory = c.Val() }
if !path.IsAbs(a.loader.directory) && config.Root != "" {
a.loader.directory = path.Join(config.Root, a.loader.directory)
}
_, err := os.Stat(a.loader.directory)
if err != nil {
if os.IsNotExist(err) {
log.Printf("[WARNING] Directory does not exist: %s", a.loader.directory)
} else {
return a, c.Errf("Unable to access root path '%s': %v", a.loader.directory, err)
}
}
// regexp for c.NextBlock() {
if c.NextArg() { switch c.Val() {
a.loader.re, err = regexp.Compile(c.Val()) case "directory": // directory DIR [REGEXP [TEMPLATE] [DURATION]]
if err != nil { if !c.NextArg() {
return a, err return a, c.ArgErr()
} }
if a.loader.re.NumSubexp() == 0 { a.loader.directory = c.Val()
return a, c.Errf("Need at least one sub expression") if !path.IsAbs(a.loader.directory) && config.Root != "" {
} a.loader.directory = path.Join(config.Root, a.loader.directory)
}
_, err := os.Stat(a.loader.directory)
if err != nil {
if os.IsNotExist(err) {
log.Printf("[WARNING] Directory does not exist: %s", a.loader.directory)
} else {
return a, c.Errf("Unable to access root path '%s': %v", a.loader.directory, err)
} }
}
// template // regexp
if c.NextArg() { if c.NextArg() {
a.loader.template = rewriteToExpand(c.Val()) a.loader.re, err = regexp.Compile(c.Val())
if err != nil {
return a, err
} }
if a.loader.re.NumSubexp() == 0 {
// duration return a, c.Errf("Need at least one sub expression")
if c.NextArg() {
i, err := strconv.Atoi(c.Val())
if err != nil {
return a, err
}
if i < 1 {
i = 1
}
a.loader.duration = time.Duration(i) * time.Second
} }
}
case "no_reload": // template
a.loader.noReload = true if c.NextArg() {
a.loader.template = rewriteToExpand(c.Val())
}
case "upstream": // duration
args := c.RemainingArgs() if c.NextArg() {
if len(args) == 0 { i, err := strconv.Atoi(c.Val())
return a, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil { if err != nil {
return a, err return a, err
} }
a.loader.proxy = proxy.NewLookup(ups) if i < 1 {
i = 1
default:
t, _, e := file.TransferParse(c, false)
if e != nil {
return a, e
}
if t != nil {
a.loader.transferTo = append(a.loader.transferTo, t...)
} }
a.loader.duration = time.Duration(i) * time.Second
} }
}
case "no_reload":
a.loader.noReload = true
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return a, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil {
return a, err
}
a.loader.proxy = proxy.NewLookup(ups)
default:
t, _, e := file.TransferParse(c, false)
if e != nil {
return a, e
}
if t != nil {
a.loader.transferTo = append(a.loader.transferTo, t...)
}
}
} }
} }
return a, nil return a, nil
......
...@@ -42,36 +42,34 @@ func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, int, error) { ...@@ -42,36 +42,34 @@ func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, int, error) {
capacity := defaultCap capacity := defaultCap
for c.Next() { for c.Next() {
if c.Val() == "dnssec" { // dnssec [zones...]
// dnssec [zones...] zones = make([]string, len(c.ServerBlockKeys))
zones = make([]string, len(c.ServerBlockKeys)) copy(zones, c.ServerBlockKeys)
copy(zones, c.ServerBlockKeys) args := c.RemainingArgs()
args := c.RemainingArgs() if len(args) > 0 {
if len(args) > 0 { zones = args
zones = args }
}
for c.NextBlock() { for c.NextBlock() {
switch c.Val() { switch c.Val() {
case "key": case "key":
k, e := keyParse(c) k, e := keyParse(c)
if e != nil { if e != nil {
return nil, nil, 0, e return nil, nil, 0, e
}
keys = append(keys, k...)
case "cache_capacity":
if !c.NextArg() {
return nil, nil, 0, c.ArgErr()
}
value := c.Val()
cacheCap, err := strconv.Atoi(value)
if err != nil {
return nil, nil, 0, err
}
capacity = cacheCap
} }
keys = append(keys, k...)
case "cache_capacity":
if !c.NextArg() {
return nil, nil, 0, c.ArgErr()
}
value := c.Val()
cacheCap, err := strconv.Atoi(value)
if err != nil {
return nil, nil, 0, err
}
capacity = cacheCap
} }
} }
} }
for i := range zones { for i := range zones {
......
...@@ -60,71 +60,69 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) { ...@@ -60,71 +60,69 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) {
stubzones = false stubzones = false
) )
for c.Next() { for c.Next() {
if c.Val() == "etcd" { etc.Zones = c.RemainingArgs()
etc.Zones = c.RemainingArgs() if len(etc.Zones) == 0 {
if len(etc.Zones) == 0 { etc.Zones = make([]string, len(c.ServerBlockKeys))
etc.Zones = make([]string, len(c.ServerBlockKeys)) copy(etc.Zones, c.ServerBlockKeys)
copy(etc.Zones, c.ServerBlockKeys) }
} for i, str := range etc.Zones {
for i, str := range etc.Zones { etc.Zones[i] = middleware.Host(str).Normalize()
etc.Zones[i] = middleware.Host(str).Normalize() }
}
if c.NextBlock() { if c.NextBlock() {
for { for {
switch c.Val() { switch c.Val() {
case "stubzones": case "stubzones":
stubzones = true stubzones = true
case "debug": case "debug":
etc.Debugging = true etc.Debugging = true
case "path": case "path":
if !c.NextArg() { if !c.NextArg() {
return &Etcd{}, false, c.ArgErr() return &Etcd{}, false, c.ArgErr()
}
etc.PathPrefix = c.Val()
case "endpoint":
args := c.RemainingArgs()
if len(args) == 0 {
return &Etcd{}, false, c.ArgErr()
}
endpoints = args
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return &Etcd{}, false, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil {
return &Etcd{}, false, err
}
etc.Proxy = proxy.NewLookup(ups)
case "tls": // cert key cacertfile
args := c.RemainingArgs()
tlsConfig, err = mwtls.NewTLSConfigFromArgs(args...)
if err != nil {
return &Etcd{}, false, err
}
default:
if c.Val() != "}" {
return &Etcd{}, false, c.Errf("unknown property '%s'", c.Val())
}
} }
etc.PathPrefix = c.Val()
if !c.Next() { case "endpoint":
break args := c.RemainingArgs()
if len(args) == 0 {
return &Etcd{}, false, c.ArgErr()
}
endpoints = args
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return &Etcd{}, false, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil {
return &Etcd{}, false, err
}
etc.Proxy = proxy.NewLookup(ups)
case "tls": // cert key cacertfile
args := c.RemainingArgs()
tlsConfig, err = mwtls.NewTLSConfigFromArgs(args...)
if err != nil {
return &Etcd{}, false, err
}
default:
if c.Val() != "}" {
return &Etcd{}, false, c.Errf("unknown property '%s'", c.Val())
} }
} }
if !c.Next() {
break
}
} }
client, err := newEtcdClient(endpoints, tlsConfig)
if err != nil {
return &Etcd{}, false, err
}
etc.Client = client
etc.endpoints = endpoints
return &etc, stubzones, nil
} }
client, err := newEtcdClient(endpoints, tlsConfig)
if err != nil {
return &Etcd{}, false, err
}
etc.Client = client
etc.endpoints = endpoints
return &etc, stubzones, nil
} }
return &Etcd{}, false, nil return &Etcd{}, false, nil
} }
......
...@@ -55,76 +55,74 @@ func fileParse(c *caddy.Controller) (Zones, error) { ...@@ -55,76 +55,74 @@ func fileParse(c *caddy.Controller) (Zones, error) {
config := dnsserver.GetConfig(c) config := dnsserver.GetConfig(c)
for c.Next() { for c.Next() {
if c.Val() == "file" { // file db.file [zones...]
// file db.file [zones...] if !c.NextArg() {
if !c.NextArg() { return Zones{}, c.ArgErr()
return Zones{}, c.ArgErr() }
} fileName := c.Val()
fileName := c.Val()
origins = make([]string, len(c.ServerBlockKeys)) origins = make([]string, len(c.ServerBlockKeys))
copy(origins, c.ServerBlockKeys) copy(origins, c.ServerBlockKeys)
args := c.RemainingArgs() args := c.RemainingArgs()
if len(args) > 0 { if len(args) > 0 {
origins = args origins = args
} }
if !path.IsAbs(fileName) && config.Root != "" { if !path.IsAbs(fileName) && config.Root != "" {
fileName = path.Join(config.Root, fileName) fileName = path.Join(config.Root, fileName)
} }
reader, err := os.Open(fileName)
if err != nil {
// bail out
return Zones{}, err
}
reader, err := os.Open(fileName) for i := range origins {
if err != nil { origins[i] = middleware.Host(origins[i]).Normalize()
// bail out zone, err := Parse(reader, origins[i], fileName, 0)
if err == nil {
z[origins[i]] = zone
} else {
return Zones{}, err return Zones{}, err
} }
names = append(names, origins[i])
}
for i := range origins { noReload := false
origins[i] = middleware.Host(origins[i]).Normalize() prxy := proxy.Proxy{}
zone, err := Parse(reader, origins[i], fileName, 0) t := []string{}
if err == nil { var e error
z[origins[i]] = zone
} else { for c.NextBlock() {
return Zones{}, err switch c.Val() {
case "transfer":
t, _, e = TransferParse(c, false)
if e != nil {
return Zones{}, e
} }
names = append(names, origins[i])
}
noReload := false case "no_reload":
prxy := proxy.Proxy{} noReload = true
t := []string{}
var e error case "upstream":
args := c.RemainingArgs()
for c.NextBlock() { if len(args) == 0 {
switch c.Val() { return Zones{}, c.ArgErr()
case "transfer":
t, _, e = TransferParse(c, false)
if e != nil {
return Zones{}, e
}
case "no_reload":
noReload = true
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return Zones{}, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil {
return Zones{}, err
}
prxy = proxy.NewLookup(ups)
} }
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil {
return Zones{}, err
}
prxy = proxy.NewLookup(ups)
}
for _, origin := range origins { for _, origin := range origins {
if t != nil { if t != nil {
z[origin].TransferTo = append(z[origin].TransferTo, t...) z[origin].TransferTo = append(z[origin].TransferTo, t...)
}
z[origin].NoReload = noReload
z[origin].Proxy = prxy
} }
z[origin].NoReload = noReload
z[origin].Proxy = prxy
} }
} }
} }
......
...@@ -41,46 +41,44 @@ func hostsParse(c *caddy.Controller) (Hosts, error) { ...@@ -41,46 +41,44 @@ func hostsParse(c *caddy.Controller) (Hosts, error) {
config := dnsserver.GetConfig(c) config := dnsserver.GetConfig(c)
for c.Next() { for c.Next() {
if c.Val() == "hosts" { // hosts [FILE] [ZONES...] args := c.RemainingArgs()
args := c.RemainingArgs() if len(args) >= 1 {
if len(args) >= 1 { h.path = args[0]
h.path = args[0] args = args[1:]
args = args[1:]
if !path.IsAbs(h.path) && config.Root != "" { if !path.IsAbs(h.path) && config.Root != "" {
h.path = path.Join(config.Root, h.path) h.path = path.Join(config.Root, h.path)
} }
_, err := os.Stat(h.path) _, err := os.Stat(h.path)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
log.Printf("[WARNING] File does not exist: %s", h.path) log.Printf("[WARNING] File does not exist: %s", h.path)
} else { } else {
return h, c.Errf("unable to access hosts file '%s': %v", h.path, err) return h, c.Errf("unable to access hosts file '%s': %v", h.path, err)
}
} }
} }
}
origins := make([]string, len(c.ServerBlockKeys)) origins := make([]string, len(c.ServerBlockKeys))
copy(origins, c.ServerBlockKeys) copy(origins, c.ServerBlockKeys)
if len(args) > 0 { if len(args) > 0 {
origins = args origins = args
} }
for i := range origins { for i := range origins {
origins[i] = middleware.Host(origins[i]).Normalize() origins[i] = middleware.Host(origins[i]).Normalize()
} }
h.Origins = origins h.Origins = origins
for c.NextBlock() { for c.NextBlock() {
switch c.Val() { switch c.Val() {
case "fallthrough": case "fallthrough":
args := c.RemainingArgs() args := c.RemainingArgs()
if len(args) == 0 { if len(args) == 0 {
h.Fallthrough = true h.Fallthrough = true
continue continue
}
return h, c.ArgErr()
} }
return h, c.ArgErr()
} }
} }
} }
......
...@@ -30,7 +30,8 @@ func (z Zones) Matches(qname string) string { ...@@ -30,7 +30,8 @@ func (z Zones) Matches(qname string) string {
return zone return zone
} }
// Normalize fully qualifies all zones in z. // Normalize fully qualifies all zones in z. The zones in Z must be domain names, without
// a port or protocol prefix.
func (z Zones) Normalize() { func (z Zones) Normalize() {
for i := range z { for i := range z {
z[i] = Name(z[i]).Normalize() z[i] = Name(z[i]).Normalize()
...@@ -54,7 +55,7 @@ func (n Name) Normalize() string { return strings.ToLower(dns.Fqdn(string(n))) } ...@@ -54,7 +55,7 @@ func (n Name) Normalize() string { return strings.ToLower(dns.Fqdn(string(n))) }
type ( type (
// Host represents a host from the Corefile, may contain port. // Host represents a host from the Corefile, may contain port.
Host string // Host represents a host from the Corefile, may contain port. Host string
) )
// Normalize will return the host portion of host, stripping // Normalize will return the host portion of host, stripping
......
...@@ -34,116 +34,111 @@ func setupReverse(c *caddy.Controller) error { ...@@ -34,116 +34,111 @@ func setupReverse(c *caddy.Controller) error {
} }
func reverseParse(c *caddy.Controller) (nets networks, fall bool, err error) { func reverseParse(c *caddy.Controller) (nets networks, fall bool, err error) {
// normalize zones, validation is almost done by dnsserver
// TODO(miek): need sane helpers for these.
zones := make([]string, len(c.ServerBlockKeys)) zones := make([]string, len(c.ServerBlockKeys))
wildcard := false wildcard := false
// We copy from the serverblock, these contains Hosts.
for i, str := range c.ServerBlockKeys { for i, str := range c.ServerBlockKeys {
zones[i] = middleware.Host(str).Normalize() zones[i] = middleware.Host(str).Normalize()
} }
for c.Next() { for c.Next() {
if c.Val() == "reverse" { 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)
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 { _, ipnet, err := net.ParseCIDR(cidr)
return nil, false, c.ArgErr() 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 // set defaults
var ( var (
template = "ip-" + templateNameIP + ".{zone[1]}" template = "ip-" + templateNameIP + ".{zone[1]}"
ttl = 60 ttl = 60
) )
for c.NextBlock() { for c.NextBlock() {
switch c.Val() { switch c.Val() {
case "hostname": case "hostname":
if !c.NextArg() { 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() return nil, false, c.ArgErr()
} }
} template = c.Val()
// 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 case "ttl":
templateZone := strings.SplitAfterN(template, ".", 2) if !c.NextArg() {
if len(templateZone) != 2 || templateZone[1] == "" { return nil, false, c.ArgErr()
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( ttl, err = strconv.Atoi(c.Val())
prefix + strings.Replace( // inject ip regex into template
regexp.QuoteMeta(template), // escape dots
regexp.QuoteMeta(templateNameIP),
regexIP,
1) + "$")
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
nets = append(nets, network{ case "wildcard":
IPnet: ipnet, wildcard = true
Zone: templateZone[1],
Template: template, case "fallthrough":
RegexMatchIP: regex, fall = true
TTL: uint32(ttl),
}) 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 by cidr
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment