001/*
002 * Copyright 2015-2018 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.dnsmessage;
012
013import org.minidns.edns.Edns;
014import org.minidns.record.Data;
015import org.minidns.record.OPT;
016import org.minidns.record.Record;
017import org.minidns.record.Record.TYPE;
018
019import java.io.ByteArrayInputStream;
020import java.io.ByteArrayOutputStream;
021import java.io.DataInputStream;
022import java.io.DataOutputStream;
023import java.io.IOException;
024import java.net.DatagramPacket;
025import java.net.InetAddress;
026import java.nio.ByteBuffer;
027import java.util.ArrayList;
028import java.util.Arrays;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.Date;
032import java.util.HashMap;
033import java.util.HashSet;
034import java.util.List;
035import java.util.Map;
036import java.util.Set;
037import java.util.logging.Level;
038import java.util.logging.Logger;
039
040/**
041 * A DNS message as defined by RFC 1035. The message consists of a header and
042 * 4 sections: question, answer, nameserver and addition resource record
043 * section.
044 * A message can either be parsed ({@link #DnsMessage(byte[])}) or serialized
045 * ({@link DnsMessage#toArray()}).
046 * 
047 * @see <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035</a>
048 */
049public class DnsMessage {
050
051    private static final Logger LOGGER = Logger.getLogger(DnsMessage.class.getName());
052
053    /**
054     * Possible DNS response codes.
055     * 
056     * @see <a href=
057     *      "http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6">
058     *      IANA Domain Name System (DNS) Paramters - DNS RCODEs</a>
059     * @see <a href="http://tools.ietf.org/html/rfc6895#section-2.3">RFC 6895 § 2.3</a>
060     */
061    public static enum RESPONSE_CODE {
062        NO_ERROR(0),
063        FORMAT_ERR(1),
064        SERVER_FAIL(2),
065        NX_DOMAIN(3),
066        NO_IMP(4),
067        REFUSED(5),
068        YXDOMAIN(6),
069        YXRRSET(7),
070        NXRRSET(8),
071        NOT_AUTH(9),
072        NOT_ZONE(10),
073        BADVERS_BADSIG(16),
074        BADKEY(17),
075        BADTIME(18),
076        BADMODE(19),
077        BADNAME(20),
078        BADALG(21),
079        BADTRUNC(22),
080        BADCOOKIE(23),
081        ;
082
083        /**
084         * Reverse lookup table for response codes.
085         */
086        private final static Map<Integer, RESPONSE_CODE> INVERSE_LUT = new HashMap<>(RESPONSE_CODE.values().length);
087
088        static {
089            for (RESPONSE_CODE responseCode : RESPONSE_CODE.values()) {
090                INVERSE_LUT.put((int) responseCode.value, responseCode);
091            }
092        }
093
094        /**
095         * The response code value.
096         */
097        private final byte value;
098
099        /**
100         * Create a new response code.
101         *
102         * @param value The response code value.
103         */
104        private RESPONSE_CODE(int value) {
105            this.value = (byte) value;
106        }
107
108        /**
109         * Retrieve the byte value of the response code.
110         *
111         * @return the response code.
112         */
113        public byte getValue() {
114            return value;
115        }
116
117        /**
118         * Retrieve the response code for a byte value.
119         *
120         * @param value The byte value.
121         * @return The symbolic response code or null.
122         * @throws IllegalArgumentException if the value is not in the range of 0..15.
123         */
124        public static RESPONSE_CODE getResponseCode(int value) throws IllegalArgumentException {
125            if (value < 0 || value > 65535) {
126                throw new IllegalArgumentException();
127            }
128            return INVERSE_LUT.get(value);
129        }
130
131    }
132
133    /**
134     * Symbolic DNS Opcode values.
135     * 
136     * @see <a href=
137     *      "http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5">
138     *      IANA Domain Name System (DNS) Paramters - DNS OpCodes</a>
139     */
140    public static enum OPCODE {
141        QUERY,
142        INVERSE_QUERY,
143        STATUS,
144        UNASSIGNED3,
145        NOTIFY,
146        UPDATE,
147        ;
148
149        /**
150         * Lookup table for for opcode resolution.
151         */
152        private final static OPCODE INVERSE_LUT[] = new OPCODE[OPCODE.values().length];
153
154        static {
155            for (OPCODE opcode : OPCODE.values()) {
156                if (INVERSE_LUT[opcode.getValue()] != null) {
157                    throw new IllegalStateException();
158                }
159                INVERSE_LUT[opcode.getValue()] = opcode;
160            }
161        }
162
163        /**
164         * The value of this opcode.
165         */
166        private final byte value;
167
168        /**
169         * Create a new opcode for a given byte value.
170         *
171         */
172        private OPCODE() {
173            this.value = (byte) this.ordinal();
174        }
175
176        /**
177         * Retrieve the byte value of this opcode.
178         *
179         * @return The byte value of this opcode.
180         */
181        public byte getValue() {
182            return value;
183        }
184
185        /**
186         * Retrieve the symbolic name of an opcode byte.
187         *
188         * @param value The byte value of the opcode.
189         * @return The symbolic opcode or null.
190         * @throws IllegalArgumentException If the byte value is not in the
191         *                                  range 0..15.
192         */
193        public static OPCODE getOpcode(int value) throws IllegalArgumentException {
194            if (value < 0 || value > 15) {
195                throw new IllegalArgumentException();
196            }
197            if (value >= INVERSE_LUT.length) {
198                return null;
199            }
200            return INVERSE_LUT[value];
201        }
202
203    }
204
205    /**
206     * The DNS message id.
207     */
208    public final int id;
209
210    /**
211     * The DNS message opcode.
212     */
213    public final OPCODE opcode;
214
215    /**
216     * The response code of this dns message.
217     */
218    public final RESPONSE_CODE responseCode;
219
220    /**
221     * The QR flag of the DNS message header. Note that this will be <code>true</code> if the message is a
222     * <b>response</b> and <code>false</code> if it is a <b>query</b>.
223     * 
224     * @see <a href="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035 § 4.1.1</a>
225     */
226    public final boolean qr;
227
228    /**
229     * True if this is a authorative response.
230     */
231    public final boolean authoritativeAnswer;
232
233    /**
234     * True if message is truncated. Then TCP should be used.
235     */
236    public final boolean truncated;
237
238    /**
239     * True if the server should recurse.
240     */
241    public final boolean recursionDesired;
242
243    /**
244     * True if recursion is possible.
245     */
246    public final boolean recursionAvailable;
247
248    /**
249     * True if the server regarded the response as authentic.
250     */
251    public final boolean authenticData;
252
253    /**
254     * True if the server should not perform DNSSEC validation before returning the result.
255     */
256    public final boolean checkingDisabled;
257
258    /**
259     * The question section content. Usually there will be only one question.
260     * <p>
261     * This list is unmodifiable.
262     * </p>
263     */
264    public final List<Question> questions;
265
266    /**
267     * The answers section records. Note that it is not guaranteed that all records found in this section will be direct
268     * answers to the question in the query. If DNSSEC is used, then this section also contains the RRSIG record.
269     * <p>
270     * This list is unmodifiable.
271     * </p>
272     */
273    public final List<Record<? extends Data>> answerSection;
274
275    /**
276     * The Authority Section. Note that it is not guaranteed that this section only contains nameserver records. If DNSSEC is used, then this section could also contain a NSEC(3) record.
277     * <p>
278     * This list is unmodifiable.
279     * </p>
280     */
281    public final List<Record<? extends Data>> authoritySection;
282
283    /**
284     * The additional section. It eventually contains RRs which relate to the query.
285     * <p>
286     * This list is unmodifiable.
287     * </p> 
288     */
289    public final List<Record<? extends Data>> additionalSection;
290
291    public final int optRrPosition;
292
293    /**
294     * The optional but very common EDNS information. Note that this field is lazily populated.
295     *
296     */
297    private Edns edns;
298
299    /**
300     * The receive timestamp. Set only if this message was created via parse.
301     * This should be used to evaluate TTLs.
302     */
303    public final long receiveTimestamp;
304
305    protected DnsMessage(Builder builder) {
306        this.id = builder.id;
307        this.opcode = builder.opcode;
308        this.responseCode = builder.responseCode;
309        this.receiveTimestamp = builder.receiveTimestamp;
310        this.qr = builder.query;
311        this.authoritativeAnswer = builder.authoritativeAnswer;
312        this.truncated = builder.truncated;
313        this.recursionDesired = builder.recursionDesired;
314        this.recursionAvailable = builder.recursionAvailable;
315        this.authenticData = builder.authenticData;
316        this.checkingDisabled = builder.checkingDisabled;
317
318        if (builder.questions == null) {
319            this.questions = Collections.emptyList();
320        } else {
321            List<Question> q = new ArrayList<>(builder.questions.size());
322            q.addAll(builder.questions);
323            this.questions = Collections.unmodifiableList(q);
324        }
325
326        if (builder.answers == null) {
327            this.answerSection = Collections.emptyList();
328        } else {
329            List<Record<? extends Data>> a = new ArrayList<>(builder.answers.size());
330            a.addAll(builder.answers);
331            this.answerSection = Collections.unmodifiableList(a);
332        }
333
334        if (builder.nameserverRecords == null) {
335            this.authoritySection = Collections.emptyList();
336        } else {
337            List<Record<? extends Data>> n = new ArrayList<>(builder.nameserverRecords.size());
338            n.addAll(builder.nameserverRecords);
339            this.authoritySection = Collections.unmodifiableList(n);
340        }
341
342        if (builder.additionalResourceRecords == null && builder.ednsBuilder == null) {
343            this.additionalSection = Collections.emptyList();
344        } else {
345            int size = 0;
346            if (builder.additionalResourceRecords != null) {
347                size += builder.additionalResourceRecords.size();
348            }
349            if (builder.ednsBuilder != null) {
350                size++;
351            }
352            List<Record<? extends Data>> a = new ArrayList<>(size);
353            if (builder.additionalResourceRecords != null) {
354                a.addAll(builder.additionalResourceRecords);
355            }
356            if (builder.ednsBuilder != null) {
357                Edns edns = builder.ednsBuilder.build();
358                this.edns = edns;
359                a.add(edns.asRecord());
360            }
361            this.additionalSection = Collections.unmodifiableList(a);
362        }
363
364        optRrPosition = getOptRrPosition(this.additionalSection);
365
366        if (optRrPosition != -1) {
367            // Verify that there are no further OPT records but the one we already found.
368            for (int i = optRrPosition + 1; i < this.additionalSection.size(); i++) {
369                if (this.additionalSection.get(i).type == TYPE.OPT) {
370                    throw new IllegalArgumentException("There must be only one OPT pseudo RR in the additional section");
371                }
372            }
373        }
374
375        // TODO Add verification of dns message state here
376    }
377
378    /**
379     * Build a DNS Message based on a binary DNS message.
380     *
381     * @param data The DNS message data.
382     * @throws IOException On read errors.
383     */
384    public DnsMessage(byte data[]) throws IOException {
385        ByteArrayInputStream bis = new ByteArrayInputStream(data);
386        DataInputStream dis = new DataInputStream(bis);
387        id = dis.readUnsignedShort();
388        int header = dis.readUnsignedShort();
389        qr = ((header >> 15) & 1) == 1;
390        opcode = OPCODE.getOpcode((header >> 11) & 0xf);
391        authoritativeAnswer = ((header >> 10) & 1) == 1;
392        truncated = ((header >> 9) & 1) == 1;
393        recursionDesired = ((header >> 8) & 1) == 1;
394        recursionAvailable = ((header >> 7) & 1) == 1;
395        authenticData = ((header >> 5) & 1) == 1;
396        checkingDisabled = ((header >> 4) & 1) == 1;
397        responseCode = RESPONSE_CODE.getResponseCode(header & 0xf);
398        receiveTimestamp = System.currentTimeMillis();
399        int questionCount = dis.readUnsignedShort();
400        int answerCount = dis.readUnsignedShort();
401        int nameserverCount = dis.readUnsignedShort();
402        int additionalResourceRecordCount = dis.readUnsignedShort();
403        questions = new ArrayList<>(questionCount);
404        for (int i = 0; i < questionCount; i++) {
405            questions.add(new Question(dis, data));
406        }
407        answerSection = new ArrayList<>(answerCount);
408        for (int i = 0; i < answerCount; i++) {
409            answerSection.add(Record.parse(dis, data));
410        }
411        authoritySection = new ArrayList<>(nameserverCount);
412        for (int i = 0; i < nameserverCount; i++) {
413            authoritySection.add(Record.parse(dis, data));
414        }
415        additionalSection = new ArrayList<>(additionalResourceRecordCount);
416        for (int i = 0; i < additionalResourceRecordCount; i++) {
417            additionalSection.add(Record.parse(dis, data));
418        }
419        optRrPosition = getOptRrPosition(additionalSection);
420    }
421
422    /**
423     * Constructs an normalized version of the given DnsMessage by setting the id to '0'.
424     *
425     * @param message the message of which normalized version should be constructed.
426     */
427    private DnsMessage(DnsMessage message) {
428        id = 0;
429        qr = message.qr;
430        opcode = message.opcode;
431        authoritativeAnswer = message.authoritativeAnswer;
432        truncated = message.truncated;
433        recursionDesired = message.recursionDesired;
434        recursionAvailable = message.recursionAvailable;
435        authenticData = message.authenticData;
436        checkingDisabled = message.checkingDisabled;
437        responseCode = message.responseCode;
438        receiveTimestamp = message.receiveTimestamp;
439        questions = message.questions;
440        answerSection = message.answerSection;
441        authoritySection = message.authoritySection;
442        additionalSection = message.additionalSection;
443        optRrPosition = message.optRrPosition;
444    }
445
446    private static int getOptRrPosition(List<Record<? extends Data>> additionalSection) {
447        int optRrPosition = -1;
448        for (int i = 0; i < additionalSection.size(); i++) {
449            Record<? extends Data> record = additionalSection.get(i);
450            if (record.type == Record.TYPE.OPT) {
451                optRrPosition = i;
452                break;
453            }
454        }
455        return optRrPosition;
456    }
457
458    /**
459     * Generate a binary dns packet out of this message.
460     *
461     * @return byte[] the binary representation.
462     */
463    public byte[] toArray() {
464        return serialize().clone();
465    }
466
467    public DatagramPacket asDatagram(InetAddress address, int port) {
468        byte[] bytes = serialize();
469        return new DatagramPacket(bytes, bytes.length, address, port);
470    }
471
472    public void writeTo(DataOutputStream dataOutputStream) throws IOException {
473        byte[] bytes = serialize();
474        dataOutputStream.writeShort(bytes.length);
475        dataOutputStream.write(bytes);
476    }
477
478    public ByteBuffer getInByteBuffer() {
479        byte[] bytes = serialize().clone();
480        return ByteBuffer.wrap(bytes);
481    }
482
483    private byte[] byteCache;
484
485    private byte[] serialize() {
486        if (byteCache != null) {
487            return byteCache;
488        }
489
490        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
491        DataOutputStream dos = new DataOutputStream(baos);
492        int header = calculateHeaderBitmap();
493        try {
494            dos.writeShort((short) id);
495            dos.writeShort((short) header);
496            if (questions == null) {
497                dos.writeShort(0);
498            } else {
499                dos.writeShort((short) questions.size());
500            }
501            if (answerSection == null) {
502                dos.writeShort(0);
503            } else {
504                dos.writeShort((short) answerSection.size());
505            }
506            if (authoritySection == null) {
507                dos.writeShort(0);
508            } else {
509                dos.writeShort((short) authoritySection.size());
510            }
511            if (additionalSection == null) {
512                dos.writeShort(0);
513            } else {
514                dos.writeShort((short) additionalSection.size());
515            }
516            if (questions != null) {
517                for (Question question : questions) {
518                    dos.write(question.toByteArray());
519                }
520            }
521            if (answerSection != null) {
522                for (Record<? extends Data> answer : answerSection) {
523                    dos.write(answer.toByteArray());
524                }
525            }
526            if (authoritySection != null) {
527                for (Record<? extends Data> nameserverRecord : authoritySection) {
528                    dos.write(nameserverRecord.toByteArray());
529                }
530            }
531            if (additionalSection != null) {
532                for (Record<? extends Data> additionalResourceRecord : additionalSection) {
533                    dos.write(additionalResourceRecord.toByteArray());
534                }
535            }
536            dos.flush();
537        } catch (IOException e) {
538            // Should never happen.
539            throw new AssertionError(e);
540        }
541        byteCache = baos.toByteArray();
542        return byteCache;
543    }
544
545    int calculateHeaderBitmap() {
546        int header = 0;
547        if (qr) {
548            header += 1 << 15;
549        }
550        if (opcode != null) {
551            header += opcode.getValue() << 11;
552        }
553        if (authoritativeAnswer) {
554            header += 1 << 10;
555        }
556        if (truncated) {
557            header += 1 << 9;
558        }
559        if (recursionDesired) {
560            header += 1 << 8;
561        }
562        if (recursionAvailable) {
563            header += 1 << 7;
564        }
565        if (authenticData) {
566            header += 1 << 5;
567        }
568        if (checkingDisabled) {
569            header += 1 << 4;
570        }
571        if (responseCode != null) {
572            header += responseCode.getValue();
573        }
574        return header;
575    }
576
577    public Question getQuestion() {
578        return questions.get(0);
579    }
580
581    /**
582     * Copy the questions found in the question section.
583     *
584     * @return a copy of the question section questions.
585     * @see #questions
586     */
587    public List<Question> copyQuestions() {
588        List<Question> copy = new ArrayList<>(questions.size());
589        copy.addAll(questions);
590        return copy;
591    }
592
593    /**
594     * Copy the records found in the answer section into a new list.
595     *
596     * @return a copy of the answer section records.
597     * @see #answerSection
598     */
599    public List<Record<? extends Data>> copyAnswers() {
600        List<Record<? extends Data>> res = new ArrayList<>(answerSection.size());
601        res.addAll(answerSection);
602        return res;
603    }
604
605    /**
606     * Copy the records found in the authority section into a new list.
607     *
608     * @return a copy of the authority section records.
609     * @see #authoritySection
610     */
611    public List<Record<? extends Data>> copyAuthority() {
612        List<Record<? extends Data>> res = new ArrayList<>(authoritySection.size());
613        res.addAll(authoritySection);
614        return res;
615    }
616
617    public Edns getEdns() {
618        if (edns != null) return edns;
619
620        Record<OPT> optRecord = getOptPseudoRecord();
621        if (optRecord == null) return null;
622        edns = new Edns(optRecord);
623        return edns;
624    }
625
626    @SuppressWarnings("unchecked")
627    public Record<OPT> getOptPseudoRecord() {
628        if (optRrPosition == -1) return null;
629        return (Record<OPT>) additionalSection.get(optRrPosition);
630    }
631
632    /**
633     * Check if the EDNS DO (DNSSEC OK) flag is set.
634     *
635     * @return true if the DO flag is set.
636     */
637    public boolean isDnssecOk() {
638        Edns edns = getEdns();
639        if (edns == null)
640            return false;
641
642        return edns.dnssecOk;
643    }
644
645    private String toStringCache;
646
647    @Override
648    public String toString() {
649        if (toStringCache != null) return toStringCache;
650
651        StringBuilder sb = new StringBuilder("DnsMessage")
652                .append('(').append(id).append(' ')
653                .append(opcode).append(' ')
654                .append(responseCode).append(' ');
655        if (qr) {
656            sb.append("resp[qr=1]");
657        } else {
658            sb.append("query[qr=0]");
659        }
660        if (authoritativeAnswer) sb.append(" aa");
661        if (truncated) sb.append(" tr");
662        if (recursionDesired) sb.append(" rd");
663        if (recursionAvailable) sb.append(" ra");
664        if (authenticData) sb.append(" ad");
665        if (checkingDisabled) sb.append(" cd");
666        sb.append(")\n");
667        if (questions != null) {
668            for (Question question : questions) {
669                sb.append("[Q: ").append(question).append("]\n");
670            }
671        }
672        if (answerSection != null) {
673            for (Record<? extends Data> record : answerSection) {
674                sb.append("[A: ").append(record).append("]\n");
675            }
676        }
677        if (authoritySection != null) {
678            for (Record<? extends Data> record : authoritySection) {
679                sb.append("[N: ").append(record).append("]\n");
680            }
681        }
682        if (additionalSection != null) {
683            for (Record<? extends Data> record : additionalSection) {
684                sb.append("[X: ");
685                Edns edns = Edns.fromRecord(record);
686                if (edns != null) {
687                    sb.append(edns.toString());
688                } else {
689                    sb.append(record);
690                }
691                sb.append("]\n");
692            }
693        }
694
695        // Strip trailing newline.
696        if (sb.charAt(sb.length() - 1) == '\n') {
697            sb.setLength(sb.length() - 1);
698        }
699
700        toStringCache = sb.toString();
701        return toStringCache;
702    }
703
704    private String terminalOutputCache;
705
706    /**
707     * Format the DnsMessage object in a way suitable for terminal output.
708     * The format is loosely based on the output provided by {@code dig}.
709     *
710     * @return This message as a String suitable for terminal output.
711     */
712    public String asTerminalOutput() {
713        if (terminalOutputCache != null) return terminalOutputCache;
714
715        StringBuilder sb = new StringBuilder(";; ->>HEADER<<-")
716                .append(" opcode: ").append(opcode)
717                .append(", status: ").append(responseCode)
718                .append(", id: ").append(id).append("\n")
719                .append(";; flags:");
720        if (!qr) sb.append(" qr");
721        if (authoritativeAnswer) sb.append(" aa");
722        if (truncated) sb.append(" tr");
723        if (recursionDesired) sb.append(" rd");
724        if (recursionAvailable) sb.append(" ra");
725        if (authenticData) sb.append(" ad");
726        if (checkingDisabled) sb.append(" cd");
727        sb.append("; QUERY: ").append(questions.size())
728                .append(", ANSWER: ").append(answerSection.size())
729                .append(", AUTHORITY: ").append(authoritySection.size())
730                .append(", ADDITIONAL: ").append(additionalSection.size())
731                .append("\n\n");
732        for (Record<? extends Data> record : additionalSection) {
733            Edns edns = Edns.fromRecord(record);
734            if (edns != null) {
735                sb.append(";; OPT PSEUDOSECTION:\n; ").append(edns.asTerminalOutput());
736                break;
737            }
738        }
739        if (questions.size() != 0) {
740            sb.append(";; QUESTION SECTION:\n");
741            for (Question question : questions) {
742                sb.append(';').append(question.toString()).append('\n');
743            }
744        }
745        if (authoritySection.size() != 0) {
746            sb.append("\n;; AUTHORITY SECTION:\n");
747            for (Record<? extends Data> record : authoritySection) {
748                sb.append(record.toString()).append('\n');
749            }
750        }
751        if (answerSection.size() != 0) {
752            sb.append("\n;; ANSWER SECTION:\n");
753            for (Record<? extends Data> record : answerSection) {
754                sb.append(record.toString()).append('\n');
755            }
756        }
757        if (additionalSection.size() != 0) {
758            boolean hasNonOptArr = false;
759            for (Record<? extends Data> record : additionalSection) {
760                if (record.type != Record.TYPE.OPT) {
761                    if (!hasNonOptArr) {
762                        hasNonOptArr = true;
763                        sb.append("\n;; ADDITIONAL SECTION:\n");
764                    }
765                    sb.append(record.toString()).append('\n');
766                }
767            }
768        }
769        if (receiveTimestamp > 0) {
770            sb.append("\n;; WHEN: ").append(new Date(receiveTimestamp).toString());
771        }
772        terminalOutputCache = sb.toString();
773        return terminalOutputCache;
774    }
775
776    public <D extends Data> Set<D> getAnswersFor(Question q) {
777        if (responseCode != RESPONSE_CODE.NO_ERROR) return null;
778
779        // It would be great if we could verify that D matches q.type at this
780        // point. But on the other hand, if it does not, then the cast to D
781        // below will fail.
782        Set<D> res = new HashSet<>(answerSection.size());
783        for (Record<? extends Data> record : answerSection) {
784            if (!record.isAnswer(q)) continue;
785
786            Data data = record.getPayload();
787            @SuppressWarnings("unchecked")
788            D d = (D) data;
789            boolean isNew = res.add(d);
790            if (!isNew) {
791                LOGGER.log(Level.WARNING, "DnsMessage contains duplicate answers. Record: " + record + "; DnsMessage: " + this);
792            }
793        }
794        return res;
795    }
796
797    public Builder asBuilder() {
798        return new Builder(this);
799    }
800
801    private DnsMessage normalizedVersionCache;
802
803    public DnsMessage asNormalizedVersion() {
804        if (normalizedVersionCache == null) {
805            normalizedVersionCache = new DnsMessage(this);
806        }
807        return normalizedVersionCache;
808    }
809
810    private transient Integer hashCodeCache;
811
812    @Override
813    public int hashCode() {
814        if (hashCodeCache == null) {
815            byte[] bytes = serialize();
816            hashCodeCache = Arrays.hashCode(bytes);
817        }
818        return hashCodeCache;
819    }
820
821    @Override
822    public boolean equals(Object other) {
823        if (!(other instanceof DnsMessage)) {
824            return false;
825        }
826        if (other == this) {
827            return true;
828        }
829        DnsMessage otherDnsMessage = (DnsMessage) other;
830        byte[] otherBytes = otherDnsMessage.serialize();
831        byte[] myBytes = serialize();
832        return Arrays.equals(myBytes, otherBytes);
833    }
834
835    public static Builder builder() {
836        return new DnsMessage.Builder();
837    }
838
839    public static class Builder {
840
841        private Builder() {
842        }
843
844        private Builder(DnsMessage message) {
845            id = message.id;
846            opcode = message.opcode;
847            responseCode = message.responseCode;
848            query = message.qr;
849            authoritativeAnswer = message.authoritativeAnswer;
850            truncated = message.truncated;
851            recursionDesired = message.recursionDesired;
852            recursionAvailable = message.recursionAvailable;
853            authenticData = message.authenticData;
854            checkingDisabled = message.checkingDisabled;
855            receiveTimestamp = message.receiveTimestamp;
856
857            // Copy the unmodifiable lists over into this new builder.
858            questions = new ArrayList<>(message.questions.size());
859            questions.addAll(message.questions);
860            answers = new ArrayList<>(message.answerSection.size());
861            answers.addAll(message.answerSection);
862            nameserverRecords = new ArrayList<>(message.authoritySection.size());
863            nameserverRecords.addAll(message.authoritySection);
864            additionalResourceRecords = new ArrayList<>(message.additionalSection.size());
865            additionalResourceRecords.addAll(message.additionalSection);
866        }
867
868        private int id;
869        private OPCODE opcode = OPCODE.QUERY;
870        private RESPONSE_CODE responseCode = RESPONSE_CODE.NO_ERROR;
871        private boolean query;
872        private boolean authoritativeAnswer;
873        private boolean truncated;
874        private boolean recursionDesired;
875        private boolean recursionAvailable;
876        private boolean authenticData;
877        private boolean checkingDisabled;
878
879        private long receiveTimestamp = -1;
880
881        private List<Question> questions;
882        private List<Record<? extends Data>> answers;
883        private List<Record<? extends Data>> nameserverRecords;
884        private List<Record<? extends Data>> additionalResourceRecords;
885        private Edns.Builder ednsBuilder;
886
887        /**
888         * Set the current DNS message id.
889         *
890         * @param id The new DNS message id.
891         * @return a reference to this builder.
892         */
893        public Builder setId(int id) {
894            this.id = id & 0xffff;
895            return this;
896        }
897
898        public Builder setOpcode(OPCODE opcode) {
899            this.opcode = opcode;
900            return this;
901        }
902
903        public Builder setResponseCode(RESPONSE_CODE responseCode) {
904            this.responseCode = responseCode;
905            return this;
906        }
907
908        /**
909         * Set the QR flag.
910         *
911         * @param query The new QR flag status.
912         * @return a reference to this builder.
913         */
914        public Builder setQrFlag(boolean query) {
915            this.query = query;
916            return this;
917        }
918
919        /**
920         * Set the authoritative answer flag.
921         *
922         * @param authoritativeAnswer Tge new authoritative answer value.
923         * @return a reference to this builder.
924         */
925        public Builder setAuthoritativeAnswer(boolean authoritativeAnswer) {
926            this.authoritativeAnswer = authoritativeAnswer;
927            return this;
928        }
929
930        /**
931         * Set the truncation bit on this DNS message.
932         *
933         * @param truncated The new truncated bit status.
934         * @return a reference to this builder.
935         */
936        public Builder setTruncated(boolean truncated) {
937            this.truncated = truncated;
938            return this;
939        }
940
941        /**
942         * Set the recursion desired flag on this message.
943         *
944         * @param recursionDesired The new recusrion setting.
945         * @return a reference to this builder.
946         */
947        public Builder setRecursionDesired(boolean recursionDesired) {
948            this.recursionDesired = recursionDesired;
949            return this;
950        }
951
952        /**
953         * Set the recursion available flog from this DNS message.
954         *
955                 * @param recursionAvailable The new recursion available status.
956         * @return a reference to this builder.
957         */
958        public Builder setRecursionAvailable(boolean recursionAvailable) {
959            this.recursionAvailable = recursionAvailable;
960            return this;
961        }
962
963        /**
964         * Set the authentic data flag on this DNS message.
965         *
966         * @param authenticData The new authentic data flag value.
967         * @return a reference to this builder.
968         */
969        public Builder setAuthenticData(boolean authenticData) {
970            this.authenticData = authenticData;
971            return this;
972        }
973
974        /**
975         * Change the check status of this packet.
976         *
977         * @param checkingDisabled The new check disabled value.
978         * @return a reference to this builder.
979         */
980        @Deprecated
981        public Builder setCheckDisabled(boolean checkingDisabled) {
982            this.checkingDisabled = checkingDisabled;
983            return this;
984        }
985
986        /**
987         * Change the check status of this packet.
988         *
989         * @param checkingDisabled The new check disabled value.
990         * @return a reference to this builder.
991         */
992        public Builder setCheckingDisabled(boolean checkingDisabled) {
993            this.checkingDisabled = checkingDisabled;
994            return this;
995        }
996
997        public void copyFlagsFrom(DnsMessage dnsMessage) {
998            this.query = dnsMessage.qr;
999            this.authoritativeAnswer = dnsMessage.authenticData;
1000            this.truncated = dnsMessage.truncated;
1001            this.recursionDesired = dnsMessage.recursionDesired;
1002            this.recursionAvailable = dnsMessage.recursionAvailable;
1003            this.authenticData = dnsMessage.authenticData;
1004            this.checkingDisabled = dnsMessage.checkingDisabled;
1005        }
1006
1007        public Builder setReceiveTimestamp(long receiveTimestamp) {
1008            this.receiveTimestamp = receiveTimestamp;
1009            return this;
1010        }
1011
1012        public Builder addQuestion(Question question) {
1013            if (questions == null) {
1014                questions = new ArrayList<>(1);
1015            }
1016            questions.add(question);
1017            return this;
1018        }
1019
1020        /**
1021         * Set the question part of this message.
1022         *
1023         * @param questions The questions.
1024         * @return a reference to this builder.
1025         */
1026        public Builder setQuestions(List<Question> questions) {
1027            this.questions = questions;
1028            return this;
1029        }
1030
1031        /**
1032         * Set the question part of this message.
1033         *
1034         * @param question The question.
1035         * @return a reference to this builder.
1036         */
1037        public Builder setQuestion(Question question) {
1038            this.questions = new ArrayList<>(1);
1039            this.questions.add(question);
1040            return this;
1041        }
1042
1043        public Builder addAnswer(Record<? extends Data> answer) {
1044            if (answers == null) {
1045                answers = new ArrayList<>(1);
1046            }
1047            answers.add(answer);
1048            return this;
1049        }
1050
1051        public Builder addAnswers(Collection<Record<? extends Data>> records) {
1052            if (answers == null) {
1053                answers = new ArrayList<>(records.size());
1054            }
1055            answers.addAll(records);
1056            return this;
1057        }
1058
1059        public Builder setAnswers(Collection<Record<? extends Data>> records) {
1060            answers = new ArrayList<>(records.size());
1061            answers.addAll(records);
1062            return this;
1063        }
1064
1065        public List<Record<? extends Data>> getAnswers() {
1066            if (answers == null) {
1067                return Collections.emptyList();
1068            }
1069            return answers;
1070        }
1071
1072        public Builder addNameserverRecords(Record<? extends Data> record) {
1073            if (nameserverRecords == null) {
1074                nameserverRecords = new ArrayList<>(8);
1075            }
1076            nameserverRecords.add(record);
1077            return this;
1078        }
1079
1080        public Builder setNameserverRecords(Collection<Record<? extends Data>> records) {
1081            nameserverRecords = new ArrayList<>(records.size());
1082            nameserverRecords.addAll(records);
1083            return this;
1084        }
1085
1086        public Builder setAdditionalResourceRecords(Collection<Record<? extends Data>> records) {
1087            additionalResourceRecords = new ArrayList<>(records.size());
1088            additionalResourceRecords.addAll(records);
1089            return this;
1090        }
1091
1092        public Builder addAdditionalResourceRecord(Record<? extends Data> record) {
1093            if (additionalResourceRecords == null) {
1094                additionalResourceRecords = new ArrayList<>();
1095            }
1096            additionalResourceRecords.add(record);
1097            return this;
1098        }
1099
1100        public Builder addAdditionalResourceRecords(List<Record<? extends Data>> records) {
1101            if (additionalResourceRecords == null) {
1102                additionalResourceRecords = new ArrayList<>(records.size());
1103            }
1104            additionalResourceRecords.addAll(records);
1105            return this;
1106        }
1107
1108        public List<Record<? extends Data>> getAdditionalResourceRecords() {
1109            if (additionalResourceRecords == null) {
1110                return Collections.emptyList();
1111            }
1112            return additionalResourceRecords;
1113        }
1114
1115        /**
1116         * Get the @{link EDNS} builder. If no builder has been set so far, then a new one will be created.
1117         * <p>
1118         * The EDNS record can be used to announce the supported size of UDP payload as well as additional flags.
1119         * </p>
1120         * <p>
1121         * Note that some networks and firewalls are known to block big UDP payloads. 1280 should be a reasonable value,
1122         * everything below 512 is treated as 512 and should work on all networks.
1123         * </p>
1124         * 
1125         * @return a EDNS builder.
1126         */
1127        public Edns.Builder getEdnsBuilder() {
1128            if (ednsBuilder == null) {
1129                ednsBuilder = Edns.builder();
1130            }
1131            return ednsBuilder;
1132        }
1133
1134        public DnsMessage build() {
1135            return new DnsMessage(this);
1136        }
1137
1138    }
1139
1140}