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