Home | History | Annotate | Download | only in DNS
      1 // Copyright (c) 2004 Brian Wellington (bwelling (at) xbill.org)
      2 
      3 package org.xbill.DNS;
      4 
      5 import java.io.*;
      6 import java.util.*;
      7 
      8 /**
      9  * A representation of a $GENERATE statement in a master file.
     10  *
     11  * @author Brian Wellington
     12  */
     13 
     14 public class Generator {
     15 
     16 /** The start of the range. */
     17 public long start;
     18 
     19 /** The end of the range. */
     20 public long end;
     21 
     22 /** The step value of the range. */
     23 public long step;
     24 
     25 /** The pattern to use for generating record names. */
     26 public final String namePattern;
     27 
     28 /** The type of the generated records. */
     29 public final int type;
     30 
     31 /** The class of the generated records. */
     32 public final int dclass;
     33 
     34 /** The ttl of the generated records. */
     35 public final long ttl;
     36 
     37 /** The pattern to use for generating record data. */
     38 public final String rdataPattern;
     39 
     40 /** The origin to append to relative names. */
     41 public final Name origin;
     42 
     43 private long current;
     44 
     45 /**
     46  * Indicates whether generation is supported for this type.
     47  * @throws InvalidTypeException The type is out of range.
     48  */
     49 public static boolean
     50 supportedType(int type) {
     51 	Type.check(type);
     52 	return (type == Type.PTR || type == Type.CNAME || type == Type.DNAME ||
     53 		type == Type.A || type == Type.AAAA || type == Type.NS);
     54 }
     55 
     56 /**
     57  * Creates a specification for generating records, as a $GENERATE
     58  * statement in a master file.
     59  * @param start The start of the range.
     60  * @param end The end of the range.
     61  * @param step The step value of the range.
     62  * @param namePattern The pattern to use for generating record names.
     63  * @param type The type of the generated records.  The supported types are
     64  * PTR, CNAME, DNAME, A, AAAA, and NS.
     65  * @param dclass The class of the generated records.
     66  * @param ttl The ttl of the generated records.
     67  * @param rdataPattern The pattern to use for generating record data.
     68  * @param origin The origin to append to relative names.
     69  * @throws IllegalArgumentException The range is invalid.
     70  * @throws IllegalArgumentException The type does not support generation.
     71  * @throws IllegalArgumentException The dclass is not a valid class.
     72  */
     73 public
     74 Generator(long start, long end, long step, String namePattern,
     75 	  int type, int dclass, long ttl, String rdataPattern, Name origin)
     76 {
     77 	if (start < 0 || end < 0 || start > end || step <= 0)
     78 		throw new IllegalArgumentException
     79 				("invalid range specification");
     80 	if (!supportedType(type))
     81 		throw new IllegalArgumentException("unsupported type");
     82 	DClass.check(dclass);
     83 
     84 	this.start = start;
     85 	this.end = end;
     86 	this.step = step;
     87 	this.namePattern = namePattern;
     88 	this.type = type;
     89 	this.dclass = dclass;
     90 	this.ttl = ttl;
     91 	this.rdataPattern = rdataPattern;
     92 	this.origin = origin;
     93 	this.current = start;
     94 }
     95 
     96 private String
     97 substitute(String spec, long n) throws IOException {
     98 	boolean escaped = false;
     99 	byte [] str = spec.getBytes();
    100 	StringBuffer sb = new StringBuffer();
    101 
    102 	for (int i = 0; i < str.length; i++) {
    103 		char c = (char)(str[i] & 0xFF);
    104 		if (escaped) {
    105 			sb.append(c);
    106 			escaped = false;
    107 		} else if (c == '\\') {
    108 			if (i + 1 == str.length)
    109 				throw new TextParseException
    110 						("invalid escape character");
    111 			escaped = true;
    112 		} else if (c == '$') {
    113 			boolean negative = false;
    114 			long offset = 0;
    115 			long width = 0;
    116 			long base = 10;
    117 			boolean wantUpperCase = false;
    118 			if (i + 1 < str.length && str[i + 1] == '$') {
    119 				// '$$' == literal '$' for backwards
    120 				// compatibility with old versions of BIND.
    121 				c = (char)(str[++i] & 0xFF);
    122 				sb.append(c);
    123 				continue;
    124 			} else if (i + 1 < str.length && str[i + 1] == '{') {
    125 				// It's a substitution with modifiers.
    126 				i++;
    127 				if (i + 1 < str.length && str[i + 1] == '-') {
    128 					negative = true;
    129 					i++;
    130 				}
    131 				while (i + 1 < str.length) {
    132 					c = (char)(str[++i] & 0xFF);
    133 					if (c == ',' || c == '}')
    134 						break;
    135 					if (c < '0' || c > '9')
    136 						throw new TextParseException(
    137 							"invalid offset");
    138 					c -= '0';
    139 					offset *= 10;
    140 					offset += c;
    141 				}
    142 				if (negative)
    143 					offset = -offset;
    144 
    145 				if (c == ',') {
    146 					while (i + 1 < str.length) {
    147 						c = (char)(str[++i] & 0xFF);
    148 						if (c == ',' || c == '}')
    149 							break;
    150 						if (c < '0' || c > '9')
    151 							throw new
    152 							   TextParseException(
    153 							   "invalid width");
    154 						c -= '0';
    155 						width *= 10;
    156 						width += c;
    157 					}
    158 				}
    159 
    160 				if (c == ',') {
    161 					if  (i + 1 == str.length)
    162 						throw new TextParseException(
    163 							   "invalid base");
    164 					c = (char)(str[++i] & 0xFF);
    165 					if (c == 'o')
    166 						base = 8;
    167 					else if (c == 'x')
    168 						base = 16;
    169 					else if (c == 'X') {
    170 						base = 16;
    171 						wantUpperCase = true;
    172 					}
    173 					else if (c != 'd')
    174 						throw new TextParseException(
    175 							   "invalid base");
    176 				}
    177 
    178 				if (i + 1 == str.length || str[i + 1] != '}')
    179 					throw new TextParseException
    180 						("invalid modifiers");
    181 				i++;
    182 			}
    183 			long v = n + offset;
    184 			if (v < 0)
    185 				throw new TextParseException
    186 						("invalid offset expansion");
    187 			String number;
    188 			if (base == 8)
    189 				number = Long.toOctalString(v);
    190 			else if (base == 16)
    191 				number = Long.toHexString(v);
    192 			else
    193 				number = Long.toString(v);
    194 			if (wantUpperCase)
    195 				number = number.toUpperCase();
    196 			if (width != 0 && width > number.length()) {
    197 				int zeros = (int)width - number.length();
    198 				while (zeros-- > 0)
    199 					sb.append('0');
    200 			}
    201 			sb.append(number);
    202 		} else {
    203 			sb.append(c);
    204 		}
    205 	}
    206 	return sb.toString();
    207 }
    208 
    209 /**
    210  * Constructs and returns the next record in the expansion.
    211  * @throws IOException The name or rdata was invalid after substitutions were
    212  * performed.
    213  */
    214 public Record
    215 nextRecord() throws IOException {
    216 	if (current > end)
    217 		return null;
    218 	String namestr = substitute(namePattern, current);
    219 	Name name = Name.fromString(namestr, origin);
    220 	String rdata = substitute(rdataPattern, current);
    221 	current += step;
    222 	return Record.fromString(name, type, dclass, ttl, rdata, origin);
    223 }
    224 
    225 /**
    226  * Constructs and returns all records in the expansion.
    227  * @throws IOException The name or rdata of a record was invalid after
    228  * substitutions were performed.
    229  */
    230 public Record []
    231 expand() throws IOException {
    232 	List list = new ArrayList();
    233 	for (long i = start; i < end; i += step) {
    234 		String namestr = substitute(namePattern, current);
    235 		Name name = Name.fromString(namestr, origin);
    236 		String rdata = substitute(rdataPattern, current);
    237 		list.add(Record.fromString(name, type, dclass, ttl,
    238 					   rdata, origin));
    239 	}
    240 	return (Record []) list.toArray(new Record[list.size()]);
    241 }
    242 
    243 /**
    244  * Converts the generate specification to a string containing the corresponding
    245  * $GENERATE statement.
    246  */
    247 public String
    248 toString() {
    249 	StringBuffer sb = new StringBuffer();
    250 	sb.append("$GENERATE ");
    251 	sb.append(start + "-" + end);
    252 	if (step > 1)
    253 		sb.append("/" + step);
    254 	sb.append(" ");
    255 	sb.append(namePattern + " ");
    256 	sb.append(ttl + " ");
    257 	if (dclass != DClass.IN || !Options.check("noPrintIN"))
    258 		sb.append(DClass.string(dclass) + " ");
    259 	sb.append(Type.string(type) + " ");
    260 	sb.append(rdataPattern + " ");
    261 	return sb.toString();
    262 }
    263 
    264 }
    265