...

Source file src/github.com/miekg/dns/generate.go

Documentation: github.com/miekg/dns

     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  	startStr, endStr, ok := strings.Cut(token, "-")
    39  	if !ok {
    40  		return zp.setParseError("bad start-stop in $GENERATE range", l)
    41  	}
    42  
    43  	start, err := strconv.ParseInt(startStr, 10, 64)
    44  	if err != nil {
    45  		return zp.setParseError("bad start in $GENERATE range", l)
    46  	}
    47  
    48  	end, err := strconv.ParseInt(endStr, 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  	offStr, s, ok0 := strings.Cut(s, ",")
   215  	widthStr, s, ok1 := strings.Cut(s, ",")
   216  	base, _, ok2 := strings.Cut(s, ",")
   217  	if !ok0 {
   218  		widthStr = "0"
   219  	}
   220  	if !ok1 {
   221  		base = "d"
   222  	}
   223  	if ok2 {
   224  		return "", 0, "bad modifier in $GENERATE"
   225  	}
   226  
   227  	switch base {
   228  	case "o", "d", "x", "X":
   229  	default:
   230  		return "", 0, "bad base in $GENERATE"
   231  	}
   232  
   233  	offset, err := strconv.ParseInt(offStr, 10, 64)
   234  	if err != nil {
   235  		return "", 0, "bad offset in $GENERATE"
   236  	}
   237  
   238  	width, err := strconv.ParseUint(widthStr, 10, 8)
   239  	if err != nil {
   240  		return "", 0, "bad width in $GENERATE"
   241  	}
   242  
   243  	if width == 0 {
   244  		return "%" + base, offset, ""
   245  	}
   246  
   247  	return "%0" + widthStr + base, offset, ""
   248  }
   249  

View as plain text