gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

generate.go (5089B)


      1 package dns
      2 
      3 import (
      4 	"bytes"
      5 	"fmt"
      6 	"io"
      7 	"strconv"
      8 	"strings"
      9 )
     10 
     11 // Parse the $GENERATE statement as used in BIND9 zones.
     12 // See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
     13 // We are called after '$GENERATE '. After which we expect:
     14 // * the range (12-24/2)
     15 // * lhs (ownername)
     16 // * [[ttl][class]]
     17 // * type
     18 // * rhs (rdata)
     19 // But we are lazy here, only the range is parsed *all* occurrences
     20 // of $ after that are interpreted.
     21 func (zp *ZoneParser) generate(l lex) (RR, bool) {
     22 	token := l.token
     23 	step := int64(1)
     24 	if i := strings.IndexByte(token, '/'); i >= 0 {
     25 		if i+1 == len(token) {
     26 			return zp.setParseError("bad step in $GENERATE range", l)
     27 		}
     28 
     29 		s, err := strconv.ParseInt(token[i+1:], 10, 64)
     30 		if err != nil || s <= 0 {
     31 			return zp.setParseError("bad step in $GENERATE range", l)
     32 		}
     33 
     34 		step = s
     35 		token = token[:i]
     36 	}
     37 
     38 	sx := strings.SplitN(token, "-", 2)
     39 	if len(sx) != 2 {
     40 		return zp.setParseError("bad start-stop in $GENERATE range", l)
     41 	}
     42 
     43 	start, err := strconv.ParseInt(sx[0], 10, 64)
     44 	if err != nil {
     45 		return zp.setParseError("bad start in $GENERATE range", l)
     46 	}
     47 
     48 	end, err := strconv.ParseInt(sx[1], 10, 64)
     49 	if err != nil {
     50 		return zp.setParseError("bad stop in $GENERATE range", l)
     51 	}
     52 	if end < 0 || start < 0 || end < start || (end-start)/step > 65535 {
     53 		return zp.setParseError("bad range in $GENERATE range", l)
     54 	}
     55 
     56 	// _BLANK
     57 	l, ok := zp.c.Next()
     58 	if !ok || l.value != zBlank {
     59 		return zp.setParseError("garbage after $GENERATE range", l)
     60 	}
     61 
     62 	// Create a complete new string, which we then parse again.
     63 	var s string
     64 	for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() {
     65 		if l.err {
     66 			return zp.setParseError("bad data in $GENERATE directive", l)
     67 		}
     68 		if l.value == zNewline {
     69 			break
     70 		}
     71 
     72 		s += l.token
     73 	}
     74 
     75 	r := &generateReader{
     76 		s: s,
     77 
     78 		cur:   start,
     79 		start: start,
     80 		end:   end,
     81 		step:  step,
     82 
     83 		file: zp.file,
     84 		lex:  &l,
     85 	}
     86 	zp.sub = NewZoneParser(r, zp.origin, zp.file)
     87 	zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed
     88 	zp.sub.generateDisallowed = true
     89 	zp.sub.SetDefaultTTL(defaultTtl)
     90 	return zp.subNext()
     91 }
     92 
     93 type generateReader struct {
     94 	s  string
     95 	si int
     96 
     97 	cur   int64
     98 	start int64
     99 	end   int64
    100 	step  int64
    101 
    102 	mod bytes.Buffer
    103 
    104 	escape bool
    105 
    106 	eof bool
    107 
    108 	file string
    109 	lex  *lex
    110 }
    111 
    112 func (r *generateReader) parseError(msg string, end int) *ParseError {
    113 	r.eof = true // Make errors sticky.
    114 
    115 	l := *r.lex
    116 	l.token = r.s[r.si-1 : end]
    117 	l.column += r.si // l.column starts one zBLANK before r.s
    118 
    119 	return &ParseError{r.file, msg, l}
    120 }
    121 
    122 func (r *generateReader) Read(p []byte) (int, error) {
    123 	// NewZLexer, through NewZoneParser, should use ReadByte and
    124 	// not end up here.
    125 
    126 	panic("not implemented")
    127 }
    128 
    129 func (r *generateReader) ReadByte() (byte, error) {
    130 	if r.eof {
    131 		return 0, io.EOF
    132 	}
    133 	if r.mod.Len() > 0 {
    134 		return r.mod.ReadByte()
    135 	}
    136 
    137 	if r.si >= len(r.s) {
    138 		r.si = 0
    139 		r.cur += r.step
    140 
    141 		r.eof = r.cur > r.end || r.cur < 0
    142 		return '\n', nil
    143 	}
    144 
    145 	si := r.si
    146 	r.si++
    147 
    148 	switch r.s[si] {
    149 	case '\\':
    150 		if r.escape {
    151 			r.escape = false
    152 			return '\\', nil
    153 		}
    154 
    155 		r.escape = true
    156 		return r.ReadByte()
    157 	case '$':
    158 		if r.escape {
    159 			r.escape = false
    160 			return '$', nil
    161 		}
    162 
    163 		mod := "%d"
    164 
    165 		if si >= len(r.s)-1 {
    166 			// End of the string
    167 			fmt.Fprintf(&r.mod, mod, r.cur)
    168 			return r.mod.ReadByte()
    169 		}
    170 
    171 		if r.s[si+1] == '$' {
    172 			r.si++
    173 			return '$', nil
    174 		}
    175 
    176 		var offset int64
    177 
    178 		// Search for { and }
    179 		if r.s[si+1] == '{' {
    180 			// Modifier block
    181 			sep := strings.Index(r.s[si+2:], "}")
    182 			if sep < 0 {
    183 				return 0, r.parseError("bad modifier in $GENERATE", len(r.s))
    184 			}
    185 
    186 			var errMsg string
    187 			mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep])
    188 			if errMsg != "" {
    189 				return 0, r.parseError(errMsg, si+3+sep)
    190 			}
    191 			if r.start+offset < 0 || r.end+offset > 1<<31-1 {
    192 				return 0, r.parseError("bad offset in $GENERATE", si+3+sep)
    193 			}
    194 
    195 			r.si += 2 + sep // Jump to it
    196 		}
    197 
    198 		fmt.Fprintf(&r.mod, mod, r.cur+offset)
    199 		return r.mod.ReadByte()
    200 	default:
    201 		if r.escape { // Pretty useless here
    202 			r.escape = false
    203 			return r.ReadByte()
    204 		}
    205 
    206 		return r.s[si], nil
    207 	}
    208 }
    209 
    210 // Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
    211 func modToPrintf(s string) (string, int64, string) {
    212 	// Modifier is { offset [ ,width [ ,base ] ] } - provide default
    213 	// values for optional width and type, if necessary.
    214 	var offStr, widthStr, base string
    215 	switch xs := strings.Split(s, ","); len(xs) {
    216 	case 1:
    217 		offStr, widthStr, base = xs[0], "0", "d"
    218 	case 2:
    219 		offStr, widthStr, base = xs[0], xs[1], "d"
    220 	case 3:
    221 		offStr, widthStr, base = xs[0], xs[1], xs[2]
    222 	default:
    223 		return "", 0, "bad modifier in $GENERATE"
    224 	}
    225 
    226 	switch base {
    227 	case "o", "d", "x", "X":
    228 	default:
    229 		return "", 0, "bad base in $GENERATE"
    230 	}
    231 
    232 	offset, err := strconv.ParseInt(offStr, 10, 64)
    233 	if err != nil {
    234 		return "", 0, "bad offset in $GENERATE"
    235 	}
    236 
    237 	width, err := strconv.ParseInt(widthStr, 10, 64)
    238 	if err != nil || width < 0 || width > 255 {
    239 		return "", 0, "bad width in $GENERATE"
    240 	}
    241 
    242 	if width == 0 {
    243 		return "%" + base, offset, ""
    244 	}
    245 
    246 	return "%0" + widthStr + base, offset, ""
    247 }