00001
00002
00003
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
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
00093 writeByte(utflen);
00094
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
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();
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 }