1// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.io.*;
6import java.util.*;
7
8/**
9 * A DNS message header
10 * @see Message
11 *
12 * @author Brian Wellington
13 */
14
15public class Header implements Cloneable {
16
17private int id;
18private int flags;
19private int [] counts;
20
21private static Random random = new Random();
22
23/** The length of a DNS Header in wire format. */
24public static final int LENGTH = 12;
25
26private void
27init() {
28	counts = new int[4];
29	flags = 0;
30	id = -1;
31}
32
33/**
34 * Create a new empty header.
35 * @param id The message id
36 */
37public
38Header(int id) {
39	init();
40	setID(id);
41}
42
43/**
44 * Create a new empty header with a random message id
45 */
46public
47Header() {
48	init();
49}
50
51/**
52 * Parses a Header from a stream containing DNS wire format.
53 */
54Header(DNSInput in) throws IOException {
55	this(in.readU16());
56	flags = in.readU16();
57	for (int i = 0; i < counts.length; i++)
58		counts[i] = in.readU16();
59}
60
61/**
62 * Creates a new Header from its DNS wire format representation
63 * @param b A byte array containing the DNS Header.
64 */
65public
66Header(byte [] b) throws IOException {
67	this(new DNSInput(b));
68}
69
70void
71toWire(DNSOutput out) {
72	out.writeU16(getID());
73	out.writeU16(flags);
74	for (int i = 0; i < counts.length; i++)
75		out.writeU16(counts[i]);
76}
77
78public byte []
79toWire() {
80	DNSOutput out = new DNSOutput();
81	toWire(out);
82	return out.toByteArray();
83}
84
85static private boolean
86validFlag(int bit) {
87	return (bit >= 0 && bit <= 0xF && Flags.isFlag(bit));
88}
89
90static private void
91checkFlag(int bit) {
92	if (!validFlag(bit))
93		throw new IllegalArgumentException("invalid flag bit " + bit);
94}
95
96/**
97 * Sets a flag to the supplied value
98 * @see Flags
99 */
100public void
101setFlag(int bit) {
102	checkFlag(bit);
103	// bits are indexed from left to right
104	flags |= (1 << (15 - bit));
105}
106
107/**
108 * Sets a flag to the supplied value
109 * @see Flags
110 */
111public void
112unsetFlag(int bit) {
113	checkFlag(bit);
114	// bits are indexed from left to right
115	flags &= ~(1 << (15 - bit));
116}
117
118/**
119 * Retrieves a flag
120 * @see Flags
121 */
122public boolean
123getFlag(int bit) {
124	checkFlag(bit);
125	// bits are indexed from left to right
126	return (flags & (1 << (15 - bit))) != 0;
127}
128
129boolean []
130getFlags() {
131	boolean [] array = new boolean[16];
132	for (int i = 0; i < array.length; i++)
133		if (validFlag(i))
134			array[i] = getFlag(i);
135	return array;
136}
137
138/**
139 * Retrieves the message ID
140 */
141public int
142getID() {
143	if (id >= 0)
144		return id;
145	synchronized (this) {
146		if (id < 0)
147			id = random.nextInt(0xffff);
148		return id;
149	}
150}
151
152/**
153 * Sets the message ID
154 */
155public void
156setID(int id) {
157	if (id < 0 || id > 0xffff)
158		throw new IllegalArgumentException("DNS message ID " + id +
159						   " is out of range");
160	this.id = id;
161}
162
163/**
164 * Sets the message's rcode
165 * @see Rcode
166 */
167public void
168setRcode(int value) {
169	if (value < 0 || value > 0xF)
170		throw new IllegalArgumentException("DNS Rcode " + value +
171						   " is out of range");
172	flags &= ~0xF;
173	flags |= value;
174}
175
176/**
177 * Retrieves the mesasge's rcode
178 * @see Rcode
179 */
180public int
181getRcode() {
182	return flags & 0xF;
183}
184
185/**
186 * Sets the message's opcode
187 * @see Opcode
188 */
189public void
190setOpcode(int value) {
191	if (value < 0 || value > 0xF)
192		throw new IllegalArgumentException("DNS Opcode " + value +
193						   "is out of range");
194	flags &= 0x87FF;
195	flags |= (value << 11);
196}
197
198/**
199 * Retrieves the mesasge's opcode
200 * @see Opcode
201 */
202public int
203getOpcode() {
204	return (flags >> 11) & 0xF;
205}
206
207void
208setCount(int field, int value) {
209	if (value < 0 || value > 0xFFFF)
210		throw new IllegalArgumentException("DNS section count " +
211						   value + " is out of range");
212	counts[field] = value;
213}
214
215void
216incCount(int field) {
217	if (counts[field] == 0xFFFF)
218		throw new IllegalStateException("DNS section count cannot " +
219						"be incremented");
220	counts[field]++;
221}
222
223void
224decCount(int field) {
225	if (counts[field] == 0)
226		throw new IllegalStateException("DNS section count cannot " +
227						"be decremented");
228	counts[field]--;
229}
230
231/**
232 * Retrieves the record count for the given section
233 * @see Section
234 */
235public int
236getCount(int field) {
237	return counts[field];
238}
239
240/** Converts the header's flags into a String */
241public String
242printFlags() {
243	StringBuffer sb = new StringBuffer();
244
245	for (int i = 0; i < 16; i++)
246		if (validFlag(i) && getFlag(i)) {
247			sb.append(Flags.string(i));
248			sb.append(" ");
249		}
250	return sb.toString();
251}
252
253String
254toStringWithRcode(int newrcode) {
255	StringBuffer sb = new StringBuffer();
256
257	sb.append(";; ->>HEADER<<- ");
258	sb.append("opcode: " + Opcode.string(getOpcode()));
259	sb.append(", status: " + Rcode.string(newrcode));
260	sb.append(", id: " + getID());
261	sb.append("\n");
262
263	sb.append(";; flags: " + printFlags());
264	sb.append("; ");
265	for (int i = 0; i < 4; i++)
266		sb.append(Section.string(i) + ": " + getCount(i) + " ");
267	return sb.toString();
268}
269
270/** Converts the header into a String */
271public String
272toString() {
273	return toStringWithRcode(getRcode());
274}
275
276/* Creates a new Header identical to the current one */
277public Object
278clone() {
279	Header h = new Header();
280	h.id = id;
281	h.flags = flags;
282	System.arraycopy(counts, 0, h.counts, 0, counts.length);
283	return h;
284}
285
286}
287