zgenerate.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package dns
  2. import (
  3. "bytes"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. )
  8. // Parse the $GENERATE statement as used in BIND9 zones.
  9. // See http://www.zytrax.com/books/dns/ch8/generate.html for instance.
  10. // We are called after '$GENERATE '. After which we expect:
  11. // * the range (12-24/2)
  12. // * lhs (ownername)
  13. // * [[ttl][class]]
  14. // * type
  15. // * rhs (rdata)
  16. // But we are lazy here, only the range is parsed *all* occurrences
  17. // of $ after that are interpreted.
  18. // Any error are returned as a string value, the empty string signals
  19. // "no error".
  20. func generate(l lex, c chan lex, t chan *Token, o string) string {
  21. step := 1
  22. if i := strings.IndexAny(l.token, "/"); i != -1 {
  23. if i+1 == len(l.token) {
  24. return "bad step in $GENERATE range"
  25. }
  26. if s, e := strconv.Atoi(l.token[i+1:]); e == nil {
  27. if s < 0 {
  28. return "bad step in $GENERATE range"
  29. }
  30. step = s
  31. } else {
  32. return "bad step in $GENERATE range"
  33. }
  34. l.token = l.token[:i]
  35. }
  36. sx := strings.SplitN(l.token, "-", 2)
  37. if len(sx) != 2 {
  38. return "bad start-stop in $GENERATE range"
  39. }
  40. start, err := strconv.Atoi(sx[0])
  41. if err != nil {
  42. return "bad start in $GENERATE range"
  43. }
  44. end, err := strconv.Atoi(sx[1])
  45. if err != nil {
  46. return "bad stop in $GENERATE range"
  47. }
  48. if end < 0 || start < 0 || end < start {
  49. return "bad range in $GENERATE range"
  50. }
  51. <-c // _BLANK
  52. // Create a complete new string, which we then parse again.
  53. s := ""
  54. BuildRR:
  55. l = <-c
  56. if l.value != zNewline && l.value != zEOF {
  57. s += l.token
  58. goto BuildRR
  59. }
  60. for i := start; i <= end; i += step {
  61. var (
  62. escape bool
  63. dom bytes.Buffer
  64. mod string
  65. err string
  66. offset int
  67. )
  68. for j := 0; j < len(s); j++ { // No 'range' because we need to jump around
  69. switch s[j] {
  70. case '\\':
  71. if escape {
  72. dom.WriteByte('\\')
  73. escape = false
  74. continue
  75. }
  76. escape = true
  77. case '$':
  78. mod = "%d"
  79. offset = 0
  80. if escape {
  81. dom.WriteByte('$')
  82. escape = false
  83. continue
  84. }
  85. escape = false
  86. if j+1 >= len(s) { // End of the string
  87. dom.WriteString(fmt.Sprintf(mod, i+offset))
  88. continue
  89. } else {
  90. if s[j+1] == '$' {
  91. dom.WriteByte('$')
  92. j++
  93. continue
  94. }
  95. }
  96. // Search for { and }
  97. if s[j+1] == '{' { // Modifier block
  98. sep := strings.Index(s[j+2:], "}")
  99. if sep == -1 {
  100. return "bad modifier in $GENERATE"
  101. }
  102. mod, offset, err = modToPrintf(s[j+2 : j+2+sep])
  103. if err != "" {
  104. return err
  105. }
  106. j += 2 + sep // Jump to it
  107. }
  108. dom.WriteString(fmt.Sprintf(mod, i+offset))
  109. default:
  110. if escape { // Pretty useless here
  111. escape = false
  112. continue
  113. }
  114. dom.WriteByte(s[j])
  115. }
  116. }
  117. // Re-parse the RR and send it on the current channel t
  118. rx, e := NewRR("$ORIGIN " + o + "\n" + dom.String())
  119. if e != nil {
  120. return e.(*ParseError).err
  121. }
  122. t <- &Token{RR: rx}
  123. // Its more efficient to first built the rrlist and then parse it in
  124. // one go! But is this a problem?
  125. }
  126. return ""
  127. }
  128. // Convert a $GENERATE modifier 0,0,d to something Printf can deal with.
  129. func modToPrintf(s string) (string, int, string) {
  130. xs := strings.SplitN(s, ",", 3)
  131. if len(xs) != 3 {
  132. return "", 0, "bad modifier in $GENERATE"
  133. }
  134. // xs[0] is offset, xs[1] is width, xs[2] is base
  135. if xs[2] != "o" && xs[2] != "d" && xs[2] != "x" && xs[2] != "X" {
  136. return "", 0, "bad base in $GENERATE"
  137. }
  138. offset, err := strconv.Atoi(xs[0])
  139. if err != nil || offset > 255 {
  140. return "", 0, "bad offset in $GENERATE"
  141. }
  142. width, err := strconv.Atoi(xs[1])
  143. if err != nil || width > 255 {
  144. return "", offset, "bad width in $GENERATE"
  145. }
  146. switch {
  147. case width < 0:
  148. return "", offset, "bad width in $GENERATE"
  149. case width == 0:
  150. return "%" + xs[1] + xs[2], offset, ""
  151. }
  152. return "%0" + xs[1] + xs[2], offset, ""
  153. }