blob: 5940da238bc244f05af48dc80ff6365773c3a138 [file] [log] [blame]
// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org)
package org.xbill.DNS;
import java.io.*;
import java.net.*;
import java.util.*;
import org.xbill.DNS.utils.*;
/**
* APL - Address Prefix List. See RFC 3123.
*
* @author Brian Wellington
*/
/*
* Note: this currently uses the same constants as the Address class;
* this could change if more constants are defined for APL records.
*/
public class APLRecord extends Record {
public static class Element {
public final int family;
public final boolean negative;
public final int prefixLength;
public final Object address;
private
Element(int family, boolean negative, Object address, int prefixLength)
{
this.family = family;
this.negative = negative;
this.address = address;
this.prefixLength = prefixLength;
if (!validatePrefixLength(family, prefixLength)) {
throw new IllegalArgumentException("invalid prefix " +
"length");
}
}
/**
* Creates an APL element corresponding to an IPv4 or IPv6 prefix.
* @param negative Indicates if this prefix is a negation.
* @param address The IPv4 or IPv6 address.
* @param prefixLength The length of this prefix, in bits.
* @throws IllegalArgumentException The prefix length is invalid.
*/
public
Element(boolean negative, InetAddress address, int prefixLength) {
this(Address.familyOf(address), negative, address,
prefixLength);
}
public String
toString() {
StringBuffer sb = new StringBuffer();
if (negative)
sb.append("!");
sb.append(family);
sb.append(":");
if (family == Address.IPv4 || family == Address.IPv6)
sb.append(((InetAddress) address).getHostAddress());
else
sb.append(base16.toString((byte []) address));
sb.append("/");
sb.append(prefixLength);
return sb.toString();
}
public boolean
equals(Object arg) {
if (arg == null || !(arg instanceof Element))
return false;
Element elt = (Element) arg;
return (family == elt.family &&
negative == elt.negative &&
prefixLength == elt.prefixLength &&
address.equals(elt.address));
}
public int
hashCode() {
return address.hashCode() + prefixLength + (negative ? 1 : 0);
}
}
private static final long serialVersionUID = -1348173791712935864L;
private List elements;
APLRecord() {}
Record
getObject() {
return new APLRecord();
}
private static boolean
validatePrefixLength(int family, int prefixLength) {
if (prefixLength < 0 || prefixLength >= 256)
return false;
if ((family == Address.IPv4 && prefixLength > 32) ||
(family == Address.IPv6 && prefixLength > 128))
return false;
return true;
}
/**
* Creates an APL Record from the given data.
* @param elements The list of APL elements.
*/
public
APLRecord(Name name, int dclass, long ttl, List elements) {
super(name, Type.APL, dclass, ttl);
this.elements = new ArrayList(elements.size());
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Object o = it.next();
if (!(o instanceof Element)) {
throw new IllegalArgumentException("illegal element");
}
Element element = (Element) o;
if (element.family != Address.IPv4 &&
element.family != Address.IPv6)
{
throw new IllegalArgumentException("unknown family");
}
this.elements.add(element);
}
}
private static byte []
parseAddress(byte [] in, int length) throws WireParseException {
if (in.length > length)
throw new WireParseException("invalid address length");
if (in.length == length)
return in;
byte [] out = new byte[length];
System.arraycopy(in, 0, out, 0, in.length);
return out;
}
void
rrFromWire(DNSInput in) throws IOException {
elements = new ArrayList(1);
while (in.remaining() != 0) {
int family = in.readU16();
int prefix = in.readU8();
int length = in.readU8();
boolean negative = (length & 0x80) != 0;
length &= ~0x80;
byte [] data = in.readByteArray(length);
Element element;
if (!validatePrefixLength(family, prefix)) {
throw new WireParseException("invalid prefix length");
}
if (family == Address.IPv4 || family == Address.IPv6) {
data = parseAddress(data,
Address.addressLength(family));
InetAddress addr = InetAddress.getByAddress(data);
element = new Element(negative, addr, prefix);
} else {
element = new Element(family, negative, data, prefix);
}
elements.add(element);
}
}
void
rdataFromString(Tokenizer st, Name origin) throws IOException {
elements = new ArrayList(1);
while (true) {
Tokenizer.Token t = st.get();
if (!t.isString())
break;
boolean negative = false;
int family = 0;
int prefix = 0;
String s = t.value;
int start = 0;
if (s.startsWith("!")) {
negative = true;
start = 1;
}
int colon = s.indexOf(':', start);
if (colon < 0)
throw st.exception("invalid address prefix element");
int slash = s.indexOf('/', colon);
if (slash < 0)
throw st.exception("invalid address prefix element");
String familyString = s.substring(start, colon);
String addressString = s.substring(colon + 1, slash);
String prefixString = s.substring(slash + 1);
try {
family = Integer.parseInt(familyString);
}
catch (NumberFormatException e) {
throw st.exception("invalid family");
}
if (family != Address.IPv4 && family != Address.IPv6)
throw st.exception("unknown family");
try {
prefix = Integer.parseInt(prefixString);
}
catch (NumberFormatException e) {
throw st.exception("invalid prefix length");
}
if (!validatePrefixLength(family, prefix)) {
throw st.exception("invalid prefix length");
}
byte [] bytes = Address.toByteArray(addressString, family);
if (bytes == null)
throw st.exception("invalid IP address " +
addressString);
InetAddress address = InetAddress.getByAddress(bytes);
elements.add(new Element(negative, address, prefix));
}
st.unget();
}
String
rrToString() {
StringBuffer sb = new StringBuffer();
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Element element = (Element) it.next();
sb.append(element);
if (it.hasNext())
sb.append(" ");
}
return sb.toString();
}
/** Returns the list of APL elements. */
public List
getElements() {
return elements;
}
private static int
addressLength(byte [] addr) {
for (int i = addr.length - 1; i >= 0; i--) {
if (addr[i] != 0)
return i + 1;
}
return 0;
}
void
rrToWire(DNSOutput out, Compression c, boolean canonical) {
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Element element = (Element) it.next();
int length = 0;
byte [] data;
if (element.family == Address.IPv4 ||
element.family == Address.IPv6)
{
InetAddress addr = (InetAddress) element.address;
data = addr.getAddress();
length = addressLength(data);
} else {
data = (byte []) element.address;
length = data.length;
}
int wlength = length;
if (element.negative) {
wlength |= 0x80;
}
out.writeU16(element.family);
out.writeU8(element.prefixLength);
out.writeU8(wlength);
out.writeByteArray(data, 0, length);
}
}
}