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.
This commit is contained in:
Miek Gieben 2017-08-10 05:30:18 -07:00 committed by GitHub
parent 28447234b1
commit 3654361be2
7 changed files with 326 additions and 341 deletions

View file

@ -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() args := c.RemainingArgs()
if len(args) > 0 { if len(args) > 0 {
a.Zones.origins = args a.Zones.origins = args
} }
for i := range a.Zones.origins { for i := range a.Zones.origins {
a.Zones.origins[i] = middleware.Host(a.Zones.origins[i]).Normalize() a.Zones.origins[i] = middleware.Host(a.Zones.origins[i]).Normalize()
} }
for c.NextBlock() { for c.NextBlock() {
switch c.Val() { switch c.Val() {
case "directory": // directory DIR [REGEXP [TEMPLATE] [DURATION]] case "directory": // directory DIR [REGEXP [TEMPLATE] [DURATION]]
if !c.NextArg() { if !c.NextArg() {
return a, c.ArgErr() return a, c.ArgErr()
} }
a.loader.directory = c.Val() a.loader.directory = c.Val()
if !path.IsAbs(a.loader.directory) && config.Root != "" { if !path.IsAbs(a.loader.directory) && config.Root != "" {
a.loader.directory = path.Join(config.Root, a.loader.directory) a.loader.directory = path.Join(config.Root, a.loader.directory)
} }
_, err := os.Stat(a.loader.directory) _, err := os.Stat(a.loader.directory)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
log.Printf("[WARNING] Directory does not exist: %s", a.loader.directory) log.Printf("[WARNING] Directory does not exist: %s", a.loader.directory)
} else { } else {
return a, c.Errf("Unable to access root path '%s': %v", a.loader.directory, err) return a, c.Errf("Unable to access root path '%s': %v", a.loader.directory, err)
}
} }
}
// regexp // regexp
if c.NextArg() { if c.NextArg() {
a.loader.re, err = regexp.Compile(c.Val()) a.loader.re, err = regexp.Compile(c.Val())
if err != nil {
return a, err
}
if a.loader.re.NumSubexp() == 0 {
return a, c.Errf("Need at least one sub expression")
}
}
// template
if c.NextArg() {
a.loader.template = rewriteToExpand(c.Val())
}
// duration
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":
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 { if err != nil {
return a, err return a, err
} }
a.loader.proxy = proxy.NewLookup(ups) if a.loader.re.NumSubexp() == 0 {
return a, c.Errf("Need at least one sub expression")
default:
t, _, e := file.TransferParse(c, false)
if e != nil {
return a, e
}
if t != nil {
a.loader.transferTo = append(a.loader.transferTo, t...)
} }
} }
}
// template
if c.NextArg() {
a.loader.template = rewriteToExpand(c.Val())
}
// duration
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":
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

View file

@ -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 {

View file

@ -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
} }

View file

@ -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) reader, err := os.Open(fileName)
if err != nil { if err != nil {
// bail out // bail out
return Zones{}, err
}
for i := range origins {
origins[i] = middleware.Host(origins[i]).Normalize()
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() {
switch c.Val() {
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 return Zones{}, err
} }
names = append(names, origins[i]) prxy = proxy.NewLookup(ups)
} }
noReload := false for _, origin := range origins {
prxy := proxy.Proxy{} if t != nil {
t := []string{} z[origin].TransferTo = append(z[origin].TransferTo, t...)
var e error
for c.NextBlock() {
switch c.Val() {
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)
}
for _, origin := range origins {
if t != nil {
z[origin].TransferTo = append(z[origin].TransferTo, t...)
}
z[origin].NoReload = noReload
z[origin].Proxy = prxy
} }
z[origin].NoReload = noReload
z[origin].Proxy = prxy
} }
} }
} }

View file

@ -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()
} }
} }
} }

View file

@ -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))) }
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

View file

@ -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
for _, cidr := range c.RemainingArgs() {
// parse all networks if cidr == "{" {
for _, cidr := range c.RemainingArgs() { break
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 { _, 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 case "ttl":
// replace {zone[index]} by the listen zone/domain of this config block if !c.NextArg() {
for i, zone := range zones { return nil, false, c.ArgErr()
// 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 := "^" ttl, err = strconv.Atoi(c.Val())
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 { 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