dns/zgenerate.go

159 lines
3.4 KiB
Go
Raw Normal View History

package dns
import (
2012-05-20 23:48:20 +10:00
"errors"
"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 {
2012-05-20 23:48:20 +10:00
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 {
2012-05-20 23:48:20 +10:00
return "bad stop 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 {
2012-05-20 23:48:20 +10:00
var (
escape bool
dom string
mod string
offset int
err error
)
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 {
2012-05-20 23:48:20 +10:00
return "", 0, errors.New("fubar")
}
// 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" {
2012-05-20 23:48:20 +10:00
return "", 0, errors.New("fubar")
}
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:
2012-05-20 23:48:20 +10:00
return "", offset, errors.New("fubar")
case width == 0:
printf += xs[1]
default:
printf += "0" + xs[1]
}
printf += xs[2]
return printf, offset, nil
}