1
2
3
4
5 package javax.jmdns.impl;
6
7 import java.io.ByteArrayOutputStream;
8 import java.io.IOException;
9 import java.util.HashMap;
10 import java.util.Map;
11
12 import javax.jmdns.impl.constants.DNSConstants;
13 import javax.jmdns.impl.constants.DNSRecordClass;
14
15
16
17
18
19
20 public final class DNSOutgoing extends DNSMessage {
21
22 public static class MessageOutputStream extends ByteArrayOutputStream {
23 private final DNSOutgoing _out;
24
25 private final int _offset;
26
27
28
29
30
31
32
33
34
35 MessageOutputStream(int size, DNSOutgoing out) {
36 this(size, out, 0);
37 }
38
39 MessageOutputStream(int size, DNSOutgoing out, int offset) {
40 super(size);
41 _out = out;
42 _offset = offset;
43 }
44
45 void writeByte(int value) {
46 this.write(value & 0xFF);
47 }
48
49 void writeBytes(String str, int off, int len) {
50 for (int i = 0; i < len; i++) {
51 writeByte(str.charAt(off + i));
52 }
53 }
54
55 void writeBytes(byte data[]) {
56 if (data != null) {
57 writeBytes(data, 0, data.length);
58 }
59 }
60
61 void writeBytes(byte data[], int off, int len) {
62 for (int i = 0; i < len; i++) {
63 writeByte(data[off + i]);
64 }
65 }
66
67 void writeShort(int value) {
68 writeByte(value >> 8);
69 writeByte(value);
70 }
71
72 void writeInt(int value) {
73 writeShort(value >> 16);
74 writeShort(value);
75 }
76
77 void writeUTF(String str, int off, int len) {
78
79 int utflen = 0;
80 for (int i = 0; i < len; i++) {
81 int ch = str.charAt(off + i);
82 if ((ch >= 0x0001) && (ch <= 0x007F)) {
83 utflen += 1;
84 } else {
85 if (ch > 0x07FF) {
86 utflen += 3;
87 } else {
88 utflen += 2;
89 }
90 }
91 }
92
93 writeByte(utflen);
94
95 for (int i = 0; i < len; i++) {
96 int ch = str.charAt(off + i);
97 if ((ch >= 0x0001) && (ch <= 0x007F)) {
98 writeByte(ch);
99 } else {
100 if (ch > 0x07FF) {
101 writeByte(0xE0 | ((ch >> 12) & 0x0F));
102 writeByte(0x80 | ((ch >> 6) & 0x3F));
103 writeByte(0x80 | ((ch >> 0) & 0x3F));
104 } else {
105 writeByte(0xC0 | ((ch >> 6) & 0x1F));
106 writeByte(0x80 | ((ch >> 0) & 0x3F));
107 }
108 }
109 }
110 }
111
112 void writeName(String name) {
113 writeName(name, true);
114 }
115
116 void writeName(String name, boolean useCompression) {
117 String aName = name;
118 while (true) {
119 int n = aName.indexOf('.');
120 if (n < 0) {
121 n = aName.length();
122 }
123 if (n <= 0) {
124 writeByte(0);
125 return;
126 }
127 String label = aName.substring(0, n);
128 if (useCompression && USE_DOMAIN_NAME_COMPRESSION) {
129 Integer offset = _out._names.get(aName);
130 if (offset != null) {
131 int val = offset.intValue();
132 writeByte((val >> 8) | 0xC0);
133 writeByte(val & 0xFF);
134 return;
135 }
136 _out._names.put(aName, Integer.valueOf(this.size() + _offset));
137 writeUTF(label, 0, label.length());
138 } else {
139 writeUTF(label, 0, label.length());
140 }
141 aName = aName.substring(n);
142 if (aName.startsWith(".")) {
143 aName = aName.substring(1);
144 }
145 }
146 }
147
148 void writeQuestion(DNSQuestion question) {
149 writeName(question.getName());
150 writeShort(question.getRecordType().indexValue());
151 writeShort(question.getRecordClass().indexValue());
152 }
153
154 void writeRecord(DNSRecord rec, long now) {
155 writeName(rec.getName());
156 writeShort(rec.getRecordType().indexValue());
157 writeShort(rec.getRecordClass().indexValue() | ((rec.isUnique() && _out.isMulticast()) ? DNSRecordClass.CLASS_UNIQUE : 0));
158 writeInt((now == 0) ? rec.getTTL() : rec.getRemainingTTL(now));
159
160
161 MessageOutputStream record = new MessageOutputStream(512, _out, _offset + this.size() + 2);
162 rec.write(record);
163 byte[] byteArray = record.toByteArray();
164
165 writeShort(byteArray.length);
166 write(byteArray, 0, byteArray.length);
167 }
168
169 }
170
171
172
173
174 public static boolean USE_DOMAIN_NAME_COMPRESSION = true;
175
176 Map<String, Integer> _names;
177
178 private int _maxUDPPayload;
179
180 private final MessageOutputStream _questionsBytes;
181
182 private final MessageOutputStream _answersBytes;
183
184 private final MessageOutputStream _authoritativeAnswersBytes;
185
186 private final MessageOutputStream _additionalsAnswersBytes;
187
188 private final static int HEADER_SIZE = 12;
189
190
191
192
193
194
195 public DNSOutgoing(int flags) {
196 this(flags, true, DNSConstants.MAX_MSG_TYPICAL);
197 }
198
199
200
201
202
203
204
205 public DNSOutgoing(int flags, boolean multicast) {
206 this(flags, multicast, DNSConstants.MAX_MSG_TYPICAL);
207 }
208
209
210
211
212
213
214
215
216
217 public DNSOutgoing(int flags, boolean multicast, int senderUDPPayload) {
218 super(flags, 0, multicast);
219 _names = new HashMap<String, Integer>();
220 _maxUDPPayload = (senderUDPPayload > 0 ? senderUDPPayload : DNSConstants.MAX_MSG_TYPICAL);
221 _questionsBytes = new MessageOutputStream(senderUDPPayload, this);
222 _answersBytes = new MessageOutputStream(senderUDPPayload, this);
223 _authoritativeAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
224 _additionalsAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
225 }
226
227
228
229
230
231
232 public int availableSpace() {
233 return _maxUDPPayload - HEADER_SIZE - _questionsBytes.size() - _answersBytes.size() - _authoritativeAnswersBytes.size() - _additionalsAnswersBytes.size();
234 }
235
236
237
238
239
240
241
242 public void addQuestion(DNSQuestion rec) throws IOException {
243 MessageOutputStream record = new MessageOutputStream(512, this);
244 record.writeQuestion(rec);
245 byte[] byteArray = record.toByteArray();
246 if (byteArray.length < this.availableSpace()) {
247 _questions.add(rec);
248 _questionsBytes.write(byteArray, 0, byteArray.length);
249 } else {
250 throw new IOException("message full");
251 }
252 }
253
254
255
256
257
258
259
260
261 public void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException {
262 if ((in == null) || !rec.suppressedBy(in)) {
263 this.addAnswer(rec, 0);
264 }
265 }
266
267
268
269
270
271
272
273
274 public void addAnswer(DNSRecord rec, long now) throws IOException {
275 if (rec != null) {
276 if ((now == 0) || !rec.isExpired(now)) {
277 MessageOutputStream record = new MessageOutputStream(512, this);
278 record.writeRecord(rec, now);
279 byte[] byteArray = record.toByteArray();
280 if (byteArray.length < this.availableSpace()) {
281 _answers.add(rec);
282 _answersBytes.write(byteArray, 0, byteArray.length);
283 } else {
284 throw new IOException("message full");
285 }
286 }
287 }
288 }
289
290
291
292
293
294
295
296 public void addAuthorativeAnswer(DNSRecord rec) throws IOException {
297 MessageOutputStream record = new MessageOutputStream(512, this);
298 record.writeRecord(rec, 0);
299 byte[] byteArray = record.toByteArray();
300 if (byteArray.length < this.availableSpace()) {
301 _authoritativeAnswers.add(rec);
302 _authoritativeAnswersBytes.write(byteArray, 0, byteArray.length);
303 } else {
304 throw new IOException("message full");
305 }
306 }
307
308
309
310
311
312
313
314
315 public void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException {
316 MessageOutputStream record = new MessageOutputStream(512, this);
317 record.writeRecord(rec, 0);
318 byte[] byteArray = record.toByteArray();
319 if (byteArray.length < this.availableSpace()) {
320 _additionals.add(rec);
321 _additionalsAnswersBytes.write(byteArray, 0, byteArray.length);
322 } else {
323 throw new IOException("message full");
324 }
325 }
326
327
328
329
330
331
332 public byte[] data() {
333 long now = System.currentTimeMillis();
334 _names.clear();
335
336 MessageOutputStream message = new MessageOutputStream(_maxUDPPayload, this);
337 message.writeShort(_multicast ? 0 : this.getId());
338 message.writeShort(this.getFlags());
339 message.writeShort(this.getNumberOfQuestions());
340 message.writeShort(this.getNumberOfAnswers());
341 message.writeShort(this.getNumberOfAuthorities());
342 message.writeShort(this.getNumberOfAdditionals());
343 for (DNSQuestion question : _questions) {
344 message.writeQuestion(question);
345 }
346 for (DNSRecord record : _answers) {
347 message.writeRecord(record, now);
348 }
349 for (DNSRecord record : _authoritativeAnswers) {
350 message.writeRecord(record, now);
351 }
352 for (DNSRecord record : _additionals) {
353 message.writeRecord(record, now);
354 }
355 return message.toByteArray();
356 }
357
358 @Override
359 public boolean isQuery() {
360 return (this.getFlags() & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
361 }
362
363
364
365
366 String print(boolean dump) {
367 StringBuilder buf = new StringBuilder();
368 buf.append(this.print());
369 if (dump) {
370 buf.append(this.print(this.data()));
371 }
372 return buf.toString();
373 }
374
375 @Override
376 public String toString() {
377 StringBuffer buf = new StringBuffer();
378 buf.append(isQuery() ? "dns[query:" : "dns[response:");
379 buf.append(" id=0x");
380 buf.append(Integer.toHexString(this.getId()));
381 if (this.getFlags() != 0) {
382 buf.append(", flags=0x");
383 buf.append(Integer.toHexString(this.getFlags()));
384 if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
385 buf.append(":r");
386 }
387 if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
388 buf.append(":aa");
389 }
390 if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
391 buf.append(":tc");
392 }
393 }
394 if (this.getNumberOfQuestions() > 0) {
395 buf.append(", questions=");
396 buf.append(this.getNumberOfQuestions());
397 }
398 if (this.getNumberOfAnswers() > 0) {
399 buf.append(", answers=");
400 buf.append(this.getNumberOfAnswers());
401 }
402 if (this.getNumberOfAuthorities() > 0) {
403 buf.append(", authorities=");
404 buf.append(this.getNumberOfAuthorities());
405 }
406 if (this.getNumberOfAdditionals() > 0) {
407 buf.append(", additionals=");
408 buf.append(this.getNumberOfAdditionals());
409 }
410 if (this.getNumberOfQuestions() > 0) {
411 buf.append("\nquestions:");
412 for (DNSQuestion question : _questions) {
413 buf.append("\n\t");
414 buf.append(question);
415 }
416 }
417 if (this.getNumberOfAnswers() > 0) {
418 buf.append("\nanswers:");
419 for (DNSRecord record : _answers) {
420 buf.append("\n\t");
421 buf.append(record);
422 }
423 }
424 if (this.getNumberOfAuthorities() > 0) {
425 buf.append("\nauthorities:");
426 for (DNSRecord record : _authoritativeAnswers) {
427 buf.append("\n\t");
428 buf.append(record);
429 }
430 }
431 if (this.getNumberOfAdditionals() > 0) {
432 buf.append("\nadditionals:");
433 for (DNSRecord record : _additionals) {
434 buf.append("\n\t");
435 buf.append(record);
436 }
437 }
438 buf.append("\nnames=");
439 buf.append(_names);
440 buf.append("]");
441 return buf.toString();
442 }
443
444
445
446
447 public int getMaxUDPPayload() {
448 return this._maxUDPPayload;
449 }
450
451 }