1package de.measite.minidns;
2
3import java.io.ByteArrayOutputStream;
4import java.io.DataInputStream;
5import java.io.DataOutputStream;
6import java.io.IOException;
7import java.util.Arrays;
8
9import de.measite.minidns.Record.CLASS;
10import de.measite.minidns.Record.TYPE;
11import de.measite.minidns.util.NameUtil;
12
13/**
14 * A DNS question (request).
15 */
16public class Question {
17
18 /**
19 * The question string (e.g. "measite.de").
20 */
21 private final String name;
22
23 /**
24 * The question type (e.g. A).
25 */
26 private final TYPE type;
27
28 /**
29 * The question class (usually IN / internet).
30 */
31 private final CLASS clazz;
32
33 /**
34 * UnicastQueries have the highest bit of the CLASS field set to 1.
35 */
36 private final boolean unicastQuery;
37
38 /**
39 * Cache for the serialized object.
40 */
41 private byte[] byteArray;
42
43 /**
44 * Create a dns question for the given name/type/class.
45 * @param name The name e.g. "measite.de".
46 * @param type The type, e.g. A.
47 * @param clazz The class, usually IN (internet).
48 */
49 public Question(String name, TYPE type, CLASS clazz, boolean unicastQuery) {
50 this.name = name;
51 this.type = type;
52 this.clazz = clazz;
53 this.unicastQuery = unicastQuery;
54 }
55
56 /**
57 * Create a dns question for the given name/type/class.
58 * @param name The name e.g. "measite.de".
59 * @param type The type, e.g. A.
60 * @param clazz The class, usually IN (internet).
61 */
62 public Question(String name, TYPE type, CLASS clazz) {
63 this(name, type, clazz, false);
64 }
65
66 /**
67 * Create a dns question for the given name/type/IN (internet class).
68 * @param name The name e.g. "measite.de".
69 * @param type The type, e.g. A.
70 */
71 public Question(String name, TYPE type) {
72 this(name, type, CLASS.IN);
73 }
74
75 /**
76 * Retrieve the type of this question.
77 * @return The type.
78 */
79 public TYPE getType() {
80 return type;
81 }
82
83 /**
84 * Retrieve the class of this dns question (usually internet).
85 * @return The class of this dns question.
86 */
87 public CLASS getClazz() {
88 return clazz;
89 }
90
91 /**
92 * Retrieve the name of this dns question (e.g. "measite.de").
93 * @return The name of this dns question.
94 */
95 public String getName() {
96 return name;
97 }
98
99 /**
100 * Parse a byte array and rebuild the dns question from it.
101 * @param dis The input stream.
102 * @param data The plain data (for dns name references).
103 * @return The parsed dns question.
104 * @throws IOException On errors (read outside of packet).
105 */
106 public static Question parse(DataInputStream dis, byte[] data) throws IOException {
107 String name = NameUtil.parse(dis, data);
108 TYPE type = TYPE.getType(dis.readUnsignedShort());
109 CLASS clazz = CLASS.getClass(dis.readUnsignedShort());
110 return new Question (name, type, clazz);
111 }
112
113 /**
114 * Generate a binary paket for this dns question.
115 * @return The dns question.
116 */
117 public byte[] toByteArray() {
118 if (byteArray == null) {
119 ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
120 DataOutputStream dos = new DataOutputStream(baos);
121
122 try {
123 dos.write(NameUtil.toByteArray(this.name));
124 dos.writeShort(type.getValue());
125 dos.writeShort(clazz.getValue() | (unicastQuery ? (1 << 15) : 0));
126 dos.flush();
127 } catch (IOException e) {
128 // Should never happen
129 throw new IllegalStateException(e);
130 }
131 byteArray = baos.toByteArray();
132 }
133 return byteArray;
134 }
135
136 @Override
137 public int hashCode() {
138 return Arrays.hashCode(toByteArray());
139 }
140
141 @Override
142 public boolean equals(Object other) {
143 if (this == other) {
144 return true;
145 }
146 if (!(other instanceof Question)) {
147 return false;
148 }
149 byte t[] = toByteArray();
150 byte o[] = ((Question)other).toByteArray();
151 return Arrays.equals(t, o);
152 }
153
154 @Override
155 public String toString() {
156 return "Question/" + clazz + "/" + type + ": " + name;
157 }
158}