forked from TrueCloudLab/distribution
a685e3fc98
Vndr has a simpler configuration and allows pointing to forked packages. Additionally other docker projects are now using vndr making vendoring in distribution more consistent. Updates letsencrypt to use fork. No longer uses sub-vendored packages. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
159 lines
3.8 KiB
Go
159 lines
3.8 KiB
Go
package dns
|
|
|
|
import (
|
|
"bytes"
|
|
"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)
|
|
// But we are lazy here, only the range is parsed *all* occurrences
|
|
// 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, err := strconv.Atoi(l.token[i+1:]); err == 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 error
|
|
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 != nil {
|
|
return err.Error()
|
|
}
|
|
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, err := NewRR("$ORIGIN " + o + "\n" + dom.String())
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
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, error) {
|
|
xs := strings.SplitN(s, ",", 3)
|
|
if len(xs) != 3 {
|
|
return "", 0, errors.New("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, errors.New("bad base in $GENERATE")
|
|
}
|
|
offset, err := strconv.Atoi(xs[0])
|
|
if err != nil || offset > 255 {
|
|
return "", 0, errors.New("bad offset in $GENERATE")
|
|
}
|
|
width, err := strconv.Atoi(xs[1])
|
|
if err != nil || width > 255 {
|
|
return "", offset, errors.New("bad width in $GENERATE")
|
|
}
|
|
switch {
|
|
case width < 0:
|
|
return "", offset, errors.New("bad width in $GENERATE")
|
|
case width == 0:
|
|
return "%" + xs[1] + xs[2], offset, nil
|
|
}
|
|
return "%0" + xs[1] + xs[2], offset, nil
|
|
}
|