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 }