| // 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; |
| } |
| |
| } |