Source code: com/strangeberry/rendezvous/DNSIncoming.java
1 // Copyright (C) 2002 Strangeberry Inc.
2 // @(#)DNSIncoming.java, 1.23, 01/13/2003
3 //
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 //
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 //
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18 package com.strangeberry.rendezvous;
19
20 import java.io.*;
21 import java.net.*;
22 import java.util.*;
23
24 /**
25 * Parse an incoming DNS message into its components.
26 *
27 * @author Arthur van Hoff
28 * @version 1.18, 10/14/2002
29 */
30 final class DNSIncoming extends DNSConstants
31 {
32 final static Vector EMPTY = new Vector();
33
34 DatagramPacket packet;
35 int off;
36 int len;
37 byte data[];
38
39 int id;
40 int flags;
41 int numQuestions;
42 int numAnswers;
43 int numAuthorities;
44 int numAdditionals;
45
46 Vector questions;
47 Vector answers;
48
49 /**
50 * Parse a message from a datagram packet.
51 */
52 DNSIncoming(DatagramPacket packet) throws IOException
53 {
54 this.packet = packet;
55 this.data = packet.getData();
56 this.len = packet.getLength();
57 this.off = packet.getOffset();
58 this.questions = EMPTY;
59 this.answers = EMPTY;
60
61 try {
62 id = readUnsignedShort();
63 flags = readUnsignedShort();
64 numQuestions = readUnsignedShort();
65 numAnswers = readUnsignedShort();
66 numAuthorities = readUnsignedShort();
67 numAdditionals = readUnsignedShort();
68
69 // parse questions
70 if (numQuestions > 0) {
71 questions = new Vector(numQuestions);
72 for (int i = 0 ; i < numQuestions ; i++) {
73 DNSQuestion question = new DNSQuestion(readName(), readUnsignedShort(), readUnsignedShort());
74 questions.add(question);
75 }
76 }
77
78 // parse answers
79 int n = numAnswers + numAuthorities + numAdditionals;
80 if (n > 0) {
81 answers = new Vector(n);
82 for (int i = 0 ; i < n ; i++) {
83 String domain = readName();
84 int type = readUnsignedShort();
85 int clazz = readUnsignedShort();
86 int ttl = readInt();
87 int len = readUnsignedShort();
88 int end = off + len;
89 DNSRecord rec = null;
90
91 switch (type) {
92 case TYPE_A:
93 rec = new DNSRecord.Address(domain, type, clazz, ttl, readInt());
94 break;
95 case TYPE_CNAME:
96 case TYPE_PTR:
97 rec = new DNSRecord.Pointer(domain, type, clazz, ttl, readName());
98 break;
99 case TYPE_TXT:
100 rec = new DNSRecord.Text(domain, type, clazz, ttl, readBytes(off, len));
101 break;
102 case TYPE_SRV:
103 rec = new DNSRecord.Service(domain, type, clazz, ttl,
104 readUnsignedShort(), readUnsignedShort(), readUnsignedShort(), readName());
105 break;
106 }
107 if (rec != null) {
108 answers.add(rec);
109 }
110 off = end;
111 }
112 }
113 } catch (IOException e) {
114 print(true);
115 throw e;
116 }
117 }
118
119 /**
120 * Check if the message is a query.
121 */
122 boolean isQuery()
123 {
124 return (flags & FLAGS_QR_MASK) == FLAGS_QR_QUERY;
125 }
126
127 /**
128 * Check if the message is a response.
129 */
130 boolean isResponse()
131 {
132 return (flags & FLAGS_QR_MASK) == FLAGS_QR_RESPONSE;
133 }
134
135 int get(int off) throws IOException
136 {
137 if ((off < 0) || (off >= len)) {
138 throw new IOException("parser error: offset=" + off);
139 }
140 return data[off] & 0xFF;
141 }
142
143 int readUnsignedShort() throws IOException
144 {
145 return (get(off++) << 8) + get(off++);
146 }
147
148 int readInt() throws IOException
149 {
150 return (readUnsignedShort() << 16) + readUnsignedShort();
151 }
152
153 byte[] readBytes(int off, int len) throws IOException
154 {
155 byte bytes[] = new byte[len];
156 System.arraycopy(data, off, bytes, 0, len);
157 return bytes;
158 }
159
160 void readUTF(StringBuffer buf, int off, int len) throws IOException
161 {
162 for (int end = off + len ; off < end ; ) {
163 int ch = get(off++);
164 switch (ch >> 4) {
165 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
166 // 0xxxxxxx
167 break;
168 case 12: case 13:
169 // 110x xxxx 10xx xxxx
170 ch = ((ch & 0x1F) << 6) | (get(off++) & 0x3F);
171 break;
172 case 14:
173 // 1110 xxxx 10xx xxxx 10xx xxxx
174 ch = ((ch & 0x0f) << 12) | ((get(off++) & 0x3F) << 6) | (get(off++) & 0x3F);
175 break;
176 default:
177 // 10xx xxxx, 1111 xxxx
178 ch = ((ch & 0x3F) << 4) | (get(off++) & 0x0f);
179 break;
180 }
181 buf.append((char)ch);
182 }
183 }
184
185 String readName() throws IOException
186 {
187 StringBuffer buf = new StringBuffer();
188 int off = this.off;
189 int next = -1;
190
191 while (true) {
192 int len = get(off++);
193 if (len == 0) {
194 break;
195 }
196 switch (len & 0xC0) {
197 case 0x00:
198 //buf.append("[" + off + "]");
199 readUTF(buf, off, len);
200 off += len;
201 buf.append('.');
202 break;
203 case 0xC0:
204 //buf.append("<" + (off - 1) + ">");
205 if (next < 0) {
206 next = off + 1;
207 }
208 off = ((len & 0x3F) << 8) | get(off++);
209 break;
210 default:
211 throw new IOException("bad domain name: '" + buf + "' at " + off);
212 }
213 }
214 this.off = (next >= 0) ? next : off;
215 return buf.toString();
216 }
217
218 /**
219 * Debugging.
220 */
221 void print(boolean dump)
222 {
223 System.out.println(toString());
224 for (Enumeration e = questions.elements() ; e.hasMoreElements() ;) {
225 System.out.println(" " + e.nextElement());
226 }
227 for (Enumeration e = answers.elements() ; e.hasMoreElements() ;) {
228 System.out.println(" " + e.nextElement());
229 }
230 if (dump) {
231 for (int off = 0, len = packet.getLength() ; off < len ; off += 32) {
232 int n = Math.min(32, len - off);
233 if (off < 10) {
234 System.out.print(' ');
235 }
236 if (off < 100) {
237 System.out.print(' ');
238 }
239 System.out.print(off);
240 System.out.print(':');
241 for (int i = 0 ; i < n ; i++) {
242 if ((i % 8) == 0) {
243 System.out.print(' ');
244 }
245 System.out.print(Integer.toHexString((data[off + i] & 0xF0) >> 4));
246 System.out.print(Integer.toHexString((data[off + i] & 0x0F) >> 0));
247 }
248 System.out.println();
249 System.out.print(" ");
250 for (int i = 0 ; i < n ; i++) {
251 if ((i % 8) == 0) {
252 System.out.print(' ');
253 }
254 System.out.print(' ');
255 int ch = data[off + i] & 0xFF;
256 System.out.print(((ch > ' ') && (ch < 127)) ? (char)ch : '.');
257 }
258 System.out.println();
259
260 // limit message size
261 if (off+32 >= 256) {
262 System.out.println("....");
263 break;
264 }
265 }
266 }
267 }
268
269 public String toString()
270 {
271 StringBuffer buf = new StringBuffer();
272 buf.append(isQuery() ? "dns[query," : "dns[response,");
273 buf.append(packet.getAddress().getHostAddress());
274 buf.append(':');
275 buf.append(packet.getPort());
276 buf.append(",len=" + packet.getLength());
277 buf.append(",id=0x" + Integer.toHexString(id));
278 if (flags != 0) {
279 buf.append(",flags=0x" + Integer.toHexString(flags));
280 if ((flags & FLAGS_QR_RESPONSE) != 0) {
281 buf.append(":r");
282 }
283 if ((flags & FLAGS_AA) != 0) {
284 buf.append(":aa");
285 }
286 if ((flags & FLAGS_TC) != 0) {
287 buf.append(":rc");
288 }
289 }
290 if (numQuestions > 0) {
291 buf.append(",questions=" + numQuestions);
292 }
293 if (numAnswers > 0) {
294 buf.append(",answers=" + numAnswers);
295 }
296 if (numAuthorities > 0) {
297 buf.append(",authorities=" + numAuthorities);
298 }
299 if (numAdditionals > 0) {
300 buf.append(",additionals=" + numAdditionals);
301 }
302 buf.append("]");
303 return buf.toString();
304 }
305 }