15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)package org.xbill.DNS; 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)import java.io.*; 6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)import java.util.*; 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * A representation of a $GENERATE statement in a master file. 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @author Brian Wellington 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public class Generator { 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The start of the range. */ 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public long start; 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The end of the range. */ 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public long end; 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The step value of the range. */ 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public long step; 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The pattern to use for generating record names. */ 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final String namePattern; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The type of the generated records. */ 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final int type; 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The class of the generated records. */ 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final int dclass; 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The ttl of the generated records. */ 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final long ttl; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** The pattern to use for generating record data. */ 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)public final String rdataPattern; 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/** The origin to append to relative names. */ 4146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)public final Name origin; 4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)private long current; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Indicates whether generation is supported for this type. 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @throws InvalidTypeException The type is out of range. 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)public static boolean 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)supportedType(int type) { 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Type.check(type); 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return (type == Type.PTR || type == Type.CNAME || type == Type.DNAME || 531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) type == Type.A || type == Type.AAAA || type == Type.NS); 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/** 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Creates a specification for generating records, as a $GENERATE 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * statement in a master file. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param start The start of the range. 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param end The end of the range. 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param step The step value of the range. 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param namePattern The pattern to use for generating record names. 63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param type The type of the generated records. The supported types are 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * PTR, CNAME, DNAME, A, AAAA, and NS. 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param dclass The class of the generated records. 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param ttl The ttl of the generated records. 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param rdataPattern The pattern to use for generating record data. 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param origin The origin to append to relative names. 69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @throws IllegalArgumentException The range is invalid. 70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @throws IllegalArgumentException The type does not support generation. 71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @throws IllegalArgumentException The dclass is not a valid class. 72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)public 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Generator(long start, long end, long step, String namePattern, 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int type, int dclass, long ttl, String rdataPattern, Name origin) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){ 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (start < 0 || end < 0 || start > end || step <= 0) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new IllegalArgumentException 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("invalid range specification"); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!supportedType(type)) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new IllegalArgumentException("unsupported type"); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DClass.check(dclass); 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.start = start; 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.end = end; 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.step = step; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.namePattern = namePattern; 88010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) this.type = type; 89010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) this.dclass = dclass; 90010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) this.ttl = ttl; 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.rdataPattern = rdataPattern; 927d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) this.origin = origin; 93a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) this.current = start; 94c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch} 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)private String 970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochsubstitute(String spec, long n) throws IOException { 980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch boolean escaped = false; 990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch byte [] str = spec.getBytes(); 1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu StringBuffer sb = new StringBuffer(); 1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu for (int i = 0; i < str.length; i++) { 10346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) char c = (char)(str[i] & 0xFF); 10446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if (escaped) { 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sb.append(c); 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) escaped = false; 1070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch } else if (c == '\\') { 1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if (i + 1 == str.length) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new TextParseException 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ("invalid escape character"); 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) escaped = true; 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } else if (c == '$') { 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) boolean negative = false; 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) long offset = 0; 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) long width = 0; 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) long base = 10; 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) boolean wantUpperCase = false; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (i + 1 < str.length && str[i + 1] == '$') { 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // '$$' == literal '$' for backwards 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // compatibility with old versions of BIND. 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c = (char)(str[++i] & 0xFF); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sb.append(c); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (i + 1 < str.length && str[i + 1] == '{') { 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It's a substitution with modifiers. 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i++; 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (i + 1 < str.length && str[i + 1] == '-') { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) negative = true; 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) i++; 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (i + 1 < str.length) { 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c = (char)(str[++i] & 0xFF); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (c == ',' || c == '}') 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (c < '0' || c > '9') 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new TextParseException( 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "invalid offset"); 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) c -= '0'; 139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch offset *= 10; 140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch offset += c; 141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (negative) 143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch offset = -offset; 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (c == ',') { 146d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 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 */ 214public Record 215nextRecord() 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 */ 230public Record [] 231expand() 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 */ 247public String 248toString() { 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