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.
158 lines
3.7 KiB
158 lines
3.7 KiB
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, "" |
|
}
|
|
|