| OLD | NEW | 
|---|
| 1 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file | 
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a | 
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. | 
| 4 | 4 | 
| 5 library mdns.src.packet; | 5 library mdns.src.packet; | 
| 6 | 6 | 
| 7 import 'dart:convert'; | 7 import 'dart:convert'; | 
| 8 import 'dart:io'; | 8 import 'dart:io'; | 
| 9 import 'dart:typed_data'; | 9 import 'dart:typed_data'; | 
| 10 | 10 | 
| 11 import 'package:mdns/src/constants.dart'; | 11 import 'package:mdns/src/constants.dart'; | 
| 12 | 12 | 
| 13 // Encode a mDNS query packet. | 13 // Encode a mDNS query packet. | 
| 14 List<int> encodeMDnsQuery(String hostname) { | 14 List<int> encodeMDnsQuery(String name, [int type = RRType.A]) { | 
| 15   List parts = hostname.split('.'); | 15   List parts = name.split('.'); | 
| 16 | 16 | 
| 17   // Calculate the size of the packet. | 17   // Calculate the size of the packet. | 
| 18   int size = headerSize; | 18   int size = headerSize; | 
| 19   for (int i = 0; i < parts.length; i++) { | 19   for (int i = 0; i < parts.length; i++) { | 
| 20     parts[i] = UTF8.encode(parts[i]); | 20     parts[i] = UTF8.encode(parts[i]); | 
| 21     size += 1 + parts[i].length; | 21     size += 1 + parts[i].length; | 
| 22   } | 22   } | 
| 23   size += 1; // End with empty part | 23   size += 1; // End with empty part | 
| 24   size += 4; // Trailer (QTYPE and QCLASS). | 24   size += 4; // Trailer (QTYPE and QCLASS). | 
| 25   Uint8List data = new Uint8List(size); | 25   Uint8List data = new Uint8List(size); | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 37   // Number of resource records - 0 for query. | 37   // Number of resource records - 0 for query. | 
| 38   bd.setUint16(arcountOffset, 0); | 38   bd.setUint16(arcountOffset, 0); | 
| 39   int offset = headerSize; | 39   int offset = headerSize; | 
| 40   for (int i = 0; i < parts.length; i++) { | 40   for (int i = 0; i < parts.length; i++) { | 
| 41     data[offset++] = parts[i].length; | 41     data[offset++] = parts[i].length; | 
| 42     data.setRange(offset, offset + parts[i].length, parts[i]); | 42     data.setRange(offset, offset + parts[i].length, parts[i]); | 
| 43     offset += parts[i].length; | 43     offset += parts[i].length; | 
| 44   } | 44   } | 
| 45   data[offset] = 0; // Empty part. | 45   data[offset] = 0; // Empty part. | 
| 46   offset++; | 46   offset++; | 
| 47   bd.setUint16(offset, 1); // QTYPE. | 47   bd.setUint16(offset, type); // QTYPE. | 
| 48   offset += 2; | 48   offset += 2; | 
| 49   bd.setUint16(offset, 1); // QCLASS. | 49   bd.setUint16(offset, RRClass.IN | 0x8000); // QCLASS + QU. | 
| 50 | 50 | 
| 51   return data; | 51   return data; | 
| 52 } | 52 } | 
| 53 | 53 | 
| 54 /// FQDN and address decoded from response. | 54 /// Partial implementation of DNS resource records (RRs). | 
| 55 class DecodeResult { | 55 class ResourceRecord { | 
|  | 56   final int type; | 
| 56   final String name; | 57   final String name; | 
| 57   final InternetAddress address; | 58   final _data; | 
| 58   DecodeResult(this.name, this.address); | 59   final int validUntil; | 
|  | 60   // TODO(karlklose): add missing header bits. | 
|  | 61 | 
|  | 62   ResourceRecord(this.type, this.name, this._data, this.validUntil); | 
|  | 63 | 
|  | 64   InternetAddress get address { | 
|  | 65     if (type != RRType.A) { | 
|  | 66       // TODO(karlklose): add IPv6 address support. | 
|  | 67       throw new StateError("'address' is only supported for type A."); | 
|  | 68     } | 
|  | 69     return _data; | 
|  | 70   } | 
|  | 71 | 
|  | 72   String get domainName { | 
|  | 73     if (type != RRType.PTR) { | 
|  | 74       throw new StateError("'domain name' is only supported for type PTR."); | 
|  | 75     } | 
|  | 76     return _data; | 
|  | 77   } | 
|  | 78 | 
|  | 79   String get target { | 
|  | 80     if (type != RRType.SRV) { | 
|  | 81       throw new StateError("'target' is only supported for type SRV."); | 
|  | 82     } | 
|  | 83     return _data; | 
|  | 84   } | 
|  | 85 | 
|  | 86   toString() => 'RR $type $_data'; | 
| 59 } | 87 } | 
| 60 | 88 | 
| 61 /// Result of reading a FQDN. The FQDN parts and the bytes consumed. | 89 /// Result of reading a FQDN. | 
| 62 class FQDNReadResult { | 90 class FQDNReadResult { | 
| 63   final List<String> fqdn; | 91   final List<String> fqdn; | 
| 64   final int bytesRead; | 92   final int bytesRead; | 
| 65   FQDNReadResult(this.fqdn, this.bytesRead); | 93   FQDNReadResult(this.fqdn, this.bytesRead); | 
| 66 } | 94 } | 
| 67 | 95 | 
| 68 /// Decode a mDNS package. | 96 /// Decode a mDNS package. | 
| 69 /// | 97 /// | 
| 70 /// If decoding fails (e.g. due to an invalid packet) `null` is returned. | 98 /// If decoding fails (e.g. due to an invalid packet) `null` is returned. | 
| 71 /// | 99 /// | 
| 72 /// See https://tools.ietf.org/html/rfc1035 for the format. | 100 /// See https://tools.ietf.org/html/rfc1035 for the format. | 
| 73 List<DecodeResult> decodeMDnsResponse(List<int> packet) { | 101 List<ResourceRecord> decodeMDnsResponse(List<int> packet) { | 
| 74   int length = packet.length; | 102   int length = packet.length; | 
| 75   if (length < headerSize) return null; | 103   if (length < headerSize) return null; | 
| 76 | 104 | 
| 77   Uint8List data = | 105   Uint8List data = | 
| 78       packet is Uint8List ? packet : new Uint8List.fromList(packet); | 106       packet is Uint8List ? packet : new Uint8List.fromList(packet); | 
| 79   ByteData bd = new ByteData.view(data.buffer); | 107   ByteData bd = new ByteData.view(data.buffer); | 
| 80   // Query identifier. | 108   // Query identifier. | 
| 81   int id = bd.getUint16(idOffset); | 109   int id = bd.getUint16(idOffset); | 
| 82   // Flags. | 110   // Flags. | 
| 83   int flags = bd.getUint16(flagsOffset); | 111   int flags = bd.getUint16(flagsOffset); | 
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 126           offset += partLength; | 154           offset += partLength; | 
| 127           parts.add(UTF8.decode(partBytes)); | 155           parts.add(UTF8.decode(partBytes)); | 
| 128         } else { | 156         } else { | 
| 129           break; | 157           break; | 
| 130         } | 158         } | 
| 131       } | 159       } | 
| 132     } | 160     } | 
| 133     return new FQDNReadResult(parts, offset - prevOffset); | 161     return new FQDNReadResult(parts, offset - prevOffset); | 
| 134   } | 162   } | 
| 135 | 163 | 
| 136   DecodeResult readAddress() { | 164   ResourceRecord readResourceRecord() { | 
| 137     // First read the FQDN. | 165     // First read the FQDN. | 
| 138     FQDNReadResult result = readFQDN(offset); | 166     FQDNReadResult result = readFQDN(offset); | 
| 139     var fqdn = result.fqdn.join('.'); | 167     var fqdn = result.fqdn.join('.'); | 
| 140     offset += result.bytesRead; | 168     offset += result.bytesRead; | 
| 141     checkLength(offset + 2); | 169     checkLength(offset + 2); | 
| 142     int type = bd.getUint16(offset); | 170     int type = bd.getUint16(offset); | 
| 143     offset += 2; | 171     offset += 2; | 
|  | 172     // The first bit of the rrclass field is set to indicate that the answer is | 
|  | 173     // unique and the querier should flush the cached answer for this name | 
|  | 174     // (RFC 6762, Sec. 10.2). We ignore it for now since we don't cache answers. | 
| 144     checkLength(offset + 2); | 175     checkLength(offset + 2); | 
| 145     int cls = bd.getUint16(offset); | 176     int cls = bd.getUint16(offset) & 0x7fff; | 
| 146     offset += 2; | 177     offset += 2; | 
| 147     checkLength(offset + 4); | 178     checkLength(offset + 4); | 
| 148     int ttl = bd.getInt32(offset); | 179     int ttl = bd.getInt32(offset); | 
| 149     offset += 4; | 180     offset += 4; | 
|  | 181 | 
|  | 182     var rData; | 
| 150     checkLength(offset + 2); | 183     checkLength(offset + 2); | 
| 151     int addressLength = bd.getUint16(offset); | 184     int rDataLength = bd.getUint16(offset); | 
| 152     offset += 2; | 185     offset += 2; | 
| 153     checkLength(offset + addressLength); | 186     switch (type) { | 
| 154     var addressBytes = new Uint8List.view(data.buffer, offset, addressLength); | 187       case RRType.A: | 
| 155     offset += addressLength; | 188         checkLength(offset + rDataLength); | 
|  | 189         rData = new Uint8List.view(data.buffer, offset, rDataLength); | 
|  | 190         String addr = rData.map((n) => n.toString()).join('.'); | 
|  | 191         rData = new InternetAddress(addr); | 
|  | 192         offset += rDataLength; | 
|  | 193         break; | 
|  | 194       case RRType.SRV: | 
|  | 195         checkLength(offset + 2); | 
|  | 196         int priority = bd.getUint16(offset); | 
|  | 197         offset += 2; | 
|  | 198         checkLength(offset + 2); | 
|  | 199         int weight = bd.getUint16(offset); | 
|  | 200         offset += 2; | 
|  | 201         checkLength(offset + 2); | 
|  | 202         int port = bd.getUint16(offset); | 
|  | 203         offset += 2; | 
|  | 204         FQDNReadResult result = readFQDN(offset); | 
|  | 205         rData = result.fqdn.join('.'); | 
|  | 206         offset += rDataLength - 6; | 
|  | 207         break; | 
|  | 208       case RRType.PTR: | 
|  | 209         checkLength(offset + rDataLength); | 
|  | 210         FQDNReadResult result = readFQDN(offset); | 
|  | 211         offset += rDataLength; | 
|  | 212         rData = result.fqdn.join('.'); | 
|  | 213         break; | 
|  | 214       case RRType.TXT: | 
|  | 215         // TODO(karlklose): convert to a String or Map. | 
|  | 216       default: | 
|  | 217         checkLength(offset + rDataLength); | 
|  | 218         rData = new Uint8List.view(data.buffer, offset, rDataLength); | 
|  | 219         offset += rDataLength; | 
|  | 220         break; | 
|  | 221     } | 
|  | 222     assert(rData != null); | 
| 156 | 223 | 
| 157     if (type == ipV4AddressType && cls == ipV4Class && addressLength == 4) { | 224     if (cls != RRClass.IN) { | 
| 158       String addr = addressBytes.map((n) => n.toString()).join('.'); | 225       // We do not support other classes at the moment. | 
| 159       return new DecodeResult(fqdn, new InternetAddress(addr)); |  | 
| 160     } else { |  | 
| 161       return null; | 226       return null; | 
| 162     } | 227     } | 
|  | 228 | 
|  | 229     int validUntil = new DateTime.now().millisecondsSinceEpoch + | 
|  | 230       ttl * 1000; | 
|  | 231     return new ResourceRecord(type, fqdn, rData, validUntil); | 
| 163   } | 232   } | 
| 164 | 233 | 
| 165   // We don't use the number of records - just read through all | 234   List<ResourceRecord> result = <ResourceRecord>[]; | 
| 166   // resource records and filter. |  | 
| 167   var result = []; |  | 
| 168   try { | 235   try { | 
| 169     while (data.length - offset >= 16) { | 236     for (int i = 0; i < ancount; i++) { | 
| 170       var address = readAddress(); | 237       ResourceRecord record = readResourceRecord(); | 
| 171       if (address != null) result.add(address); | 238       if (record != null) { | 
|  | 239         result.add(record); | 
|  | 240       } | 
| 172     } | 241     } | 
| 173   } on MDnsDecodeException catch (e, s) { | 242   } on MDnsDecodeException catch (e, s) { | 
| 174     // If decoding fails return null. | 243     // If decoding fails return null. | 
| 175     return null; | 244     return null; | 
| 176   } | 245   } | 
| 177 |  | 
| 178   return result; | 246   return result; | 
| 179 } | 247 } | 
| 180 | 248 | 
| 181 /// Exceptions thrown by decoder when the packet is invalid. | 249 /// Exceptions thrown by decoder when the packet is invalid. | 
| 182 class MDnsDecodeException implements Exception { | 250 class MDnsDecodeException implements Exception { | 
| 183   /// Exception message. | 251   /// Exception message. | 
| 184   final int offset; | 252   final int offset; | 
| 185   const MDnsDecodeException(this.offset); | 253   const MDnsDecodeException(this.offset); | 
| 186   String toString() => 'Decoding error at $offset'; | 254   String toString() => 'Decoding error at $offset'; | 
| 187 } | 255 } | 
| OLD | NEW | 
|---|