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