157 lines
3.4 KiB
Go
157 lines
3.4 KiB
Go
package dns
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Parse the $GENERATE statement as used in BIND9 zones.
|
|
// See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
|
|
// We are called after '$GENERATE '. After which we expect:
|
|
// * the range (12-24/2)
|
|
// * lhs (ownername)
|
|
// * [[ttl][class]]
|
|
// * type
|
|
// * rhs (rdata)
|
|
func generate(l lex, c chan lex, t chan Token, o string) string {
|
|
step := 1
|
|
if i := strings.IndexAny(l.token, "/"); i != -1 {
|
|
if i+1 == len(l.token) {
|
|
return "bad step in $GENERATE range"
|
|
}
|
|
if s, e := strconv.Atoi(l.token[i+1:]); e != nil {
|
|
return "bad step in $GENERATE range"
|
|
} else {
|
|
if s < 0 {
|
|
return "bad step in $GENERATE range"
|
|
}
|
|
step = s
|
|
}
|
|
l.token = l.token[:i]
|
|
}
|
|
sx := strings.SplitN(l.token, "-", 2)
|
|
if len(sx) != 2 {
|
|
return "bad start/stop in $GENERATE range"
|
|
}
|
|
start, err := strconv.Atoi(sx[0])
|
|
if err != nil {
|
|
return "bad start in $GENERATE range"
|
|
}
|
|
end, err := strconv.Atoi(sx[1])
|
|
if err != nil {
|
|
return "bad end in $GENERATE range"
|
|
}
|
|
if end < 0 || start < 0 || end <= start {
|
|
return "bad range in $GENERATE range"
|
|
}
|
|
|
|
<-c // _BLANK
|
|
// Create a complete new string, which we then parse again.
|
|
s := ""
|
|
BuildRR:
|
|
l = <-c
|
|
if l.value != _NEWLINE && l.value != _EOF {
|
|
s += l.token
|
|
goto BuildRR
|
|
}
|
|
for i := start; i <= end; i += step {
|
|
escape := false
|
|
dom := ""
|
|
// Defaults
|
|
mod := "%d"
|
|
offset := 0
|
|
var err error
|
|
// Build the domain name.
|
|
for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
|
|
switch s[j] {
|
|
case '\\':
|
|
if escape {
|
|
dom += "\\"
|
|
escape = false
|
|
continue
|
|
}
|
|
escape = true
|
|
case '$':
|
|
mod = "%d"
|
|
offset = 0
|
|
if escape {
|
|
dom += "$"
|
|
escape = false
|
|
continue
|
|
}
|
|
escape = false
|
|
if j+1 >= len(s) { // End of the string
|
|
dom += fmt.Sprintf(mod, i+offset)
|
|
continue
|
|
} else {
|
|
if s[j+1] == '$' {
|
|
dom += "$"
|
|
j++
|
|
continue
|
|
}
|
|
}
|
|
// Search for { and }
|
|
if s[j+1] == '{' { // Modifier block
|
|
sep := strings.Index(s[j+2:], "}")
|
|
if sep == -1 {
|
|
return "bad modifier in $GENERATE"
|
|
}
|
|
//println("checking", s[j+2:j+2+sep])
|
|
mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
|
|
if err != nil {
|
|
return "bad modifier in $GENERATE"
|
|
}
|
|
j += 2 + sep // Jump to it
|
|
}
|
|
//println("mod", mod)
|
|
dom += fmt.Sprintf(mod, i+offset)
|
|
default:
|
|
if escape { // Pretty useless here
|
|
escape = false
|
|
continue
|
|
}
|
|
dom += string(s[j])
|
|
}
|
|
}
|
|
// Re-parse the RR and send it on the current channel t
|
|
rx, err := NewRR("$ORIGIN " + o + "\n" + dom)
|
|
if err != nil {
|
|
return err.(*ParseError).err
|
|
}
|
|
t <- Token{RR: rx}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
|
|
func modToPrintf(s string) (string, int, error) {
|
|
xs := strings.SplitN(s, ",", 3)
|
|
if len(xs) != 3 {
|
|
return "", 0, nil // make error
|
|
}
|
|
// xs[0] is offset, xs[1] is width, xs[2] is base
|
|
if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" {
|
|
return "", 0, nil // make error
|
|
}
|
|
offset, err := strconv.Atoi(xs[0])
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
width, err := strconv.Atoi(xs[1])
|
|
if err != nil {
|
|
return "", offset, err
|
|
}
|
|
printf := "%"
|
|
switch {
|
|
case width < 0:
|
|
return "", offset, nil // make error
|
|
case width == 0:
|
|
printf += xs[1]
|
|
default:
|
|
printf += "0" + xs[1]
|
|
}
|
|
printf += xs[2]
|
|
return printf, offset, nil
|
|
}
|