/* * Copied from the DnsJava project * * Copyright (c) 1998-2011, Brian Wellington. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package io.milton.dns.record; import io.milton.dns.Address; import io.milton.dns.Name; import io.milton.dns.utils.base16; import java.io.*; import java.net.*; import java.util.*; /** * 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() { StringBuilder sb = new StringBuilder(); 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() { StringBuilder sb = new StringBuilder(); 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); } } }