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.ByteArrayOutputStream;
83742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.io.IOException;
93742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.HashMap;
103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport java.util.Map;
113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSConstants;
133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanimport javax.jmdns.impl.constants.DNSRecordClass;
143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman/**
163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * An outgoing DNS message.
173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman *
183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman * @author Arthur van Hoff, Rick Blair, Werner Randelshofer
193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman */
203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Romanpublic final class DNSOutgoing extends DNSMessage {
213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static class MessageOutputStream extends ByteArrayOutputStream {
233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private final DNSOutgoing _out;
243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        private final int         _offset;
263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        /**
283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * Creates a new message stream, with a buffer capacity of the specified size, in bytes.
293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *
303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @param size
313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *            the initial size.
323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         * @exception IllegalArgumentException
333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         *                if size is negative.
343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman         */
353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        MessageOutputStream(int size, DNSOutgoing out) {
363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this(size, out, 0);
373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        MessageOutputStream(int size, DNSOutgoing out, int offset) {
403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            super(size);
413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _out = out;
423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _offset = offset;
433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeByte(int value) {
463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.write(value & 0xFF);
473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeBytes(String str, int off, int len) {
503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (int i = 0; i < len; i++) {
513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                writeByte(str.charAt(off + i));
523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeBytes(byte data[]) {
563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if (data != null) {
573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                writeBytes(data, 0, data.length);
583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeBytes(byte data[], int off, int len) {
623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (int i = 0; i < len; i++) {
633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                writeByte(data[off + i]);
643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeShort(int value) {
683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeByte(value >> 8);
693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeByte(value);
703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeInt(int value) {
733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeShort(value >> 16);
743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeShort(value);
753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeUTF(String str, int off, int len) {
783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // compute utf length
793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            int utflen = 0;
803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (int i = 0; i < len; i++) {
813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int ch = str.charAt(off + i);
823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if ((ch >= 0x0001) && (ch <= 0x007F)) {
833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    utflen += 1;
843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (ch > 0x07FF) {
863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        utflen += 3;
873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    } else {
883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        utflen += 2;
893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // write utf length
933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeByte(utflen);
943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // write utf data
953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (int i = 0; i < len; i++) {
963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int ch = str.charAt(off + i);
973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if ((ch >= 0x0001) && (ch <= 0x007F)) {
983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    writeByte(ch);
993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
1003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (ch > 0x07FF) {
1013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        writeByte(0xE0 | ((ch >> 12) & 0x0F));
1023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        writeByte(0x80 | ((ch >> 6) & 0x3F));
1033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        writeByte(0x80 | ((ch >> 0) & 0x3F));
1043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    } else {
1053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        writeByte(0xC0 | ((ch >> 6) & 0x1F));
1063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        writeByte(0x80 | ((ch >> 0) & 0x3F));
1073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
1083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeName(String name) {
1133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeName(name, true);
1143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeName(String name, boolean useCompression) {
1173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            String aName = name;
1183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            while (true) {
1193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                int n = aName.indexOf('.');
1203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (n < 0) {
1213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    n = aName.length();
1223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (n <= 0) {
1243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    writeByte(0);
1253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    return;
1263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                String label = aName.substring(0, n);
1283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (useCompression && USE_DOMAIN_NAME_COMPRESSION) {
1293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    Integer offset = _out._names.get(aName);
1303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    if (offset != null) {
1313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        int val = offset.intValue();
1323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        writeByte((val >> 8) | 0xC0);
1333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        writeByte(val & 0xFF);
1343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                        return;
1353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    }
1363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _out._names.put(aName, Integer.valueOf(this.size() + _offset));
1373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    writeUTF(label, 0, label.length());
1383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
1393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    writeUTF(label, 0, label.length());
1403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                aName = aName.substring(n);
1423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (aName.startsWith(".")) {
1433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    aName = aName.substring(1);
1443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
1453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
1463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeQuestion(DNSQuestion question) {
1493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeName(question.getName());
1503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeShort(question.getRecordType().indexValue());
1513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeShort(question.getRecordClass().indexValue());
1523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        void writeRecord(DNSRecord rec, long now) {
1553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeName(rec.getName());
1563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeShort(rec.getRecordType().indexValue());
1573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeShort(rec.getRecordClass().indexValue() | ((rec.isUnique() && _out.isMulticast()) ? DNSRecordClass.CLASS_UNIQUE : 0));
1583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeInt((now == 0) ? rec.getTTL() : rec.getRemainingTTL(now));
1593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            // We need to take into account the 2 size bytes
1613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            MessageOutputStream record = new MessageOutputStream(512, _out, _offset + this.size() + 2);
1623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            rec.write(record);
1633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            byte[] byteArray = record.toByteArray();
1643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            writeShort(byteArray.length);
1663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            write(byteArray, 0, byteArray.length);
1673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
1683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
1703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
1723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * This can be used to turn off domain name compression. This was helpful for tracking problems interacting with other mdns implementations.
1733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
1743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public static boolean             USE_DOMAIN_NAME_COMPRESSION = true;
1753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    Map<String, Integer>              _names;
1773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private int                       _maxUDPPayload;
1793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final MessageOutputStream _questionsBytes;
1813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final MessageOutputStream _answersBytes;
1833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final MessageOutputStream _authoritativeAnswersBytes;
1853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final MessageOutputStream _additionalsAnswersBytes;
1873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    private final static int          HEADER_SIZE                 = 12;
1893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
1913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Create an outgoing multicast query or response.
1923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
1933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param flags
1943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
1953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSOutgoing(int flags) {
1963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this(flags, true, DNSConstants.MAX_MSG_TYPICAL);
1973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
1983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
1993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Create an outgoing query or response.
2013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param flags
2033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param multicast
2043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSOutgoing(int flags, boolean multicast) {
2063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        this(flags, multicast, DNSConstants.MAX_MSG_TYPICAL);
2073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Create an outgoing query or response.
2113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param flags
2133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param multicast
2143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param senderUDPPayload
2153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *            The sender's UDP payload size is the number of bytes of the largest UDP payload that can be reassembled and delivered in the sender's network stack.
2163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public DNSOutgoing(int flags, boolean multicast, int senderUDPPayload) {
2183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        super(flags, 0, multicast);
2193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _names = new HashMap<String, Integer>();
2203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _maxUDPPayload = (senderUDPPayload > 0 ? senderUDPPayload : DNSConstants.MAX_MSG_TYPICAL);
2213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _questionsBytes = new MessageOutputStream(senderUDPPayload, this);
2223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _answersBytes = new MessageOutputStream(senderUDPPayload, this);
2233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _authoritativeAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
2243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _additionalsAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
2253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Return the number of byte available in the message.
2293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return available space
2313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public int availableSpace() {
2333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return _maxUDPPayload - HEADER_SIZE - _questionsBytes.size() - _answersBytes.size() - _authoritativeAnswersBytes.size() - _additionalsAnswersBytes.size();
2343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Add a question to the message.
2383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param rec
2403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
2413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addQuestion(DNSQuestion rec) throws IOException {
2433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        MessageOutputStream record = new MessageOutputStream(512, this);
2443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        record.writeQuestion(rec);
2453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        byte[] byteArray = record.toByteArray();
2463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (byteArray.length < this.availableSpace()) {
2473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _questions.add(rec);
2483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _questionsBytes.write(byteArray, 0, byteArray.length);
2493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } else {
2503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            throw new IOException("message full");
2513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Add an answer if it is not suppressed.
2563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param in
2583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param rec
2593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
2603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException {
2623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if ((in == null) || !rec.suppressedBy(in)) {
2633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            this.addAnswer(rec, 0);
2643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Add an answer to the message.
2693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param rec
2713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param now
2723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
2733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addAnswer(DNSRecord rec, long now) throws IOException {
2753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (rec != null) {
2763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((now == 0) || !rec.isExpired(now)) {
2773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                MessageOutputStream record = new MessageOutputStream(512, this);
2783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                record.writeRecord(rec, now);
2793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                byte[] byteArray = record.toByteArray();
2803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                if (byteArray.length < this.availableSpace()) {
2813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _answers.add(rec);
2823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    _answersBytes.write(byteArray, 0, byteArray.length);
2833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                } else {
2843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                    throw new IOException("message full");
2853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                }
2863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
2873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
2883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
2893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
2903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
2913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Add an authoritative answer to the message.
2923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
2933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param rec
2943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
2953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
2963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addAuthorativeAnswer(DNSRecord rec) throws IOException {
2973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        MessageOutputStream record = new MessageOutputStream(512, this);
2983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        record.writeRecord(rec, 0);
2993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        byte[] byteArray = record.toByteArray();
3003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (byteArray.length < this.availableSpace()) {
3013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _authoritativeAnswers.add(rec);
3023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _authoritativeAnswersBytes.write(byteArray, 0, byteArray.length);
3033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } else {
3043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            throw new IOException("message full");
3053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
3073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Add an additional answer to the record. Omit if there is no room.
3103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
3113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param in
3123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @param rec
3133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @exception IOException
3143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException {
3163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        MessageOutputStream record = new MessageOutputStream(512, this);
3173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        record.writeRecord(rec, 0);
3183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        byte[] byteArray = record.toByteArray();
3193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (byteArray.length < this.availableSpace()) {
3203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _additionals.add(rec);
3213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            _additionalsAnswersBytes.write(byteArray, 0, byteArray.length);
3223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        } else {
3233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            throw new IOException("message full");
3243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
3263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Builds the final message buffer to be send and returns it.
3293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     *
3303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return bytes to send.
3313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public byte[] data() {
3333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        long now = System.currentTimeMillis(); // System.currentTimeMillis()
3343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        _names.clear();
3353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        MessageOutputStream message = new MessageOutputStream(_maxUDPPayload, this);
3373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        message.writeShort(_multicast ? 0 : this.getId());
3383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        message.writeShort(this.getFlags());
3393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        message.writeShort(this.getNumberOfQuestions());
3403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        message.writeShort(this.getNumberOfAnswers());
3413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        message.writeShort(this.getNumberOfAuthorities());
3423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        message.writeShort(this.getNumberOfAdditionals());
3433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSQuestion question : _questions) {
3443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            message.writeQuestion(question);
3453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSRecord record : _answers) {
3473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            message.writeRecord(record, now);
3483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSRecord record : _authoritativeAnswers) {
3503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            message.writeRecord(record, now);
3513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3523742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        for (DNSRecord record : _additionals) {
3533742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            message.writeRecord(record, now);
3543742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3553742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return message.toByteArray();
3563742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
3573742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3583742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
3593742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public boolean isQuery() {
3603742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return (this.getFlags() & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
3613742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
3623742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3633742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
3643742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * Debugging.
3653742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
3663742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    String print(boolean dump) {
3673742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        StringBuilder buf = new StringBuilder();
3683742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(this.print());
3693742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (dump) {
3703742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.print(this.data()));
3713742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3723742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return buf.toString();
3733742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
3743742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
3753742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    @Override
3763742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public String toString() {
3773742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        StringBuffer buf = new StringBuffer();
3783742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(isQuery() ? "dns[query:" : "dns[response:");
3793742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(" id=0x");
3803742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(Integer.toHexString(this.getId()));
3813742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getFlags() != 0) {
3823742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", flags=0x");
3833742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(Integer.toHexString(this.getFlags()));
3843742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
3853742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(":r");
3863742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
3873742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
3883742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(":aa");
3893742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
3903742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
3913742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(":tc");
3923742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
3933742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3943742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfQuestions() > 0) {
3953742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", questions=");
3963742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfQuestions());
3973742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
3983742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAnswers() > 0) {
3993742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", answers=");
4003742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfAnswers());
4013742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4023742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAuthorities() > 0) {
4033742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", authorities=");
4043742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfAuthorities());
4053742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4063742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAdditionals() > 0) {
4073742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(", additionals=");
4083742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append(this.getNumberOfAdditionals());
4093742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4103742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfQuestions() > 0) {
4113742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nquestions:");
4123742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSQuestion question : _questions) {
4133742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
4143742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(question);
4153742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4163742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4173742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAnswers() > 0) {
4183742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nanswers:");
4193742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSRecord record : _answers) {
4203742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
4213742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(record);
4223742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4233742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4243742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAuthorities() > 0) {
4253742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nauthorities:");
4263742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSRecord record : _authoritativeAnswers) {
4273742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
4283742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(record);
4293742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4303742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4313742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        if (this.getNumberOfAdditionals() > 0) {
4323742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            buf.append("\nadditionals:");
4333742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            for (DNSRecord record : _additionals) {
4343742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append("\n\t");
4353742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman                buf.append(record);
4363742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman            }
4373742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        }
4383742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append("\nnames=");
4393742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append(_names);
4403742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        buf.append("]");
4413742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return buf.toString();
4423742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
4433742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4443742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    /**
4453742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     * @return the maxUDPPayload
4463742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman     */
4473742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    public int getMaxUDPPayload() {
4483742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman        return this._maxUDPPayload;
4493742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman    }
4503742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman
4513742d9db8b6edb10627b0f89336cca5249f1d15aManuel Roman}
452