// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) package org.xbill.DNS; import java.io.*; import java.util.*; /** * A DNS message header * @see Message * * @author Brian Wellington */ public class Header implements Cloneable { private int id; private int flags; private int [] counts; private static Random random = new Random(); /** The length of a DNS Header in wire format. */ public static final int LENGTH = 12; private void init() { counts = new int[4]; flags = 0; id = -1; } /** * Create a new empty header. * @param id The message id */ public Header(int id) { init(); setID(id); } /** * Create a new empty header with a random message id */ public Header() { init(); } /** * Parses a Header from a stream containing DNS wire format. */ Header(DNSInput in) throws IOException { this(in.readU16()); flags = in.readU16(); for (int i = 0; i < counts.length; i++) counts[i] = in.readU16(); } /** * Creates a new Header from its DNS wire format representation * @param b A byte array containing the DNS Header. */ public Header(byte [] b) throws IOException { this(new DNSInput(b)); } void toWire(DNSOutput out) { out.writeU16(getID()); out.writeU16(flags); for (int i = 0; i < counts.length; i++) out.writeU16(counts[i]); } public byte [] toWire() { DNSOutput out = new DNSOutput(); toWire(out); return out.toByteArray(); } static private boolean validFlag(int bit) { return (bit >= 0 && bit <= 0xF && Flags.isFlag(bit)); } static private void checkFlag(int bit) { if (!validFlag(bit)) throw new IllegalArgumentException("invalid flag bit " + bit); } /** * Sets a flag to the supplied value * @see Flags */ public void setFlag(int bit) { checkFlag(bit); // bits are indexed from left to right flags |= (1 << (15 - bit)); } /** * Sets a flag to the supplied value * @see Flags */ public void unsetFlag(int bit) { checkFlag(bit); // bits are indexed from left to right flags &= ~(1 << (15 - bit)); } /** * Retrieves a flag * @see Flags */ public boolean getFlag(int bit) { checkFlag(bit); // bits are indexed from left to right return (flags & (1 << (15 - bit))) != 0; } boolean [] getFlags() { boolean [] array = new boolean[16]; for (int i = 0; i < array.length; i++) if (validFlag(i)) array[i] = getFlag(i); return array; } /** * Retrieves the message ID */ public int getID() { if (id >= 0) return id; synchronized (this) { if (id < 0) id = random.nextInt(0xffff); return id; } } /** * Sets the message ID */ public void setID(int id) { if (id < 0 || id > 0xffff) throw new IllegalArgumentException("DNS message ID " + id + " is out of range"); this.id = id; } /** * Sets the message's rcode * @see Rcode */ public void setRcode(int value) { if (value < 0 || value > 0xF) throw new IllegalArgumentException("DNS Rcode " + value + " is out of range"); flags &= ~0xF; flags |= value; } /** * Retrieves the mesasge's rcode * @see Rcode */ public int getRcode() { return flags & 0xF; } /** * Sets the message's opcode * @see Opcode */ public void setOpcode(int value) { if (value < 0 || value > 0xF) throw new IllegalArgumentException("DNS Opcode " + value + "is out of range"); flags &= 0x87FF; flags |= (value << 11); } /** * Retrieves the mesasge's opcode * @see Opcode */ public int getOpcode() { return (flags >> 11) & 0xF; } void setCount(int field, int value) { if (value < 0 || value > 0xFFFF) throw new IllegalArgumentException("DNS section count " + value + " is out of range"); counts[field] = value; } void incCount(int field) { if (counts[field] == 0xFFFF) throw new IllegalStateException("DNS section count cannot " + "be incremented"); counts[field]++; } void decCount(int field) { if (counts[field] == 0) throw new IllegalStateException("DNS section count cannot " + "be decremented"); counts[field]--; } /** * Retrieves the record count for the given section * @see Section */ public int getCount(int field) { return counts[field]; } /** Converts the header's flags into a String */ public String printFlags() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < 16; i++) if (validFlag(i) && getFlag(i)) { sb.append(Flags.string(i)); sb.append(" "); } return sb.toString(); } String toStringWithRcode(int newrcode) { StringBuffer sb = new StringBuffer(); sb.append(";; ->>HEADER<<- "); sb.append("opcode: " + Opcode.string(getOpcode())); sb.append(", status: " + Rcode.string(newrcode)); sb.append(", id: " + getID()); sb.append("\n"); sb.append(";; flags: " + printFlags()); sb.append("; "); for (int i = 0; i < 4; i++) sb.append(Section.string(i) + ": " + getCount(i) + " "); return sb.toString(); } /** Converts the header into a String */ public String toString() { return toStringWithRcode(getRcode()); } /* Creates a new Header identical to the current one */ public Object clone() { Header h = new Header(); h.id = id; h.flags = flags; System.arraycopy(counts, 0, h.counts, 0, counts.length); return h; } }