123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- package dns
-
- import (
- "bytes"
- "fmt"
- "io"
- "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.
- func (zp *ZoneParser) generate(l lex) (RR, bool) {
- token := l.token
- step := int64(1)
- if i := strings.IndexByte(token, '/'); i >= 0 {
- if i+1 == len(token) {
- return zp.setParseError("bad step in $GENERATE range", l)
- }
-
- s, err := strconv.ParseInt(token[i+1:], 10, 64)
- if err != nil || s <= 0 {
- return zp.setParseError("bad step in $GENERATE range", l)
- }
-
- step = s
- token = token[:i]
- }
-
- sx := strings.SplitN(token, "-", 2)
- if len(sx) != 2 {
- return zp.setParseError("bad start-stop in $GENERATE range", l)
- }
-
- start, err := strconv.ParseInt(sx[0], 10, 64)
- if err != nil {
- return zp.setParseError("bad start in $GENERATE range", l)
- }
-
- end, err := strconv.ParseInt(sx[1], 10, 64)
- if err != nil {
- return zp.setParseError("bad stop in $GENERATE range", l)
- }
- if end < 0 || start < 0 || end < start || (end-start)/step > 65535 {
- return zp.setParseError("bad range in $GENERATE range", l)
- }
-
- // _BLANK
- l, ok := zp.c.Next()
- if !ok || l.value != zBlank {
- return zp.setParseError("garbage after $GENERATE range", l)
- }
-
- // Create a complete new string, which we then parse again.
- var s string
- for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {
- if l.err {
- return zp.setParseError("bad data in $GENERATE directive", l)
- }
- if l.value == zNewline {
- break
- }
-
- s += l.token
- }
-
- r := &generateReader{
- s: s,
-
- cur: start,
- start: start,
- end: end,
- step: step,
-
- file: zp.file,
- lex: &l,
- }
- zp.sub = NewZoneParser(r, zp.origin, zp.file)
- zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed
- zp.sub.generateDisallowed = true
- zp.sub.SetDefaultTTL(defaultTtl)
- return zp.subNext()
- }
-
- type generateReader struct {
- s string
- si int
-
- cur int64
- start int64
- end int64
- step int64
-
- mod bytes.Buffer
-
- escape bool
-
- eof bool
-
- file string
- lex *lex
- }
-
- func (r *generateReader) parseError(msg string, end int) *ParseError {
- r.eof = true // Make errors sticky.
-
- l := *r.lex
- l.token = r.s[r.si-1 : end]
- l.column += r.si // l.column starts one zBLANK before r.s
-
- return &ParseError{r.file, msg, l}
- }
-
- func (r *generateReader) Read(p []byte) (int, error) {
- // NewZLexer, through NewZoneParser, should use ReadByte and
- // not end up here.
-
- panic("not implemented")
- }
-
- func (r *generateReader) ReadByte() (byte, error) {
- if r.eof {
- return 0, io.EOF
- }
- if r.mod.Len() > 0 {
- return r.mod.ReadByte()
- }
-
- if r.si >= len(r.s) {
- r.si = 0
- r.cur += r.step
-
- r.eof = r.cur > r.end || r.cur < 0
- return '\n', nil
- }
-
- si := r.si
- r.si++
-
- switch r.s[si] {
- case '\\':
- if r.escape {
- r.escape = false
- return '\\', nil
- }
-
- r.escape = true
- return r.ReadByte()
- case '$':
- if r.escape {
- r.escape = false
- return '$', nil
- }
-
- mod := "%d"
-
- if si >= len(r.s)-1 {
- // End of the string
- fmt.Fprintf(&r.mod, mod, r.cur)
- return r.mod.ReadByte()
- }
-
- if r.s[si+1] == '$' {
- r.si++
- return '$', nil
- }
-
- var offset int64
-
- // Search for { and }
- if r.s[si+1] == '{' {
- // Modifier block
- sep := strings.Index(r.s[si+2:], "}")
- if sep < 0 {
- return 0, r.parseError("bad modifier in $GENERATE", len(r.s))
- }
-
- var errMsg string
- mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep])
- if errMsg != "" {
- return 0, r.parseError(errMsg, si+3+sep)
- }
- if r.start+offset < 0 || r.end+offset > 1<<31-1 {
- return 0, r.parseError("bad offset in $GENERATE", si+3+sep)
- }
-
- r.si += 2 + sep // Jump to it
- }
-
- fmt.Fprintf(&r.mod, mod, r.cur+offset)
- return r.mod.ReadByte()
- default:
- if r.escape { // Pretty useless here
- r.escape = false
- return r.ReadByte()
- }
-
- return r.s[si], nil
- }
- }
-
- // Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
- func modToPrintf(s string) (string, int64, string) {
- // Modifier is { offset [ ,width [ ,base ] ] } - provide default
- // values for optional width and type, if necessary.
- var offStr, widthStr, base string
- switch xs := strings.Split(s, ","); len(xs) {
- case 1:
- offStr, widthStr, base = xs[0], "0", "d"
- case 2:
- offStr, widthStr, base = xs[0], xs[1], "d"
- case 3:
- offStr, widthStr, base = xs[0], xs[1], xs[2]
- default:
- return "", 0, "bad modifier in $GENERATE"
- }
-
- switch base {
- case "o", "d", "x", "X":
- default:
- return "", 0, "bad base in $GENERATE"
- }
-
- offset, err := strconv.ParseInt(offStr, 10, 64)
- if err != nil {
- return "", 0, "bad offset in $GENERATE"
- }
-
- width, err := strconv.ParseInt(widthStr, 10, 64)
- if err != nil || width < 0 || width > 255 {
- return "", 0, "bad width in $GENERATE"
- }
-
- if width == 0 {
- return "%" + base, offset, ""
- }
-
- return "%0" + widthStr + base, offset, ""
- }
|