$search
00001 // Copyright 2003-2005 Arthur van Hoff, Rick Blair 00002 // Licensed under Apache License version 2.0 00003 // Original license LGPL 00004 00005 package javax.jmdns.impl; 00006 00007 import java.io.ByteArrayOutputStream; 00008 import java.io.IOException; 00009 import java.util.HashMap; 00010 import java.util.Map; 00011 00012 import javax.jmdns.impl.constants.DNSConstants; 00013 import javax.jmdns.impl.constants.DNSRecordClass; 00014 00020 public final class DNSOutgoing extends DNSMessage { 00021 00022 public static class MessageOutputStream extends ByteArrayOutputStream { 00023 private final DNSOutgoing _out; 00024 00025 private final int _offset; 00026 00035 MessageOutputStream(int size, DNSOutgoing out) { 00036 this(size, out, 0); 00037 } 00038 00039 MessageOutputStream(int size, DNSOutgoing out, int offset) { 00040 super(size); 00041 _out = out; 00042 _offset = offset; 00043 } 00044 00045 void writeByte(int value) { 00046 this.write(value & 0xFF); 00047 } 00048 00049 void writeBytes(String str, int off, int len) { 00050 for (int i = 0; i < len; i++) { 00051 writeByte(str.charAt(off + i)); 00052 } 00053 } 00054 00055 void writeBytes(byte data[]) { 00056 if (data != null) { 00057 writeBytes(data, 0, data.length); 00058 } 00059 } 00060 00061 void writeBytes(byte data[], int off, int len) { 00062 for (int i = 0; i < len; i++) { 00063 writeByte(data[off + i]); 00064 } 00065 } 00066 00067 void writeShort(int value) { 00068 writeByte(value >> 8); 00069 writeByte(value); 00070 } 00071 00072 void writeInt(int value) { 00073 writeShort(value >> 16); 00074 writeShort(value); 00075 } 00076 00077 void writeUTF(String str, int off, int len) { 00078 // compute utf length 00079 int utflen = 0; 00080 for (int i = 0; i < len; i++) { 00081 int ch = str.charAt(off + i); 00082 if ((ch >= 0x0001) && (ch <= 0x007F)) { 00083 utflen += 1; 00084 } else { 00085 if (ch > 0x07FF) { 00086 utflen += 3; 00087 } else { 00088 utflen += 2; 00089 } 00090 } 00091 } 00092 // write utf length 00093 writeByte(utflen); 00094 // write utf data 00095 for (int i = 0; i < len; i++) { 00096 int ch = str.charAt(off + i); 00097 if ((ch >= 0x0001) && (ch <= 0x007F)) { 00098 writeByte(ch); 00099 } else { 00100 if (ch > 0x07FF) { 00101 writeByte(0xE0 | ((ch >> 12) & 0x0F)); 00102 writeByte(0x80 | ((ch >> 6) & 0x3F)); 00103 writeByte(0x80 | ((ch >> 0) & 0x3F)); 00104 } else { 00105 writeByte(0xC0 | ((ch >> 6) & 0x1F)); 00106 writeByte(0x80 | ((ch >> 0) & 0x3F)); 00107 } 00108 } 00109 } 00110 } 00111 00112 void writeName(String name) { 00113 writeName(name, true); 00114 } 00115 00116 void writeName(String name, boolean useCompression) { 00117 String aName = name; 00118 while (true) { 00119 int n = aName.indexOf('.'); 00120 if (n < 0) { 00121 n = aName.length(); 00122 } 00123 if (n <= 0) { 00124 writeByte(0); 00125 return; 00126 } 00127 String label = aName.substring(0, n); 00128 if (useCompression && USE_DOMAIN_NAME_COMPRESSION) { 00129 Integer offset = _out._names.get(aName); 00130 if (offset != null) { 00131 int val = offset.intValue(); 00132 writeByte((val >> 8) | 0xC0); 00133 writeByte(val & 0xFF); 00134 return; 00135 } 00136 _out._names.put(aName, Integer.valueOf(this.size() + _offset)); 00137 writeUTF(label, 0, label.length()); 00138 } else { 00139 writeUTF(label, 0, label.length()); 00140 } 00141 aName = aName.substring(n); 00142 if (aName.startsWith(".")) { 00143 aName = aName.substring(1); 00144 } 00145 } 00146 } 00147 00148 void writeQuestion(DNSQuestion question) { 00149 writeName(question.getName()); 00150 writeShort(question.getRecordType().indexValue()); 00151 writeShort(question.getRecordClass().indexValue()); 00152 } 00153 00154 void writeRecord(DNSRecord rec, long now) { 00155 writeName(rec.getName()); 00156 writeShort(rec.getRecordType().indexValue()); 00157 writeShort(rec.getRecordClass().indexValue() | ((rec.isUnique() && _out.isMulticast()) ? DNSRecordClass.CLASS_UNIQUE : 0)); 00158 writeInt((now == 0) ? rec.getTTL() : rec.getRemainingTTL(now)); 00159 00160 // We need to take into account the 2 size bytes 00161 MessageOutputStream record = new MessageOutputStream(512, _out, _offset + this.size() + 2); 00162 rec.write(record); 00163 byte[] byteArray = record.toByteArray(); 00164 00165 writeShort(byteArray.length); 00166 write(byteArray, 0, byteArray.length); 00167 } 00168 00169 } 00170 00174 public static boolean USE_DOMAIN_NAME_COMPRESSION = true; 00175 00176 Map<String, Integer> _names; 00177 00178 private int _maxUDPPayload; 00179 00180 private final MessageOutputStream _questionsBytes; 00181 00182 private final MessageOutputStream _answersBytes; 00183 00184 private final MessageOutputStream _authoritativeAnswersBytes; 00185 00186 private final MessageOutputStream _additionalsAnswersBytes; 00187 00188 private final static int HEADER_SIZE = 12; 00189 00195 public DNSOutgoing(int flags) { 00196 this(flags, true, DNSConstants.MAX_MSG_TYPICAL); 00197 } 00198 00205 public DNSOutgoing(int flags, boolean multicast) { 00206 this(flags, multicast, DNSConstants.MAX_MSG_TYPICAL); 00207 } 00208 00217 public DNSOutgoing(int flags, boolean multicast, int senderUDPPayload) { 00218 super(flags, 0, multicast); 00219 _names = new HashMap<String, Integer>(); 00220 _maxUDPPayload = (senderUDPPayload > 0 ? senderUDPPayload : DNSConstants.MAX_MSG_TYPICAL); 00221 _questionsBytes = new MessageOutputStream(senderUDPPayload, this); 00222 _answersBytes = new MessageOutputStream(senderUDPPayload, this); 00223 _authoritativeAnswersBytes = new MessageOutputStream(senderUDPPayload, this); 00224 _additionalsAnswersBytes = new MessageOutputStream(senderUDPPayload, this); 00225 } 00226 00232 public int availableSpace() { 00233 return _maxUDPPayload - HEADER_SIZE - _questionsBytes.size() - _answersBytes.size() - _authoritativeAnswersBytes.size() - _additionalsAnswersBytes.size(); 00234 } 00235 00242 public void addQuestion(DNSQuestion rec) throws IOException { 00243 MessageOutputStream record = new MessageOutputStream(512, this); 00244 record.writeQuestion(rec); 00245 byte[] byteArray = record.toByteArray(); 00246 if (byteArray.length < this.availableSpace()) { 00247 _questions.add(rec); 00248 _questionsBytes.write(byteArray, 0, byteArray.length); 00249 } else { 00250 throw new IOException("message full"); 00251 } 00252 } 00253 00261 public void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException { 00262 if ((in == null) || !rec.suppressedBy(in)) { 00263 this.addAnswer(rec, 0); 00264 } 00265 } 00266 00274 public void addAnswer(DNSRecord rec, long now) throws IOException { 00275 if (rec != null) { 00276 if ((now == 0) || !rec.isExpired(now)) { 00277 MessageOutputStream record = new MessageOutputStream(512, this); 00278 record.writeRecord(rec, now); 00279 byte[] byteArray = record.toByteArray(); 00280 if (byteArray.length < this.availableSpace()) { 00281 _answers.add(rec); 00282 _answersBytes.write(byteArray, 0, byteArray.length); 00283 } else { 00284 throw new IOException("message full"); 00285 } 00286 } 00287 } 00288 } 00289 00296 public void addAuthorativeAnswer(DNSRecord rec) throws IOException { 00297 MessageOutputStream record = new MessageOutputStream(512, this); 00298 record.writeRecord(rec, 0); 00299 byte[] byteArray = record.toByteArray(); 00300 if (byteArray.length < this.availableSpace()) { 00301 _authoritativeAnswers.add(rec); 00302 _authoritativeAnswersBytes.write(byteArray, 0, byteArray.length); 00303 } else { 00304 throw new IOException("message full"); 00305 } 00306 } 00307 00315 public void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException { 00316 MessageOutputStream record = new MessageOutputStream(512, this); 00317 record.writeRecord(rec, 0); 00318 byte[] byteArray = record.toByteArray(); 00319 if (byteArray.length < this.availableSpace()) { 00320 _additionals.add(rec); 00321 _additionalsAnswersBytes.write(byteArray, 0, byteArray.length); 00322 } else { 00323 throw new IOException("message full"); 00324 } 00325 } 00326 00332 public byte[] data() { 00333 long now = System.currentTimeMillis(); // System.currentTimeMillis() 00334 _names.clear(); 00335 00336 MessageOutputStream message = new MessageOutputStream(_maxUDPPayload, this); 00337 message.writeShort(_multicast ? 0 : this.getId()); 00338 message.writeShort(this.getFlags()); 00339 message.writeShort(this.getNumberOfQuestions()); 00340 message.writeShort(this.getNumberOfAnswers()); 00341 message.writeShort(this.getNumberOfAuthorities()); 00342 message.writeShort(this.getNumberOfAdditionals()); 00343 for (DNSQuestion question : _questions) { 00344 message.writeQuestion(question); 00345 } 00346 for (DNSRecord record : _answers) { 00347 message.writeRecord(record, now); 00348 } 00349 for (DNSRecord record : _authoritativeAnswers) { 00350 message.writeRecord(record, now); 00351 } 00352 for (DNSRecord record : _additionals) { 00353 message.writeRecord(record, now); 00354 } 00355 return message.toByteArray(); 00356 } 00357 00358 @Override 00359 public boolean isQuery() { 00360 return (this.getFlags() & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY; 00361 } 00362 00366 String print(boolean dump) { 00367 StringBuilder buf = new StringBuilder(); 00368 buf.append(this.print()); 00369 if (dump) { 00370 buf.append(this.print(this.data())); 00371 } 00372 return buf.toString(); 00373 } 00374 00375 @Override 00376 public String toString() { 00377 StringBuffer buf = new StringBuffer(); 00378 buf.append(isQuery() ? "dns[query:" : "dns[response:"); 00379 buf.append(" id=0x"); 00380 buf.append(Integer.toHexString(this.getId())); 00381 if (this.getFlags() != 0) { 00382 buf.append(", flags=0x"); 00383 buf.append(Integer.toHexString(this.getFlags())); 00384 if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) { 00385 buf.append(":r"); 00386 } 00387 if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) { 00388 buf.append(":aa"); 00389 } 00390 if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) { 00391 buf.append(":tc"); 00392 } 00393 } 00394 if (this.getNumberOfQuestions() > 0) { 00395 buf.append(", questions="); 00396 buf.append(this.getNumberOfQuestions()); 00397 } 00398 if (this.getNumberOfAnswers() > 0) { 00399 buf.append(", answers="); 00400 buf.append(this.getNumberOfAnswers()); 00401 } 00402 if (this.getNumberOfAuthorities() > 0) { 00403 buf.append(", authorities="); 00404 buf.append(this.getNumberOfAuthorities()); 00405 } 00406 if (this.getNumberOfAdditionals() > 0) { 00407 buf.append(", additionals="); 00408 buf.append(this.getNumberOfAdditionals()); 00409 } 00410 if (this.getNumberOfQuestions() > 0) { 00411 buf.append("\nquestions:"); 00412 for (DNSQuestion question : _questions) { 00413 buf.append("\n\t"); 00414 buf.append(question); 00415 } 00416 } 00417 if (this.getNumberOfAnswers() > 0) { 00418 buf.append("\nanswers:"); 00419 for (DNSRecord record : _answers) { 00420 buf.append("\n\t"); 00421 buf.append(record); 00422 } 00423 } 00424 if (this.getNumberOfAuthorities() > 0) { 00425 buf.append("\nauthorities:"); 00426 for (DNSRecord record : _authoritativeAnswers) { 00427 buf.append("\n\t"); 00428 buf.append(record); 00429 } 00430 } 00431 if (this.getNumberOfAdditionals() > 0) { 00432 buf.append("\nadditionals:"); 00433 for (DNSRecord record : _additionals) { 00434 buf.append("\n\t"); 00435 buf.append(record); 00436 } 00437 } 00438 buf.append("\nnames="); 00439 buf.append(_names); 00440 buf.append("]"); 00441 return buf.toString(); 00442 } 00443 00447 public int getMaxUDPPayload() { 00448 return this._maxUDPPayload; 00449 } 00450 00451 }