00001
00002
00003
00004
00005 package javax.jmdns.impl;
00006
00007 import java.io.ByteArrayInputStream;
00008 import java.io.IOException;
00009 import java.net.DatagramPacket;
00010 import java.net.Inet4Address;
00011 import java.net.InetAddress;
00012 import java.util.HashMap;
00013 import java.util.Map;
00014 import java.util.logging.Level;
00015 import java.util.logging.Logger;
00016
00017 import javax.jmdns.impl.constants.DNSConstants;
00018 import javax.jmdns.impl.constants.DNSLabel;
00019 import javax.jmdns.impl.constants.DNSOptionCode;
00020 import javax.jmdns.impl.constants.DNSRecordClass;
00021 import javax.jmdns.impl.constants.DNSRecordType;
00022 import javax.jmdns.impl.constants.DNSResultCode;
00023
00029 public final class DNSIncoming extends DNSMessage {
00030 private static Logger logger = Logger.getLogger(DNSIncoming.class.getName());
00031
00032
00033
00034 public static boolean USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET = true;
00035
00036 public static class MessageInputStream extends ByteArrayInputStream {
00037 private static Logger logger1 = Logger.getLogger(MessageInputStream.class.getName());
00038
00039 final Map<Integer, String> _names;
00040
00041 public MessageInputStream(byte[] buffer, int length) {
00042 this(buffer, 0, length);
00043 }
00044
00050 public MessageInputStream(byte[] buffer, int offset, int length) {
00051 super(buffer, offset, length);
00052 _names = new HashMap<Integer, String>();
00053 }
00054
00055 public int readByte() {
00056 return this.read();
00057 }
00058
00059 public int readUnsignedShort() {
00060 return (this.read() << 8) | this.read();
00061 }
00062
00063 public int readInt() {
00064 return (this.readUnsignedShort() << 16) | this.readUnsignedShort();
00065 }
00066
00067 public byte[] readBytes(int len) {
00068 byte bytes[] = new byte[len];
00069 this.read(bytes, 0, len);
00070 return bytes;
00071 }
00072
00073 public String readUTF(int len) {
00074 StringBuilder buffer = new StringBuilder(len);
00075 for (int index = 0; index < len; index++) {
00076 int ch = this.read();
00077 switch (ch >> 4) {
00078 case 0:
00079 case 1:
00080 case 2:
00081 case 3:
00082 case 4:
00083 case 5:
00084 case 6:
00085 case 7:
00086
00087 break;
00088 case 12:
00089 case 13:
00090
00091 ch = ((ch & 0x1F) << 6) | (this.read() & 0x3F);
00092 index++;
00093 break;
00094 case 14:
00095
00096 ch = ((ch & 0x0f) << 12) | ((this.read() & 0x3F) << 6) | (this.read() & 0x3F);
00097 index++;
00098 index++;
00099 break;
00100 default:
00101
00102 ch = ((ch & 0x3F) << 4) | (this.read() & 0x0f);
00103 index++;
00104 break;
00105 }
00106 buffer.append((char) ch);
00107 }
00108 return buffer.toString();
00109 }
00110
00111 protected synchronized int peek() {
00112 return (pos < count) ? (buf[pos] & 0xff) : -1;
00113 }
00114
00115 public String readName() {
00116 Map<Integer, StringBuilder> names = new HashMap<Integer, StringBuilder>();
00117 StringBuilder buffer = new StringBuilder();
00118 boolean finished = false;
00119 while (!finished) {
00120 int len = this.read();
00121 if (len == 0) {
00122 finished = true;
00123 break;
00124 }
00125 switch (DNSLabel.labelForByte(len)) {
00126 case Standard:
00127 int offset = pos - 1;
00128 String label = this.readUTF(len) + ".";
00129 buffer.append(label);
00130 for (StringBuilder previousLabel : names.values()) {
00131 previousLabel.append(label);
00132 }
00133 names.put(Integer.valueOf(offset), new StringBuilder(label));
00134 break;
00135 case Compressed:
00136 int index = (DNSLabel.labelValue(len) << 8) | this.read();
00137 String compressedLabel = _names.get(Integer.valueOf(index));
00138 if (compressedLabel == null) {
00139 logger1.severe("bad domain name: possible circular name detected. Bad offset: 0x" + Integer.toHexString(index) + " at 0x" + Integer.toHexString(pos - 2));
00140 compressedLabel = "";
00141 }
00142 buffer.append(compressedLabel);
00143 for (StringBuilder previousLabel : names.values()) {
00144 previousLabel.append(compressedLabel);
00145 }
00146 finished = true;
00147 break;
00148 case Extended:
00149
00150 logger1.severe("Extended label are not currently supported.");
00151 break;
00152 case Unknown:
00153 default:
00154 logger1.severe("unsupported dns label type: '" + Integer.toHexString(len & 0xC0) + "'");
00155 }
00156 }
00157 for (Integer index : names.keySet()) {
00158 _names.put(index, names.get(index).toString());
00159 }
00160 return buffer.toString();
00161 }
00162
00163 public String readNonNameString() {
00164 int len = this.read();
00165 return this.readUTF(len);
00166 }
00167
00168 }
00169
00170 private final DatagramPacket _packet;
00171
00172 private final long _receivedTime;
00173
00174 private final MessageInputStream _messageInputStream;
00175
00176 private int _senderUDPPayload;
00177
00184 public DNSIncoming(DatagramPacket packet) throws IOException {
00185 super(0, 0, packet.getPort() == DNSConstants.MDNS_PORT);
00186 this._packet = packet;
00187 InetAddress source = packet.getAddress();
00188 this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
00189 this._receivedTime = System.currentTimeMillis();
00190 this._senderUDPPayload = DNSConstants.MAX_MSG_TYPICAL;
00191
00192 try {
00193 this.setId(_messageInputStream.readUnsignedShort());
00194 this.setFlags(_messageInputStream.readUnsignedShort());
00195 int numQuestions = _messageInputStream.readUnsignedShort();
00196 int numAnswers = _messageInputStream.readUnsignedShort();
00197 int numAuthorities = _messageInputStream.readUnsignedShort();
00198 int numAdditionals = _messageInputStream.readUnsignedShort();
00199
00200
00201 if (numQuestions > 0) {
00202 for (int i = 0; i < numQuestions; i++) {
00203 _questions.add(this.readQuestion());
00204 }
00205 }
00206
00207
00208 if (numAnswers > 0) {
00209 for (int i = 0; i < numAnswers; i++) {
00210 DNSRecord rec = this.readAnswer(source);
00211 if (rec != null) {
00212
00213 _answers.add(rec);
00214 }
00215 }
00216 }
00217
00218 if (numAuthorities > 0) {
00219 for (int i = 0; i < numAuthorities; i++) {
00220 DNSRecord rec = this.readAnswer(source);
00221 if (rec != null) {
00222
00223 _authoritativeAnswers.add(rec);
00224 }
00225 }
00226 }
00227
00228 if (numAdditionals > 0) {
00229 for (int i = 0; i < numAdditionals; i++) {
00230 DNSRecord rec = this.readAnswer(source);
00231 if (rec != null) {
00232
00233 _additionals.add(rec);
00234 }
00235 }
00236 }
00237 } catch (Exception e) {
00238 logger.log(Level.WARNING, "DNSIncoming() dump " + print(true) + "\n exception ", e);
00239
00240 IOException ioe = new IOException("DNSIncoming corrupted message");
00241 ioe.initCause(e);
00242 throw ioe;
00243 }
00244 }
00245
00246 private DNSIncoming(int flags, int id, boolean multicast, DatagramPacket packet, long receivedTime) {
00247 super(flags, id, multicast);
00248 this._packet = packet;
00249 this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
00250 this._receivedTime = receivedTime;
00251 }
00252
00253
00254
00255
00256
00257
00258
00259 @Override
00260 public DNSIncoming clone() {
00261 DNSIncoming in = new DNSIncoming(this.getFlags(), this.getId(), this.isMulticast(), this._packet, this._receivedTime);
00262 in._senderUDPPayload = this._senderUDPPayload;
00263 in._questions.addAll(this._questions);
00264 in._answers.addAll(this._answers);
00265 in._authoritativeAnswers.addAll(this._authoritativeAnswers);
00266 in._additionals.addAll(this._additionals);
00267 return in;
00268 }
00269
00270
00271 private DNSQuestion readQuestion() {
00272 String domain = _messageInputStream.readName();
00273 DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
00274 if (type == DNSRecordType.TYPE_IGNORE) {
00275 logger.log(Level.SEVERE, "Could not find record type: " + this.print(true));
00276 }
00277 int recordClassIndex = _messageInputStream.readUnsignedShort();
00278 DNSRecordClass recordClass = DNSRecordClass.classForIndex(recordClassIndex);
00279 boolean unique = recordClass.isUnique(recordClassIndex);
00280 return DNSQuestion.newQuestion(domain, type, recordClass, unique);
00281 }
00282
00283 private DNSRecord readAnswer(InetAddress source) {
00284 String domain = _messageInputStream.readName();
00285 int type_value = _messageInputStream.readUnsignedShort();
00286 DNSRecordType type = DNSRecordType.typeForIndex(type_value);
00287 if (type == DNSRecordType.TYPE_IGNORE) {
00288 logger.log(Level.SEVERE, "Could not find record type. domain: " + domain + "\n" + this.print(true));
00289 }
00290 int recordClassIndex = _messageInputStream.readUnsignedShort();
00291 DNSRecordClass recordClass = (type == DNSRecordType.TYPE_OPT ? DNSRecordClass.CLASS_UNKNOWN : DNSRecordClass.classForIndex(recordClassIndex));
00292 if ((recordClass == DNSRecordClass.CLASS_UNKNOWN) && (type != DNSRecordType.TYPE_OPT)) {
00293 logger.log(Level.SEVERE, "Could not find record class. domain: " + domain + " type: " + type + "\n" + this.print(true));
00294 }
00295 boolean unique = recordClass.isUnique(recordClassIndex);
00296 int ttl = _messageInputStream.readInt();
00297 int len = _messageInputStream.readUnsignedShort();
00298 DNSRecord rec = null;
00299
00300 switch (type) {
00301 case TYPE_A:
00302 rec = new DNSRecord.IPv4Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
00303
00304 break;
00305 case TYPE_AAAA:
00306 rec = new DNSRecord.IPv6Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
00307
00308 break;
00309 case TYPE_CNAME:
00310 case TYPE_PTR:
00311 String service = "";
00312 service = _messageInputStream.readName();
00313 if (service.length() > 0) {
00314 rec = new DNSRecord.Pointer(domain, recordClass, unique, ttl, service);
00315 } else {
00316 logger.log(Level.WARNING, "PTR record of class: " + recordClass + ", there was a problem reading the service name of the answer for domain:" + domain);
00317 }
00318 break;
00319 case TYPE_TXT:
00320 rec = new DNSRecord.Text(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
00321 break;
00322 case TYPE_SRV:
00323 int priority = _messageInputStream.readUnsignedShort();
00324 int weight = _messageInputStream.readUnsignedShort();
00325 int port = _messageInputStream.readUnsignedShort();
00326 String target = "";
00327
00328
00329 if (USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
00330 target = _messageInputStream.readName();
00331 } else {
00332
00333 target = _messageInputStream.readNonNameString();
00334 }
00335 rec = new DNSRecord.Service(domain, recordClass, unique, ttl, priority, weight, port, target);
00336
00337 break;
00338 case TYPE_HINFO:
00339 StringBuilder buf = new StringBuilder();
00340 buf.append(_messageInputStream.readUTF(len));
00341 int index = buf.indexOf(" ");
00342 String cpu = (index > 0 ? buf.substring(0, index) : buf.toString()).trim();
00343 String os = (index > 0 ? buf.substring(index + 1) : "").trim();
00344 rec = new DNSRecord.HostInformation(domain, recordClass, unique, ttl, cpu, os);
00345 break;
00346 case TYPE_OPT:
00347 DNSResultCode extendedResultCode = DNSResultCode.resultCodeForFlags(this.getFlags(), ttl);
00348 int version = (ttl & 0x00ff0000) >> 16;
00349 if (version == 0) {
00350 _senderUDPPayload = recordClassIndex;
00351 while (_messageInputStream.available() > 0) {
00352
00353 int optionCodeInt = 0;
00354 DNSOptionCode optionCode = null;
00355 if (_messageInputStream.available() >= 2) {
00356 optionCodeInt = _messageInputStream.readUnsignedShort();
00357 optionCode = DNSOptionCode.resultCodeForFlags(optionCodeInt);
00358 } else {
00359 logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
00360 break;
00361 }
00362 int optionLength = 0;
00363 if (_messageInputStream.available() >= 2) {
00364 optionLength = _messageInputStream.readUnsignedShort();
00365 } else {
00366 logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
00367 break;
00368 }
00369 byte[] optiondata = new byte[0];
00370 if (_messageInputStream.available() >= optionLength) {
00371 optiondata = _messageInputStream.readBytes(optionLength);
00372 }
00373
00374
00375 switch (optionCode) {
00376 case Owner:
00377
00378
00379
00380
00381
00382 int ownerVersion = 0;
00383 int ownerSequence = 0;
00384 byte[] ownerPrimaryMacAddress = null;
00385 byte[] ownerWakeupMacAddress = null;
00386 byte[] ownerPassword = null;
00387 try {
00388 ownerVersion = optiondata[0];
00389 ownerSequence = optiondata[1];
00390 ownerPrimaryMacAddress = new byte[] { optiondata[2], optiondata[3], optiondata[4], optiondata[5], optiondata[6], optiondata[7] };
00391 ownerWakeupMacAddress = ownerPrimaryMacAddress;
00392 if (optiondata.length > 8) {
00393
00394 ownerWakeupMacAddress = new byte[] { optiondata[8], optiondata[9], optiondata[10], optiondata[11], optiondata[12], optiondata[13] };
00395 }
00396 if (optiondata.length == 18) {
00397
00398 ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17] };
00399 }
00400 if (optiondata.length == 22) {
00401
00402 ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17], optiondata[18], optiondata[19], optiondata[20], optiondata[21] };
00403 }
00404 } catch (Exception exception) {
00405 logger.warning("Malformed OPT answer. Option code: Owner data: " + this._hexString(optiondata));
00406 }
00407 if (logger.isLoggable(Level.FINE)) {
00408 logger.fine("Unhandled Owner OPT version: " + ownerVersion + " sequence: " + ownerSequence + " MAC address: " + this._hexString(ownerPrimaryMacAddress)
00409 + (ownerWakeupMacAddress != ownerPrimaryMacAddress ? " wakeup MAC address: " + this._hexString(ownerWakeupMacAddress) : "") + (ownerPassword != null ? " password: " + this._hexString(ownerPassword) : ""));
00410 }
00411 break;
00412 case LLQ:
00413 case NSID:
00414 case UL:
00415 if (logger.isLoggable(Level.FINE)) {
00416 logger.log(Level.FINE, "There was an OPT answer. Option code: " + optionCode + " data: " + this._hexString(optiondata));
00417 }
00418 break;
00419 case Unknown:
00420 logger.log(Level.WARNING, "There was an OPT answer. Not currently handled. Option code: " + optionCodeInt + " data: " + this._hexString(optiondata));
00421 break;
00422 default:
00423
00424 break;
00425 }
00426 }
00427 } else {
00428 logger.log(Level.WARNING, "There was an OPT answer. Wrong version number: " + version + " result code: " + extendedResultCode);
00429 }
00430 break;
00431 default:
00432 if (logger.isLoggable(Level.FINER)) {
00433 logger.finer("DNSIncoming() unknown type:" + type);
00434 }
00435 _messageInputStream.skip(len);
00436 break;
00437 }
00438 if (rec != null) {
00439 rec.setRecordSource(source);
00440 }
00441 return rec;
00442 }
00443
00447 String print(boolean dump) {
00448 StringBuilder buf = new StringBuilder();
00449 buf.append(this.print());
00450 if (dump) {
00451 byte[] data = new byte[_packet.getLength()];
00452 System.arraycopy(_packet.getData(), 0, data, 0, data.length);
00453 buf.append(this.print(data));
00454 }
00455 return buf.toString();
00456 }
00457
00458 @Override
00459 public String toString() {
00460 StringBuilder buf = new StringBuilder();
00461 buf.append(isQuery() ? "dns[query," : "dns[response,");
00462 if (_packet.getAddress() != null) {
00463 buf.append(_packet.getAddress().getHostAddress());
00464 }
00465 buf.append(':');
00466 buf.append(_packet.getPort());
00467 buf.append(", length=");
00468 buf.append(_packet.getLength());
00469 buf.append(", id=0x");
00470 buf.append(Integer.toHexString(this.getId()));
00471 if (this.getFlags() != 0) {
00472 buf.append(", flags=0x");
00473 buf.append(Integer.toHexString(this.getFlags()));
00474 if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
00475 buf.append(":r");
00476 }
00477 if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
00478 buf.append(":aa");
00479 }
00480 if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
00481 buf.append(":tc");
00482 }
00483 }
00484 if (this.getNumberOfQuestions() > 0) {
00485 buf.append(", questions=");
00486 buf.append(this.getNumberOfQuestions());
00487 }
00488 if (this.getNumberOfAnswers() > 0) {
00489 buf.append(", answers=");
00490 buf.append(this.getNumberOfAnswers());
00491 }
00492 if (this.getNumberOfAuthorities() > 0) {
00493 buf.append(", authorities=");
00494 buf.append(this.getNumberOfAuthorities());
00495 }
00496 if (this.getNumberOfAdditionals() > 0) {
00497 buf.append(", additionals=");
00498 buf.append(this.getNumberOfAdditionals());
00499 }
00500 if (this.getNumberOfQuestions() > 0) {
00501 buf.append("\nquestions:");
00502 for (DNSQuestion question : _questions) {
00503 buf.append("\n\t");
00504 buf.append(question);
00505 }
00506 }
00507 if (this.getNumberOfAnswers() > 0) {
00508 buf.append("\nanswers:");
00509 for (DNSRecord record : _answers) {
00510 buf.append("\n\t");
00511 buf.append(record);
00512 }
00513 }
00514 if (this.getNumberOfAuthorities() > 0) {
00515 buf.append("\nauthorities:");
00516 for (DNSRecord record : _authoritativeAnswers) {
00517 buf.append("\n\t");
00518 buf.append(record);
00519 }
00520 }
00521 if (this.getNumberOfAdditionals() > 0) {
00522 buf.append("\nadditionals:");
00523 for (DNSRecord record : _additionals) {
00524 buf.append("\n\t");
00525 buf.append(record);
00526 }
00527 }
00528 buf.append("]");
00529 return buf.toString();
00530 }
00531
00538 void append(DNSIncoming that) {
00539 if (this.isQuery() && this.isTruncated() && that.isQuery()) {
00540 this._questions.addAll(that.getQuestions());
00541 this._answers.addAll(that.getAnswers());
00542 this._authoritativeAnswers.addAll(that.getAuthorities());
00543 this._additionals.addAll(that.getAdditionals());
00544 } else {
00545 throw new IllegalArgumentException();
00546 }
00547 }
00548
00549 public int elapseSinceArrival() {
00550 return (int) (System.currentTimeMillis() - _receivedTime);
00551 }
00552
00558 public int getSenderUDPPayload() {
00559 return this._senderUDPPayload;
00560 }
00561
00562 private static final char[] _nibbleToHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
00563
00570 private String _hexString(byte[] bytes) {
00571
00572 StringBuilder result = new StringBuilder(2 * bytes.length);
00573
00574 for (int i = 0; i < bytes.length; i++) {
00575 int b = bytes[i] & 0xFF;
00576 result.append(_nibbleToHex[b / 16]);
00577 result.append(_nibbleToHex[b % 16]);
00578 }
00579
00580 return result.toString();
00581 }
00582
00583 }