generate.go 4.7 KB

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