13742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// /Copyright 2003-2005 Arthur van Hoff, Rick Blair
23742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Licensed under Apache License version 2.0
33742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman// Original license LGPL
43742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
53742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpackage javax.jmdns.impl;
63742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
73742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.io.ByteArrayInputStream;
83742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.io.IOException;
93742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.DatagramPacket;
103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.net.InetAddress;
113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.HashMap;
123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Map;
133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Level;
143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.logging.Logger;
153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSConstants;
173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSLabel;
183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSOptionCode;
193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSRecordClass;
203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSRecordType;
213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSResultCode;
223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman/**
243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * Parse an incoming DNS message into its components.
253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman *
263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert
273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */
283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpublic final class DNSIncoming extends DNSMessage {
293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private static Logger logger                                = Logger.getLogger(DNSIncoming.class.getName());
303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // This is a hack to handle a bug in the BonjourConformanceTest
323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    // It is sending out target strings that don't follow the "domain name" format.
333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static boolean USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET = true;
343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static class MessageInputStream extends ByteArrayInputStream {
363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private static Logger      logger1 = Logger.getLogger(MessageInputStream.class.getName());
373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        final Map<Integer, String> _names;
393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public MessageInputStream(byte[] buffer, int length) {
413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this(buffer, 0, length);
423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param buffer
463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param offset
473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param length
483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public MessageInputStream(byte[] buffer, int offset, int length) {
503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            super(buffer, offset, length);
513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _names = new HashMap<Integer, String>();
523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public int readByte() {
553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return this.read();
563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public int readUnsignedShort() {
593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return (this.read() << 8) | this.read();
603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public int readInt() {
633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return (this.readUnsignedShort() << 16) | this.readUnsignedShort();
643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public byte[] readBytes(int len) {
673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            byte bytes[] = new byte[len];
683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.read(bytes, 0, len);
693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return bytes;
703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public String readUTF(int len) {
733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            StringBuilder buffer = new StringBuilder(len);
743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (int index = 0; index < len; index++) {
753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int ch = this.read();
763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                switch (ch >> 4) {
773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 0:
783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 1:
793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 2:
803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 3:
813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 4:
823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 5:
833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 6:
843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 7:
853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // 0xxxxxxx
863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 12:
883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 13:
893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // 110x xxxx 10xx xxxx
903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        ch = ((ch & 0x1F) << 6) | (this.read() & 0x3F);
913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        index++;
923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case 14:
943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // 1110 xxxx 10xx xxxx 10xx xxxx
953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        ch = ((ch & 0x0f) << 12) | ((this.read() & 0x3F) << 6) | (this.read() & 0x3F);
963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        index++;
973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        index++;
983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    default:
1003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // 10xx xxxx, 1111 xxxx
1013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        ch = ((ch & 0x3F) << 4) | (this.read() & 0x0f);
1023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        index++;
1033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
1043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buffer.append((char) ch);
1063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return buffer.toString();
1083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        protected synchronized int peek() {
1113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return (pos < count) ? (buf[pos] & 0xff) : -1;
1123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public String readName() {
1153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            Map<Integer, StringBuilder> names = new HashMap<Integer, StringBuilder>();
1163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            StringBuilder buffer = new StringBuilder();
1173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            boolean finished = false;
1183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            while (!finished) {
1193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int len = this.read();
1203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (len == 0) {
1213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    finished = true;
1223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    break;
1233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                switch (DNSLabel.labelForByte(len)) {
1253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case Standard:
1263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        int offset = pos - 1;
1273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        String label = this.readUTF(len) + ".";
1283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        buffer.append(label);
1293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        for (StringBuilder previousLabel : names.values()) {
1303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            previousLabel.append(label);
1313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
1323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        names.put(Integer.valueOf(offset), new StringBuilder(label));
1333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
1343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case Compressed:
1353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        int index = (DNSLabel.labelValue(len) << 8) | this.read();
1363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        String compressedLabel = _names.get(Integer.valueOf(index));
1373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        if (compressedLabel == null) {
1383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            logger1.severe("bad domain name: possible circular name detected. Bad offset: 0x" + Integer.toHexString(index) + " at 0x" + Integer.toHexString(pos - 2));
1393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            compressedLabel = "";
1403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
1413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        buffer.append(compressedLabel);
1423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        for (StringBuilder previousLabel : names.values()) {
1433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            previousLabel.append(compressedLabel);
1443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
1453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        finished = true;
1463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
1473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case Extended:
1483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // int extendedLabelClass = DNSLabel.labelValue(len);
1493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        logger1.severe("Extended label are not currently supported.");
1503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        break;
1513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    case Unknown:
1523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    default:
1533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        logger1.severe("unsupported dns label type: '" + Integer.toHexString(len & 0xC0) + "'");
1543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (Integer index : names.keySet()) {
1573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _names.put(index, names.get(index).toString());
1583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return buffer.toString();
1603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        public String readNonNameString() {
1633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            int len = this.read();
1643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            return this.readUTF(len);
1653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
1683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final DatagramPacket     _packet;
1703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final long               _receivedTime;
1723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final MessageInputStream _messageInputStream;
1743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private int                      _senderUDPPayload;
1763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
1783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Parse a message from a datagram packet.
1793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
1803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param packet
1813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
1823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
1833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSIncoming(DatagramPacket packet) throws IOException {
1843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        super(0, 0, packet.getPort() == DNSConstants.MDNS_PORT);
1853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._packet = packet;
1863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        InetAddress source = packet.getAddress();
1873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
1883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._receivedTime = System.currentTimeMillis();
1893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._senderUDPPayload = DNSConstants.MAX_MSG_TYPICAL;
1903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        try {
1923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.setId(_messageInputStream.readUnsignedShort());
1933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.setFlags(_messageInputStream.readUnsignedShort());
1943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            int numQuestions = _messageInputStream.readUnsignedShort();
1953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            int numAnswers = _messageInputStream.readUnsignedShort();
1963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            int numAuthorities = _messageInputStream.readUnsignedShort();
1973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            int numAdditionals = _messageInputStream.readUnsignedShort();
1983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // parse questions
2003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (numQuestions > 0) {
2013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (int i = 0; i < numQuestions; i++) {
2023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _questions.add(this.readQuestion());
2033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
2043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // parse answers
2073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (numAnswers > 0) {
2083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (int i = 0; i < numAnswers; i++) {
2093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    DNSRecord rec = this.readAnswer(source);
2103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (rec != null) {
2113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // Add a record, if we were able to create one.
2123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        _answers.add(rec);
2133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
2143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
2153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (numAuthorities > 0) {
2183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (int i = 0; i < numAuthorities; i++) {
2193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    DNSRecord rec = this.readAnswer(source);
2203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (rec != null) {
2213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // Add a record, if we were able to create one.
2223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        _authoritativeAnswers.add(rec);
2233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
2243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
2253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (numAdditionals > 0) {
2283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                for (int i = 0; i < numAdditionals; i++) {
2293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    DNSRecord rec = this.readAnswer(source);
2303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (rec != null) {
2313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // Add a record, if we were able to create one.
2323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        _additionals.add(rec);
2333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
2343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
2353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } catch (Exception e) {
2373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.log(Level.WARNING, "DNSIncoming() dump " + print(true) + "\n exception ", e);
2383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // This ugly but some JVM don't implement the cause on IOException
2393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            IOException ioe = new IOException("DNSIncoming corrupted message");
2403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            ioe.initCause(e);
2413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            throw ioe;
2423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private DNSIncoming(int flags, int id, boolean multicast, DatagramPacket packet, long receivedTime) {
2463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        super(flags, id, multicast);
2473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._packet = packet;
2483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
2493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this._receivedTime = receivedTime;
2503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /*
2543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * (non-Javadoc)
2553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @see java.lang.Object#clone()
2573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
2593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSIncoming clone() {
2603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSIncoming in = new DNSIncoming(this.getFlags(), this.getId(), this.isMulticast(), this._packet, this._receivedTime);
2613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         in._senderUDPPayload = this._senderUDPPayload;
2623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         in._questions.addAll(this._questions);
2633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         in._answers.addAll(this._answers);
2643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         in._authoritativeAnswers.addAll(this._authoritativeAnswers);
2653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         in._additionals.addAll(this._additionals);
2663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         return in;
2673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private DNSQuestion readQuestion() {
2713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String domain = _messageInputStream.readName();
2723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
2733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (type == DNSRecordType.TYPE_IGNORE) {
2743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.log(Level.SEVERE, "Could not find record type: " + this.print(true));
2753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        int recordClassIndex = _messageInputStream.readUnsignedShort();
2773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSRecordClass recordClass = DNSRecordClass.classForIndex(recordClassIndex);
2783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean unique = recordClass.isUnique(recordClassIndex);
2793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return DNSQuestion.newQuestion(domain, type, recordClass, unique);
2803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private DNSRecord readAnswer(InetAddress source) {
2833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        String domain = _messageInputStream.readName();
2843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
2853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (type == DNSRecordType.TYPE_IGNORE) {
2863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.log(Level.SEVERE, "Could not find record type. domain: " + domain + "\n" + this.print(true));
2873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        int recordClassIndex = _messageInputStream.readUnsignedShort();
2893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSRecordClass recordClass = (type == DNSRecordType.TYPE_OPT ? DNSRecordClass.CLASS_UNKNOWN : DNSRecordClass.classForIndex(recordClassIndex));
2903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if ((recordClass == DNSRecordClass.CLASS_UNKNOWN) && (type != DNSRecordType.TYPE_OPT)) {
2913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            logger.log(Level.SEVERE, "Could not find record class. domain: " + domain + " type: " + type + "\n" + this.print(true));
2923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        boolean unique = recordClass.isUnique(recordClassIndex);
2943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        int ttl = _messageInputStream.readInt();
2953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        int len = _messageInputStream.readUnsignedShort();
2963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        DNSRecord rec = null;
2973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        switch (type) {
2993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_A: // IPv4
3003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                rec = new DNSRecord.IPv4Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
3013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
3023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_AAAA: // IPv6
3033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                rec = new DNSRecord.IPv6Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
3043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
3053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_CNAME:
3063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_PTR:
3073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                String service = "";
3083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                service = _messageInputStream.readName();
3093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (service.length() > 0) {
3103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    rec = new DNSRecord.Pointer(domain, recordClass, unique, ttl, service);
3113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
3123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger.log(Level.WARNING, "PTR record of class: " + recordClass + ", there was a problem reading the service name of the answer for domain:" + domain);
3133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
3143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
3153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_TXT:
3163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                rec = new DNSRecord.Text(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
3173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
3183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_SRV:
3193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int priority = _messageInputStream.readUnsignedShort();
3203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int weight = _messageInputStream.readUnsignedShort();
3213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int port = _messageInputStream.readUnsignedShort();
3223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                String target = "";
3233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // This is a hack to handle a bug in the BonjourConformanceTest
3243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                // It is sending out target strings that don't follow the "domain name" format.
3253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
3263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    target = _messageInputStream.readName();
3273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
3283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed to start by a length.
3293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    target = _messageInputStream.readNonNameString();
3303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
3313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                rec = new DNSRecord.Service(domain, recordClass, unique, ttl, priority, weight, port, target);
3323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
3333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_HINFO:
3343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                StringBuilder buf = new StringBuilder();
3353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(_messageInputStream.readUTF(len));
3363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int index = buf.indexOf(" ");
3373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                String cpu = (index > 0 ? buf.substring(0, index) : buf.toString()).trim();
3383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                String os = (index > 0 ? buf.substring(index + 1) : "").trim();
3393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                rec = new DNSRecord.HostInformation(domain, recordClass, unique, ttl, cpu, os);
3403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
3413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            case TYPE_OPT:
3423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                DNSResultCode extendedResultCode = DNSResultCode.resultCodeForFlags(this.getFlags(), ttl);
3433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int version = (ttl & 0x00ff0000) >> 16;
3443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (version == 0) {
3453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _senderUDPPayload = recordClassIndex;
3463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    while (_messageInputStream.available() > 0) {
3473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // Read RDData
3483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        int optionCodeInt = 0;
3493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        DNSOptionCode optionCode = null;
3503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        if (_messageInputStream.available() >= 2) {
3513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            optionCodeInt = _messageInputStream.readUnsignedShort();
3523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            optionCode = DNSOptionCode.resultCodeForFlags(optionCodeInt);
3533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        } else {
3543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
3553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            break;
3563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
3573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        int optionLength = 0;
3583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        if (_messageInputStream.available() >= 2) {
3593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            optionLength = _messageInputStream.readUnsignedShort();
3603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        } else {
3613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
3623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            break;
3633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
3643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        byte[] optiondata = new byte[0];
3653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        if (_messageInputStream.available() >= optionLength) {
3663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            optiondata = _messageInputStream.readBytes(optionLength);
3673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
3683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        //
3693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        // We should really do something with those options.
3703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        switch (optionCode) {
3713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            case Owner:
3723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                // Valid length values are 8, 14, 18 and 20
3733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                // |Opt|Len|V|S|Primary MAC|Wakeup MAC | Password |
3753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
3763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                //
3773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                int ownerVersion = 0;
3783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                int ownerSequence = 0;
3793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                byte[] ownerPrimaryMacAddress = null;
3803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                byte[] ownerWakeupMacAddress = null;
3813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                byte[] ownerPassword = null;
3823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                try {
3833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    ownerVersion = optiondata[0];
3843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    ownerSequence = optiondata[1];
3853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    ownerPrimaryMacAddress = new byte[] { optiondata[2], optiondata[3], optiondata[4], optiondata[5], optiondata[6], optiondata[7] };
3863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    ownerWakeupMacAddress = ownerPrimaryMacAddress;
3873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    if (optiondata.length > 8) {
3883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        // We have a wakeupMacAddress.
3893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        ownerWakeupMacAddress = new byte[] { optiondata[8], optiondata[9], optiondata[10], optiondata[11], optiondata[12], optiondata[13] };
3903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    }
3913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    if (optiondata.length == 18) {
3923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        // We have a short password.
3933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17] };
3943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    }
3953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    if (optiondata.length == 22) {
3963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        // We have a long password.
3973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                        ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17], optiondata[18], optiondata[19], optiondata[20], optiondata[21] };
3983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    }
3993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                } catch (Exception exception) {
4003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    logger.warning("Malformed OPT answer. Option code: Owner data: " + this._hexString(optiondata));
4013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                }
4023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                if (logger.isLoggable(Level.FINE)) {
4033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    logger.fine("Unhandled Owner OPT version: " + ownerVersion + " sequence: " + ownerSequence + " MAC address: " + this._hexString(ownerPrimaryMacAddress)
4043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                            + (ownerWakeupMacAddress != ownerPrimaryMacAddress ? " wakeup MAC address: " + this._hexString(ownerWakeupMacAddress) : "") + (ownerPassword != null ? " password: " + this._hexString(ownerPassword) : ""));
4053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                }
4063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                break;
4073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            case LLQ:
4083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            case NSID:
4093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            case UL:
4103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                if (logger.isLoggable(Level.FINE)) {
4113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                    logger.log(Level.FINE, "There was an OPT answer. Option code: " + optionCode + " data: " + this._hexString(optiondata));
4123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                }
4133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                break;
4143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            case Unknown:
4153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                logger.log(Level.WARNING, "There was an OPT answer. Not currently handled. Option code: " + optionCodeInt + " data: " + this._hexString(optiondata));
4163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                break;
4173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                            default:
4183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                // This is to keep the compiler happy.
4193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                                break;
4203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        }
4213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
4223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
4233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger.log(Level.WARNING, "There was an OPT answer. Wrong version number: " + version + " result code: " + extendedResultCode);
4243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
4253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
4263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            default:
4273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (logger.isLoggable(Level.FINER)) {
4283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    logger.finer("DNSIncoming() unknown type:" + type);
4293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
4303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                _messageInputStream.skip(len);
4313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                break;
4323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (rec != null) {
4343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            rec.setRecordSource(source);
4353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return rec;
4373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
4383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
4403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Debugging.
4413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
4423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    String print(boolean dump) {
4433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        StringBuilder buf = new StringBuilder();
4443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(this.print());
4453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (dump) {
4463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            byte[] data = new byte[_packet.getLength()];
4473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            System.arraycopy(_packet.getData(), 0, data, 0, data.length);
4483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.print(data));
4493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return buf.toString();
4513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
4523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
4543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public String toString() {
4553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        StringBuilder buf = new StringBuilder();
4563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(isQuery() ? "dns[query," : "dns[response,");
4573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (_packet.getAddress() != null) {
4583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(_packet.getAddress().getHostAddress());
4593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(':');
4613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(_packet.getPort());
4623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(", length=");
4633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(_packet.getLength());
4643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(", id=0x");
4653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(Integer.toHexString(this.getId()));
4663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getFlags() != 0) {
4673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", flags=0x");
4683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(Integer.toHexString(this.getFlags()));
4693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
4703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(":r");
4713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
4733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(":aa");
4743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
4763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(":tc");
4773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfQuestions() > 0) {
4803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", questions=");
4813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfQuestions());
4823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAnswers() > 0) {
4843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", answers=");
4853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfAnswers());
4863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAuthorities() > 0) {
4883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", authorities=");
4893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfAuthorities());
4903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAdditionals() > 0) {
4923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", additionals=");
4933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfAdditionals());
4943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfQuestions() > 0) {
4963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nquestions:");
4973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSQuestion question : _questions) {
4983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
4993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(question);
5003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
5013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
5023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAnswers() > 0) {
5033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nanswers:");
5043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSRecord record : _answers) {
5053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
5063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(record);
5073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
5083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
5093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAuthorities() > 0) {
5103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nauthorities:");
5113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSRecord record : _authoritativeAnswers) {
5123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
5133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(record);
5143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
5153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
5163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAdditionals() > 0) {
5173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nadditionals:");
5183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSRecord record : _additionals) {
5193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
5203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(record);
5213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
5223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
5233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append("]");
5243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return buf.toString();
5253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Appends answers to this Incoming.
5293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
5303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IllegalArgumentException
5313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *                If not a query or if Truncated.
5323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    void append(DNSIncoming that) {
5343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.isQuery() && this.isTruncated() && that.isQuery()) {
5353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this._questions.addAll(that.getQuestions());
5363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this._answers.addAll(that.getAnswers());
5373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this._authoritativeAnswers.addAll(that.getAuthorities());
5383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this._additionals.addAll(that.getAdditionals());
5393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } else {
5403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            throw new IllegalArgumentException();
5413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
5423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public int elapseSinceArrival() {
5453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return (int) (System.currentTimeMillis() - _receivedTime);
5463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This will return the default UDP payload except if an OPT record was found with a different size.
5503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
5513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return the senderUDPPayload
5523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public int getSenderUDPPayload() {
5543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._senderUDPPayload;
5553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private static final char[] _nibbleToHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
5583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
5603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Returns a hex-string for printing
5613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
5623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param bytes
5633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return Returns a hex-string which can be used within a SQL expression
5643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
5653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private String _hexString(byte[] bytes) {
5663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        StringBuilder result = new StringBuilder(2 * bytes.length);
5683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (int i = 0; i < bytes.length; i++) {
5703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            int b = bytes[i] & 0xFF;
5713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            result.append(_nibbleToHex[b / 16]);
5723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            result.append(_nibbleToHex[b % 16]);
5733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
5743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return result.toString();
5763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
5773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
5783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman}
579