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     * MDNS defines the highest bit of the class as the unicast query bit.
227     */
228    protected boolean unicastQuery;
229
230    /**
231     * Parse a given record based on the full message data and the current
232     * stream position.
233     * @param dis The DataInputStream positioned at the first record byte.
234     * @param data The full message data.
235     * @throws IOException In case of malformed replies.
236     */
237    public void parse(DataInputStream dis, byte[] data) throws IOException {
238        this.name = NameUtil.parse(dis, data);
239        this.type = TYPE.getType(dis.readUnsignedShort());
240        int clazzValue = dis.readUnsignedShort();
241        this.clazz = CLASS.getClass(clazzValue & 0x7fff);
242        this.unicastQuery = (clazzValue & 0x8000) > 0;
243        if (this.clazz == null) {
244            System.out.println("Unknown class " + clazzValue);
245        }
246        this.ttl = (((long)dis.readUnsignedShort()) << 32) +
247                   dis.readUnsignedShort();
248        int payloadLength = dis.readUnsignedShort();
249        switch (this.type) {
250        case SRV:
251            this.payloadData = new SRV();
252            break;
253        case AAAA:
254            this.payloadData = new AAAA();
255            break;
256        case A:
257            this.payloadData = new A();
258            break;
259        case NS:
260            this.payloadData = new NS();
261            break;
262        case CNAME:
263            this.payloadData = new CNAME();
264            break;
265        case PTR:
266            this.payloadData = new PTR();
267            break;
268        case TXT:
269            this.payloadData = new TXT();
270            break;
271        default:
272            System.out.println("Unparsed type " + type);
273            this.payloadData = null;
274            for (int i = 0; i < payloadLength; i++) {
275                dis.readByte();
276            }
277            break;
278        }
279        if (this.payloadData != null) {
280            this.payloadData.parse(dis, data, payloadLength);
281        }
282    }
283
284    /**
285     * Retrieve a textual representation of this resource record.
286     * @return String
287     */
288    @Override
289    public String toString() {
290        if (payloadData == null) {
291            return "RR " + type + "/" + clazz;
292        }
293        return "RR " + type + "/" + clazz + ": " + payloadData.toString();
294    };
295
296    /**
297     * Check if this record answers a given query.
298     * @param q The query.
299     * @return True if this record is a valid answer.
300     */
301    public boolean isAnswer(Question q) {
302        return ((q.getType() == type) || (q.getType() == TYPE.ANY)) &&
303               ((q.getClazz() == clazz) || (q.getClazz() == CLASS.ANY)) &&
304               (q.getName().equals(name));
305    }
306
307    /**
308     * See if this query/response was a unicast query (highest class bit set).
309     * @return True if it is a unicast query/response record.
310     */
311    public boolean isUnicastQuery() {
312        return unicastQuery;
313    }
314
315    /**
316     * The generic record name, e.g. "measite.de".
317     * @return The record name.
318     */
319    public String getName() {
320        return name;
321    }
322
323    /**
324     * The payload data, usually a subclass of data (A, AAAA, CNAME, ...).
325     * @return The payload data.
326     */
327    public Data getPayload() {
328        return payloadData;
329    }
330
331    /**
332     * Retrieve the record ttl.
333     * @return The record ttl.
334     */
335    public long getTtl() {
336        return ttl;
337    }
338
339}