1 // Copyright (c) 2004 Brian Wellington (bwelling (at) xbill.org) 2 3 package org.xbill.DNS; 4 5 import java.io.*; 6 import java.net.*; 7 import java.util.*; 8 import org.xbill.DNS.utils.*; 9 10 /** 11 * APL - Address Prefix List. See RFC 3123. 12 * 13 * @author Brian Wellington 14 */ 15 16 /* 17 * Note: this currently uses the same constants as the Address class; 18 * this could change if more constants are defined for APL records. 19 */ 20 21 public class APLRecord extends Record { 22 23 public static class Element { 24 public final int family; 25 public final boolean negative; 26 public final int prefixLength; 27 public final Object address; 28 29 private 30 Element(int family, boolean negative, Object address, int prefixLength) 31 { 32 this.family = family; 33 this.negative = negative; 34 this.address = address; 35 this.prefixLength = prefixLength; 36 if (!validatePrefixLength(family, prefixLength)) { 37 throw new IllegalArgumentException("invalid prefix " + 38 "length"); 39 } 40 } 41 42 /** 43 * Creates an APL element corresponding to an IPv4 or IPv6 prefix. 44 * @param negative Indicates if this prefix is a negation. 45 * @param address The IPv4 or IPv6 address. 46 * @param prefixLength The length of this prefix, in bits. 47 * @throws IllegalArgumentException The prefix length is invalid. 48 */ 49 public 50 Element(boolean negative, InetAddress address, int prefixLength) { 51 this(Address.familyOf(address), negative, address, 52 prefixLength); 53 } 54 55 public String 56 toString() { 57 StringBuffer sb = new StringBuffer(); 58 if (negative) 59 sb.append("!"); 60 sb.append(family); 61 sb.append(":"); 62 if (family == Address.IPv4 || family == Address.IPv6) 63 sb.append(((InetAddress) address).getHostAddress()); 64 else 65 sb.append(base16.toString((byte []) address)); 66 sb.append("/"); 67 sb.append(prefixLength); 68 return sb.toString(); 69 } 70 71 public boolean 72 equals(Object arg) { 73 if (arg == null || !(arg instanceof Element)) 74 return false; 75 Element elt = (Element) arg; 76 return (family == elt.family && 77 negative == elt.negative && 78 prefixLength == elt.prefixLength && 79 address.equals(elt.address)); 80 } 81 82 public int 83 hashCode() { 84 return address.hashCode() + prefixLength + (negative ? 1 : 0); 85 } 86 } 87 88 private static final long serialVersionUID = -1348173791712935864L; 89 90 private List elements; 91 92 APLRecord() {} 93 94 Record 95 getObject() { 96 return new APLRecord(); 97 } 98 99 private static boolean 100 validatePrefixLength(int family, int prefixLength) { 101 if (prefixLength < 0 || prefixLength >= 256) 102 return false; 103 if ((family == Address.IPv4 && prefixLength > 32) || 104 (family == Address.IPv6 && prefixLength > 128)) 105 return false; 106 return true; 107 } 108 109 /** 110 * Creates an APL Record from the given data. 111 * @param elements The list of APL elements. 112 */ 113 public 114 APLRecord(Name name, int dclass, long ttl, List elements) { 115 super(name, Type.APL, dclass, ttl); 116 this.elements = new ArrayList(elements.size()); 117 for (Iterator it = elements.iterator(); it.hasNext(); ) { 118 Object o = it.next(); 119 if (!(o instanceof Element)) { 120 throw new IllegalArgumentException("illegal element"); 121 } 122 Element element = (Element) o; 123 if (element.family != Address.IPv4 && 124 element.family != Address.IPv6) 125 { 126 throw new IllegalArgumentException("unknown family"); 127 } 128 this.elements.add(element); 129 130 } 131 } 132 133 private static byte [] 134 parseAddress(byte [] in, int length) throws WireParseException { 135 if (in.length > length) 136 throw new WireParseException("invalid address length"); 137 if (in.length == length) 138 return in; 139 byte [] out = new byte[length]; 140 System.arraycopy(in, 0, out, 0, in.length); 141 return out; 142 } 143 144 void 145 rrFromWire(DNSInput in) throws IOException { 146 elements = new ArrayList(1); 147 while (in.remaining() != 0) { 148 int family = in.readU16(); 149 int prefix = in.readU8(); 150 int length = in.readU8(); 151 boolean negative = (length & 0x80) != 0; 152 length &= ~0x80; 153 154 byte [] data = in.readByteArray(length); 155 Element element; 156 if (!validatePrefixLength(family, prefix)) { 157 throw new WireParseException("invalid prefix length"); 158 } 159 160 if (family == Address.IPv4 || family == Address.IPv6) { 161 data = parseAddress(data, 162 Address.addressLength(family)); 163 InetAddress addr = InetAddress.getByAddress(data); 164 element = new Element(negative, addr, prefix); 165 } else { 166 element = new Element(family, negative, data, prefix); 167 } 168 elements.add(element); 169 170 } 171 } 172 173 void 174 rdataFromString(Tokenizer st, Name origin) throws IOException { 175 elements = new ArrayList(1); 176 while (true) { 177 Tokenizer.Token t = st.get(); 178 if (!t.isString()) 179 break; 180 181 boolean negative = false; 182 int family = 0; 183 int prefix = 0; 184 185 String s = t.value; 186 int start = 0; 187 if (s.startsWith("!")) { 188 negative = true; 189 start = 1; 190 } 191 int colon = s.indexOf(':', start); 192 if (colon < 0) 193 throw st.exception("invalid address prefix element"); 194 int slash = s.indexOf('/', colon); 195 if (slash < 0) 196 throw st.exception("invalid address prefix element"); 197 198 String familyString = s.substring(start, colon); 199 String addressString = s.substring(colon + 1, slash); 200 String prefixString = s.substring(slash + 1); 201 202 try { 203 family = Integer.parseInt(familyString); 204 } 205 catch (NumberFormatException e) { 206 throw st.exception("invalid family"); 207 } 208 if (family != Address.IPv4 && family != Address.IPv6) 209 throw st.exception("unknown family"); 210 211 try { 212 prefix = Integer.parseInt(prefixString); 213 } 214 catch (NumberFormatException e) { 215 throw st.exception("invalid prefix length"); 216 } 217 218 if (!validatePrefixLength(family, prefix)) { 219 throw st.exception("invalid prefix length"); 220 } 221 222 byte [] bytes = Address.toByteArray(addressString, family); 223 if (bytes == null) 224 throw st.exception("invalid IP address " + 225 addressString); 226 227 InetAddress address = InetAddress.getByAddress(bytes); 228 elements.add(new Element(negative, address, prefix)); 229 } 230 st.unget(); 231 } 232 233 String 234 rrToString() { 235 StringBuffer sb = new StringBuffer(); 236 for (Iterator it = elements.iterator(); it.hasNext(); ) { 237 Element element = (Element) it.next(); 238 sb.append(element); 239 if (it.hasNext()) 240 sb.append(" "); 241 } 242 return sb.toString(); 243 } 244 245 /** Returns the list of APL elements. */ 246 public List 247 getElements() { 248 return elements; 249 } 250 251 private static int 252 addressLength(byte [] addr) { 253 for (int i = addr.length - 1; i >= 0; i--) { 254 if (addr[i] != 0) 255 return i + 1; 256 } 257 return 0; 258 } 259 260 void 261 rrToWire(DNSOutput out, Compression c, boolean canonical) { 262 for (Iterator it = elements.iterator(); it.hasNext(); ) { 263 Element element = (Element) it.next(); 264 int length = 0; 265 byte [] data; 266 if (element.family == Address.IPv4 || 267 element.family == Address.IPv6) 268 { 269 InetAddress addr = (InetAddress) element.address; 270 data = addr.getAddress(); 271 length = addressLength(data); 272 } else { 273 data = (byte []) element.address; 274 length = data.length; 275 } 276 int wlength = length; 277 if (element.negative) { 278 wlength |= 0x80; 279 } 280 out.writeU16(element.family); 281 out.writeU8(element.prefixLength); 282 out.writeU8(wlength); 283 out.writeByteArray(data, 0, length); 284 } 285 } 286 287 } 288