Record.java

  1package de.measite.minidns;
  2
  3import java.io.DataInputStream;
  4import java.io.IOException;
  5import java.util.HashMap;
  6
  7import de.measite.minidns.record.A;
  8import de.measite.minidns.record.AAAA;
  9import de.measite.minidns.record.CNAME;
 10import de.measite.minidns.record.Data;
 11import de.measite.minidns.record.NS;
 12import de.measite.minidns.record.PTR;
 13import de.measite.minidns.record.SRV;
 14import de.measite.minidns.record.TXT;
 15import de.measite.minidns.util.NameUtil;
 16
 17/**
 18 * A generic DNS record.
 19 */
 20public class Record {
 21
 22    /**
 23     * The record type.
 24     * @see <a href="http://www.iana.org/assignments/dns-parameters">IANA DNS Parameters</a>
 25     */
 26    public static enum TYPE {
 27        A(1),
 28        NS(2),
 29        MD(3),
 30        MF(4),
 31        CNAME(5),
 32        SOA(6),
 33        MB(7),
 34        MG(8),
 35        MR(9),
 36        NULL(10),
 37        WKS(11),
 38        PTR(12),
 39        HINFO(13),
 40        MINFO(14),
 41        MX(15),
 42        TXT(16),
 43        RP(17),
 44        AFSDB(18),
 45        X25(19),
 46        ISDN(20),
 47        RT(21),
 48        NSAP(22),
 49        NSAP_PTR(23),
 50        SIG(24),
 51        KEY(25),
 52        PX(26),
 53        GPOS(27),
 54        AAAA(28),
 55        LOC(29),
 56        NXT(30),
 57        EID(31),
 58        NIMLOC(32),
 59        SRV(33),
 60        ATMA(34),
 61        NAPTR(35),
 62        KX(36),
 63        CERT(37),
 64        A6(38),
 65        DNAME(39),
 66        SINK(40),
 67        OPT(41),
 68        APL(42),
 69        DS(43),
 70        SSHFP(44),
 71        IPSECKEY(45),
 72        RRSIG(46),
 73        NSEC(47),
 74        DNSKEY(48),
 75        DHCID(49),
 76        NSEC3(50),
 77        NSEC3PARAM(51),
 78        HIP(55),
 79        NINFO(56),
 80        RKEY(57),
 81        TALINK(58),
 82        SPF(99),
 83        UINFO(100),
 84        UID(101),
 85        GID(102),
 86        TKEY(249),
 87        TSIG(250),
 88        IXFR(251),
 89        AXFR(252),
 90        MAILB(253),
 91        MAILA(254),
 92        ANY(255),
 93        TA(32768),
 94        DLV(32769);
 95
 96        /**
 97         * The value of this DNS record type.
 98         */
 99        private final int value;
100
101        /**
102         * Internal lookup table to map values to types.
103         */
104        private final static HashMap<Integer, TYPE> INVERSE_LUT =
105                                        new HashMap<Integer, TYPE>();
106
107        /**
108         * Initialize the reverse lookup table.
109         */
110        static {
111            for(TYPE t: TYPE.values()) {
112                INVERSE_LUT.put(t.getValue(), t);
113            }
114        }
115
116        /**
117         * Create a new record type.
118         * @param value The binary value of this type.
119         */
120        private TYPE(int value) {
121            this.value = value;
122        }
123
124        /**
125         * Retrieve the binary value of this type.
126         * @return The binary value.
127         */
128        public int getValue() {
129            return value;
130        }
131
132        /**
133         * Retrieve the symbolic type of the binary value.
134         * @param value The binary type value.
135         * @return The symbolic tpye.
136         */
137        public static TYPE getType(int value) {
138            return INVERSE_LUT.get(value);
139        }
140    };
141
142    /**
143     * The symbolic class of a DNS record (usually IN for Internet).
144     */
145    public static enum CLASS {
146        IN(1),
147        CH(3),
148        HS(4),
149        NONE(254),
150        ANY(255);
151
152        /**
153         * Internal reverse lookup table to map binary class values to symbolic
154         * names.
155         */
156        private final static HashMap<Integer, CLASS> INVERSE_LUT =
157                                            new HashMap<Integer, CLASS>();
158
159        /**
160         * Initialize the interal reverse lookup table.
161         */
162        static {
163            for(CLASS c: CLASS.values()) {
164                INVERSE_LUT.put(c.getValue(), c);
165            }
166        }
167
168        /**
169         * The binary value of this dns class.
170         */
171        private final int value;
172
173        /**
174         * Create a new DNS class based on a binary value.
175         * @param value The binary value of this DNS class.
176         */
177        private CLASS(int value) {
178            this.value = value;
179        }
180
181        /**
182         * Retrieve the binary value of this DNS class.
183         * @return The binary value of this DNS class.
184         */
185        public int getValue() {
186            return value;
187        }
188
189        /**
190         * Retrieve the symbolic DNS class for a binary class value.
191         * @param value The binary DNS class value.
192         * @return The symbolic class instance.
193         */
194        public static CLASS getClass(int value) {
195            return INVERSE_LUT.get(value);
196        }
197
198    }
199
200    /**
201     * The generic name of this record.
202     */
203    protected String name;
204
205    /**
206     * The type (and payload type) of this record.
207     */
208    protected TYPE type;
209
210    /**
211     * The record class (usually CLASS.IN).
212     */
213    protected CLASS clazz;
214
215    /**
216     * The ttl of this record.
217     */
218    protected long ttl;
219
220    /**
221     * The payload object of this record.
222     */
223    protected Data payloadData;
224
225    /**
226     * Parse a given record based on the full message data and the current
227     * stream position.
228     * @param dis The DataInputStream positioned at the first record byte.
229     * @param data The full message data.
230     * @throws IOException In case of malformed replies.
231     */
232    public void parse(DataInputStream dis, byte[] data) throws IOException {
233        this.name = NameUtil.parse(dis, data);
234        this.type = TYPE.getType(dis.readUnsignedShort());
235        int clazzValue = dis.readUnsignedShort();
236        this.clazz = CLASS.getClass(clazzValue);
237        if (this.clazz == null) {
238            System.out.println("Unknown class " + clazzValue);
239        }
240        this.ttl = (((long)dis.readUnsignedShort()) << 32) +
241                   dis.readUnsignedShort();
242        int payloadLength = dis.readUnsignedShort();
243        switch (this.type) {
244        case SRV:
245            this.payloadData = new SRV();
246            break;
247        case AAAA:
248            this.payloadData = new AAAA();
249            break;
250        case A:
251            this.payloadData = new A();
252            break;
253        case NS:
254            this.payloadData = new NS();
255            break;
256        case CNAME:
257            this.payloadData = new CNAME();
258            break;
259        case PTR:
260            this.payloadData = new PTR();
261            break;
262        case TXT:
263            this.payloadData = new TXT();
264            break;
265        default:
266            System.out.println("Unparsed type " + type);
267            this.payloadData = null;
268            for (int i = 0; i < payloadLength; i++) {
269                dis.readByte();
270            }
271            break;
272        }
273        if (this.payloadData != null) {
274            this.payloadData.parse(dis, data, payloadLength);
275        }
276    }
277
278    /**
279     * Retrieve a textual representation of this resource record.
280     * @return String
281     */
282    @Override
283    public String toString() {
284        if (payloadData == null) {
285            return "RR " + type + "/" + clazz;
286        }
287        return "RR " + type + "/" + clazz + ": " + payloadData.toString();
288    };
289
290    /**
291     * Check if this record answers a given query.
292     * @param q The query.
293     * @return True if this record is a valid answer.
294     */
295    public boolean isAnswer(Question q) {
296        return ((q.getType() == type) || (q.getType() == TYPE.ANY)) &&
297               ((q.getClazz() == clazz) || (q.getClazz() == CLASS.ANY)) &&
298               (q.getName().equals(name));
299    }
300
301    /**
302     * The generic record name, e.g. "measite.de".
303     * @return The record name.
304     */
305    public String getName() {
306        return name;
307    }
308
309    /**
310     * The payload data, usually a subclass of data (A, AAAA, CNAME, ...).
311     * @return The payload data.
312     */
313    public Data getPayload() {
314        return payloadData;
315    }
316
317    /**
318     * Retrieve the record ttl.
319     * @return The record ttl.
320     */
321    public long getTtl() {
322        return ttl;
323    }
324
325}