1// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.*;
6import java.net.*;
7import java.util.*;
8import 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
21public class APLRecord extends Record {
22
23public 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
88private static final long serialVersionUID = -1348173791712935864L;
89
90private List elements;
91
92APLRecord() {}
93
94Record
95getObject() {
96	return new APLRecord();
97}
98
99private static boolean
100validatePrefixLength(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 */
113public
114APLRecord(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
133private static byte []
134parseAddress(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
144void
145rrFromWire(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
173void
174rdataFromString(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
233String
234rrToString() {
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. */
246public List
247getElements() {
248	return elements;
249}
250
251private static int
252addressLength(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
260void
261rrToWire(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