589 lines · 19.0 KB · Java
RawDownload
/* * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jndi.dns; import javax.naming.InvalidNameException;  /** * The ResourceRecord class represents a DNS resource record. * The string format is based on the master file representation in * RFC 1035. * * @author Scott Seligman */  public class ResourceRecord {     /*     * Resource record type codes     */    static final int TYPE_A     =  1;    static final int TYPE_NS    =  2;    static final int TYPE_CNAME =  5;    static final int TYPE_SOA   =  6;    static final int TYPE_PTR   = 12;    static final int TYPE_HINFO = 13;    static final int TYPE_MX    = 15;    static final int TYPE_TXT   = 16;    static final int TYPE_AAAA  = 28;    static final int TYPE_SRV   = 33;    static final int TYPE_NAPTR = 35;    static final int QTYPE_AXFR = 252;          // zone transfer    static final int QTYPE_STAR = 255;          // query type "*"     /*     * Mapping from resource record type codes to type name strings.     */    static final String rrTypeNames[] = {        null, "A", "NS", null, null,        "CNAME", "SOA", null, null, null,        null, null, "PTR", "HINFO", null,        "MX", "TXT", null, null, null,        null, null, null, null, null,        null, null, null, "AAAA", null,        null, null, null, "SRV", null,        "NAPTR"    };     /*     * Resource record class codes     */    static final int CLASS_INTERNET = 1;    static final int CLASS_HESIOD   = 2;    static final int QCLASS_STAR    = 255;      // query class "*"     /*     * Mapping from resource record type codes to class name strings.     */    static final String rrClassNames[] = {        null, "IN", null, null, "HS"    };      byte[] msg;                 // DNS message    int msgLen;                 // msg size (in octets)    boolean qSection;           // true if this RR is part of question section                                // and therefore has no ttl or rdata    int offset;                 // offset of RR w/in msg    int rrlen;                  // number of octets in encoded RR    DnsName name;               // name field of RR, including root label    int rrtype;                 // type field of RR    String rrtypeName;          // name of of rrtype    int rrclass;                // class field of RR    String rrclassName;         // name of rrclass    int ttl = 0;                // ttl field of RR    int rdlen = 0;              // number of octets of rdata    Object rdata = null;        // rdata -- most are String, unknown are byte[]      /*     * Constructs a new ResourceRecord.  The encoded data of the DNS     * message is contained in msg; data for this RR begins at msg[offset].     * If qSection is true this RR is part of a question section.  It's     * not a true resource record in that case, but is treated as if it     * were a shortened one (with no ttl or rdata).  If decodeRdata is     * false, the rdata is not decoded (and getRdata() will return null)     * unless this is an SOA record.     *     * @throws InvalidNameException if a decoded domain name isn't valid.     * @throws ArrayIndexOutOfBoundsException given certain other corrupt data.     */    ResourceRecord(byte[] msg, int msgLen, int offset,                   boolean qSection, boolean decodeRdata)            throws InvalidNameException {         this.msg = msg;        this.msgLen = msgLen;        this.offset = offset;        this.qSection = qSection;        decode(decodeRdata);    }     public String toString() {        String text = name + " " + rrclassName + " " + rrtypeName;        if (!qSection) {            text += " " + ttl + " " +                ((rdata != null) ? rdata : "[n/a]");        }        return text;    }     /*     * Returns the name field of this RR, including the root label.     */    public DnsName getName() {        return name;    }     /*     * Returns the number of octets in the encoded RR.     */    public int size() {        return rrlen;    }     public int getType() {        return rrtype;    }     public int getRrclass() {        return rrclass;    }     public Object getRdata() {        return rdata;    }      public static String getTypeName(int rrtype) {        return valueToName(rrtype, rrTypeNames);    }     public static int getType(String typeName) {        return nameToValue(typeName, rrTypeNames);    }     public static String getRrclassName(int rrclass) {        return valueToName(rrclass, rrClassNames);    }     public static int getRrclass(String className) {        return nameToValue(className, rrClassNames);    }     private static String valueToName(int val, String[] names) {        String name = null;        if ((val > 0) && (val < names.length)) {            name = names[val];        } else if (val == QTYPE_STAR) {         // QTYPE_STAR == QCLASS_STAR            name = "*";        }        if (name == null) {            name = Integer.toString(val);        }        return name;    }     private static int nameToValue(String name, String[] names) {        if (name.equals("")) {            return -1;                          // invalid name        } else if (name.equals("*")) {            return QTYPE_STAR;                  // QTYPE_STAR == QCLASS_STAR        }        if (Character.isDigit(name.charAt(0))) {            try {                return Integer.parseInt(name);            } catch (NumberFormatException e) {            }        }        for (int i = 1; i < names.length; i++) {            if ((names[i] != null) &&                    name.equalsIgnoreCase(names[i])) {                return i;            }        }        return -1;                              // unknown name    }     /*     * Compares two SOA record serial numbers using 32-bit serial number     * arithmetic as defined in RFC 1982.  Serial numbers are unsigned     * 32-bit quantities.  Returns a negative, zero, or positive value     * as the first serial number is less than, equal to, or greater     * than the second.  If the serial numbers are not comparable the     * result is undefined.  Note that the relation is not transitive.     */    public static int compareSerialNumbers(long s1, long s2) {        long diff = s2 - s1;        if (diff == 0) {            return 0;        } else if ((diff > 0 &&  diff <= 0x7FFFFFFF) ||                   (diff < 0 && -diff >  0x7FFFFFFF)) {            return -1;        } else {            return 1;        }    }      /*     * Decodes the binary format of the RR.     * May throw ArrayIndexOutOfBoundsException given corrupt data.     */    private void decode(boolean decodeRdata) throws InvalidNameException {        int pos = offset;       // index of next unread octet         name = new DnsName();                           // NAME        pos = decodeName(pos, name);         rrtype = getUShort(pos);                        // TYPE        rrtypeName = (rrtype < rrTypeNames.length)            ? rrTypeNames[rrtype]            : null;        if (rrtypeName == null) {            rrtypeName = Integer.toString(rrtype);        }        pos += 2;         rrclass = getUShort(pos);                       // CLASS        rrclassName = (rrclass < rrClassNames.length)            ? rrClassNames[rrclass]            : null;        if (rrclassName == null) {            rrclassName = Integer.toString(rrclass);        }        pos += 2;         if (!qSection) {            ttl = getInt(pos);                          // TTL            pos += 4;             rdlen = getUShort(pos);                     // RDLENGTH            pos += 2;             rdata = (decodeRdata ||                     // RDATA                     (rrtype == TYPE_SOA))                ? decodeRdata(pos)                : null;            if (rdata instanceof DnsName) {                rdata = rdata.toString();            }            pos += rdlen;        }         rrlen = pos - offset;         msg = null;     // free up for GC    }     /*     * Returns the 1-byte unsigned value at msg[pos].     */    private int getUByte(int pos) {        return (msg[pos] & 0xFF);    }     /*     * Returns the 2-byte unsigned value at msg[pos].  The high     * order byte comes first.     */    private int getUShort(int pos) {        return (((msg[pos] & 0xFF) << 8) |                (msg[pos + 1] & 0xFF));    }     /*     * Returns the 4-byte signed value at msg[pos].  The high     * order byte comes first.     */    private int getInt(int pos) {        return ((getUShort(pos) << 16) | getUShort(pos + 2));    }     /*     * Returns the 4-byte unsigned value at msg[pos].  The high     * order byte comes first.     */    private long getUInt(int pos) {        return (getInt(pos) & 0xffffffffL);    }     /*     * Returns the name encoded at msg[pos], including the root label.     */    private DnsName decodeName(int pos) throws InvalidNameException {        DnsName n = new DnsName();        decodeName(pos, n);        return n;    }     /*     * Prepends to "n" the domain name encoded at msg[pos], including the root     * label.  Returns the index into "msg" following the name.     */    private int decodeName(int pos, DnsName n) throws InvalidNameException {        if (msg[pos] == 0) {                            // end of name            n.add(0, "");            return (pos + 1);        } else if ((msg[pos] & 0xC0) != 0) {            // name compression            decodeName(getUShort(pos) & 0x3FFF, n);            return (pos + 2);        } else {                                        // append a label            int len = msg[pos++];            try {                n.add(0, new String(msg, pos, len, "ISO-8859-1"));            } catch (java.io.UnsupportedEncodingException e) {                // assert false : "ISO-Latin-1 charset unavailable";            }            return decodeName(pos + len, n);        }    }     /*     * Returns the rdata encoded at msg[pos].  The format is dependent     * on the rrtype and rrclass values, which have already been set.     * The length of the encoded data is rdlen, which has already been     * set.     * The rdata of records with unknown type/class combinations is     * returned in a newly-allocated byte array.     */    private Object decodeRdata(int pos) throws InvalidNameException {        if (rrclass == CLASS_INTERNET) {            switch (rrtype) {            case TYPE_A:                return decodeA(pos);            case TYPE_AAAA:                return decodeAAAA(pos);            case TYPE_CNAME:            case TYPE_NS:            case TYPE_PTR:                return decodeName(pos);            case TYPE_MX:                return decodeMx(pos);            case TYPE_SOA:                return decodeSoa(pos);            case TYPE_SRV:                return decodeSrv(pos);            case TYPE_NAPTR:                return decodeNaptr(pos);            case TYPE_TXT:                return decodeTxt(pos);            case TYPE_HINFO:                return decodeHinfo(pos);            }        }        // Unknown RR type/class        byte[] rd = new byte[rdlen];        System.arraycopy(msg, pos, rd, 0, rdlen);        return rd;    }     /*     * Returns the rdata of an MX record that is encoded at msg[pos].     */    private String decodeMx(int pos) throws InvalidNameException {        int preference = getUShort(pos);        pos += 2;        DnsName name = decodeName(pos);        return (preference + " " + name);    }     /*     * Returns the rdata of an SOA record that is encoded at msg[pos].     */    private String decodeSoa(int pos) throws InvalidNameException {        DnsName mname = new DnsName();        pos = decodeName(pos, mname);        DnsName rname = new DnsName();        pos = decodeName(pos, rname);         long serial = getUInt(pos);        pos += 4;        long refresh = getUInt(pos);        pos += 4;        long retry = getUInt(pos);        pos += 4;        long expire = getUInt(pos);        pos += 4;        long minimum = getUInt(pos);    // now used as negative TTL        pos += 4;         return (mname + " " + rname + " " + serial + " " +                refresh + " " + retry + " " + expire + " " + minimum);    }     /*     * Returns the rdata of an SRV record that is encoded at msg[pos].     * See RFC 2782.     */    private String decodeSrv(int pos) throws InvalidNameException {        int priority = getUShort(pos);        pos += 2;        int weight =   getUShort(pos);        pos += 2;        int port =     getUShort(pos);        pos += 2;        DnsName target = decodeName(pos);        return (priority + " " + weight + " " + port + " " + target);    }     /*     * Returns the rdata of an NAPTR record that is encoded at msg[pos].     * See RFC 2915.     */    private String decodeNaptr(int pos) throws InvalidNameException {        int order = getUShort(pos);        pos += 2;        int preference = getUShort(pos);        pos += 2;        StringBuffer flags = new StringBuffer();        pos += decodeCharString(pos, flags);        StringBuffer services = new StringBuffer();        pos += decodeCharString(pos, services);        StringBuffer regexp = new StringBuffer(rdlen);        pos += decodeCharString(pos, regexp);        DnsName replacement = decodeName(pos);         return (order + " " + preference + " " + flags + " " +                services + " " + regexp + " " + replacement);    }     /*     * Returns the rdata of a TXT record that is encoded at msg[pos].     * The rdata consists of one or more <character-string>s.     */    private String decodeTxt(int pos) {        StringBuffer buf = new StringBuffer(rdlen);        int end = pos + rdlen;        while (pos < end) {            pos += decodeCharString(pos, buf);            if (pos < end) {                buf.append(' ');            }        }        return buf.toString();    }     /*     * Returns the rdata of an HINFO record that is encoded at msg[pos].     * The rdata consists of two <character-string>s.     */    private String decodeHinfo(int pos) {        StringBuffer buf = new StringBuffer(rdlen);        pos += decodeCharString(pos, buf);        buf.append(' ');        pos += decodeCharString(pos, buf);        return buf.toString();    }     /*     * Decodes the <character-string> at msg[pos] and adds it to buf.     * If the string contains one of the meta-characters ' ', '\\', or     * '"', then the result is quoted and any embedded '\\' or '"'     * chars are escaped with '\\'.  Empty strings are also quoted.     * Returns the size of the encoded string, including the initial     * length octet.     */    private int decodeCharString(int pos, StringBuffer buf) {        int start = buf.length();       // starting index of this string        int len = getUByte(pos++);      // encoded string length        boolean quoted = (len == 0);    // quote string if empty        for (int i = 0; i < len; i++) {            int c = getUByte(pos++);            quoted |= (c == ' ');            if ((c == '\\') || (c == '"')) {                quoted = true;

Lines 1–500 of 589.