You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
3.7 KiB
159 lines
3.7 KiB
9 years ago
|
package dns
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"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)
|
||
|
// But we are lazy here, only the range is parsed *all* occurences
|
||
|
// of $ after that are interpreted.
|
||
|
// Any error are returned as a string value, the empty string signals
|
||
|
// "no error".
|
||
|
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 {
|
||
|
if s < 0 {
|
||
|
return "bad step in $GENERATE range"
|
||
|
}
|
||
|
step = s
|
||
|
} else {
|
||
|
return "bad step in $GENERATE range"
|
||
|
}
|
||
|
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 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 != zNewline && l.value != zEOF {
|
||
|
s += l.token
|
||
|
goto BuildRR
|
||
|
}
|
||
|
for i := start; i <= end; i += step {
|
||
|
var (
|
||
|
escape bool
|
||
|
dom bytes.Buffer
|
||
|
mod string
|
||
|
err string
|
||
|
offset int
|
||
|
)
|
||
|
|
||
|
for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
|
||
|
switch s[j] {
|
||
|
case '\\':
|
||
|
if escape {
|
||
|
dom.WriteByte('\\')
|
||
|
escape = false
|
||
|
continue
|
||
|
}
|
||
|
escape = true
|
||
|
case '$':
|
||
|
mod = "%d"
|
||
|
offset = 0
|
||
|
if escape {
|
||
|
dom.WriteByte('$')
|
||
|
escape = false
|
||
|
continue
|
||
|
}
|
||
|
escape = false
|
||
|
if j+1 >= len(s) { // End of the string
|
||
|
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
||
|
continue
|
||
|
} else {
|
||
|
if s[j+1] == '$' {
|
||
|
dom.WriteByte('$')
|
||
|
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"
|
||
|
}
|
||
|
mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
|
||
|
if err != "" {
|
||
|
return err
|
||
|
}
|
||
|
j += 2 + sep // Jump to it
|
||
|
}
|
||
|
dom.WriteString(fmt.Sprintf(mod, i+offset))
|
||
|
default:
|
||
|
if escape { // Pretty useless here
|
||
|
escape = false
|
||
|
continue
|
||
|
}
|
||
|
dom.WriteByte(s[j])
|
||
|
}
|
||
|
}
|
||
|
// Re-parse the RR and send it on the current channel t
|
||
|
rx, e := NewRR("$ORIGIN " + o + "\n" + dom.String())
|
||
|
if e != nil {
|
||
|
return e.(*ParseError).err
|
||
|
}
|
||
|
t <- &Token{RR: rx}
|
||
|
// Its more efficient to first built the rrlist and then parse it in
|
||
|
// one go! But is this a problem?
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
|
||
|
func modToPrintf(s string) (string, int, string) {
|
||
|
xs := strings.SplitN(s, ",", 3)
|
||
|
if len(xs) != 3 {
|
||
|
return "", 0, "bad modifier in $GENERATE"
|
||
|
}
|
||
|
// 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, "bad base in $GENERATE"
|
||
|
}
|
||
|
offset, err := strconv.Atoi(xs[0])
|
||
|
if err != nil {
|
||
|
return "", 0, "bad offset in $GENERATE"
|
||
|
}
|
||
|
width, err := strconv.Atoi(xs[1])
|
||
|
if err != nil {
|
||
|
return "", offset, "bad width in $GENERATE"
|
||
|
}
|
||
|
switch {
|
||
|
case width < 0:
|
||
|
return "", offset, "bad width in $GENERATE"
|
||
|
case width == 0:
|
||
|
return "%" + xs[1] + xs[2], offset, ""
|
||
|
}
|
||
|
return "%0" + xs[1] + xs[2], offset, ""
|
||
|
}
|