| 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 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 throw new StateError("'target' is only supported for type SRV."); | 81 throw new StateError("'target' is only supported for type SRV."); |
| 82 } | 82 } |
| 83 return _data; | 83 return _data; |
| 84 } | 84 } |
| 85 | 85 |
| 86 toString() => 'RR $type $_data'; | 86 toString() => 'RR $type $_data'; |
| 87 } | 87 } |
| 88 | 88 |
| 89 /// Result of reading a FQDN. | 89 /// Result of reading a FQDN. |
| 90 class FQDNReadResult { | 90 class FQDNReadResult { |
| 91 final List<String> fqdn; | 91 final List<String> fqdnParts; |
| 92 final int bytesRead; | 92 final int bytesRead; |
| 93 FQDNReadResult(this.fqdn, this.bytesRead); | 93 String get fqdn => fqdnParts.join('.'); |
| 94 FQDNReadResult(this.fqdnParts, this.bytesRead); |
| 95 } |
| 96 |
| 97 String readFQDN(List<int> packet, [int offset = 0]) { |
| 98 Uint8List data = |
| 99 packet is Uint8List ? packet : new Uint8List.fromList(packet); |
| 100 ByteData bd = new ByteData.view(data.buffer); |
| 101 |
| 102 return _readFQDN(data, bd, offset, data.length).fqdn; |
| 103 } |
| 104 |
| 105 // Read a FQDN at the given offset. Returns a pair with the FQDN |
| 106 // parts and the number of bytes consumed. |
| 107 // |
| 108 // If decoding fails (e.g. due to an invalid packet) `null` is returned. |
| 109 FQDNReadResult _readFQDN(Uint8List data, ByteData bd, int offset, int length) { |
| 110 void checkLength(int required) { |
| 111 if (length < required) throw new MDnsDecodeException(required); |
| 112 } |
| 113 |
| 114 List<String> parts = []; |
| 115 int prevOffset = offset; |
| 116 while (true) { |
| 117 // At least one byte is required. |
| 118 checkLength(offset + 1); |
| 119 |
| 120 // Check for compressed. |
| 121 if (data[offset] & 0xc0 == 0xc0) { |
| 122 // At least two bytes are required for a compressed FQDN. |
| 123 checkLength(offset + 2); |
| 124 |
| 125 // A compressed FQDN has a new offset in the lower 14 bits. |
| 126 FQDNReadResult result = _readFQDN( |
| 127 data, bd, bd.getUint16(offset) & ~0xc000, length); |
| 128 parts.addAll(result.fqdnParts); |
| 129 offset += 2; |
| 130 break; |
| 131 } else { |
| 132 // A normal FQDN part has a length and a UTF-8 encoded name |
| 133 // part. If the length is 0 this is the end of the FQDN. |
| 134 var partLength = data[offset]; |
| 135 offset++; |
| 136 if (partLength != 0) { |
| 137 checkLength(offset + partLength); |
| 138 var partBytes = new Uint8List.view(data.buffer, offset, partLength); |
| 139 offset += partLength; |
| 140 parts.add(UTF8.decode(partBytes)); |
| 141 } else { |
| 142 break; |
| 143 } |
| 144 } |
| 145 } |
| 146 return new FQDNReadResult(parts, offset - prevOffset); |
| 94 } | 147 } |
| 95 | 148 |
| 96 /// Decode a mDNS package. | 149 /// Decode a mDNS package. |
| 97 /// | 150 /// |
| 98 /// If decoding fails (e.g. due to an invalid packet) `null` is returned. | 151 /// If decoding fails (e.g. due to an invalid packet) `null` is returned. |
| 99 /// | 152 /// |
| 100 /// See https://tools.ietf.org/html/rfc1035 for the format. | 153 /// See https://tools.ietf.org/html/rfc1035 for the format. |
| 101 List<ResourceRecord> decodeMDnsResponse(List<int> packet) { | 154 List<ResourceRecord> decodeMDnsResponse(List<int> packet) { |
| 102 int length = packet.length; | 155 int length = packet.length; |
| 103 if (length < headerSize) return null; | 156 if (length < headerSize) return null; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 118 // Number of name server records. | 171 // Number of name server records. |
| 119 int nscount = bd.getUint16(nscountOffset); | 172 int nscount = bd.getUint16(nscountOffset); |
| 120 // Number of resource records. | 173 // Number of resource records. |
| 121 int arcount = bd.getUint16(arcountOffset); | 174 int arcount = bd.getUint16(arcountOffset); |
| 122 int offset = headerSize; | 175 int offset = headerSize; |
| 123 | 176 |
| 124 void checkLength(int required) { | 177 void checkLength(int required) { |
| 125 if (length < required) throw new MDnsDecodeException(required); | 178 if (length < required) throw new MDnsDecodeException(required); |
| 126 } | 179 } |
| 127 | 180 |
| 128 // Read a FQDN at the given offset. Returns a pair with the FQDN | |
| 129 // parts and the number of bytes consumed. | |
| 130 // | |
| 131 // If decoding fails (e.g. due to an invalid packet) `null` is returned. | |
| 132 FQDNReadResult readFQDN(int offset) { | |
| 133 List<String> parts = []; | |
| 134 int prevOffset = offset; | |
| 135 while (true) { | |
| 136 // At least two bytes required. | |
| 137 checkLength(offset + 2); | |
| 138 | |
| 139 // Check for compressed. | |
| 140 if (data[offset] & 0xc0 == 0xc0) { | |
| 141 // A compressed FQDN has a new offset in the lower 14 bits. | |
| 142 FQDNReadResult result = readFQDN(bd.getUint16(offset) & ~0xc000); | |
| 143 parts.addAll(result.fqdn); | |
| 144 offset += 2; | |
| 145 break; | |
| 146 } else { | |
| 147 // A normal FQDN part has a length and a UTF-8 encoded name | |
| 148 // part. If the length is 0 this is the end of the FQDN. | |
| 149 var partLength = data[offset]; | |
| 150 offset++; | |
| 151 if (partLength != 0) { | |
| 152 checkLength(offset + partLength); | |
| 153 var partBytes = new Uint8List.view(data.buffer, offset, partLength); | |
| 154 offset += partLength; | |
| 155 parts.add(UTF8.decode(partBytes)); | |
| 156 } else { | |
| 157 break; | |
| 158 } | |
| 159 } | |
| 160 } | |
| 161 return new FQDNReadResult(parts, offset - prevOffset); | |
| 162 } | |
| 163 | |
| 164 ResourceRecord readResourceRecord() { | 181 ResourceRecord readResourceRecord() { |
| 165 // First read the FQDN. | 182 // First read the FQDN. |
| 166 FQDNReadResult result = readFQDN(offset); | 183 FQDNReadResult result = _readFQDN(data, bd, offset, length); |
| 167 var fqdn = result.fqdn.join('.'); | 184 var fqdn = result.fqdn; |
| 168 offset += result.bytesRead; | 185 offset += result.bytesRead; |
| 169 checkLength(offset + 2); | 186 checkLength(offset + 2); |
| 170 int type = bd.getUint16(offset); | 187 int type = bd.getUint16(offset); |
| 171 offset += 2; | 188 offset += 2; |
| 172 // The first bit of the rrclass field is set to indicate that the answer is | 189 // 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 | 190 // 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. | 191 // (RFC 6762, Sec. 10.2). We ignore it for now since we don't cache answers. |
| 175 checkLength(offset + 2); | 192 checkLength(offset + 2); |
| 176 int cls = bd.getUint16(offset) & 0x7fff; | 193 int cls = bd.getUint16(offset) & 0x7fff; |
| 177 offset += 2; | 194 offset += 2; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 194 case RRType.SRV: | 211 case RRType.SRV: |
| 195 checkLength(offset + 2); | 212 checkLength(offset + 2); |
| 196 int priority = bd.getUint16(offset); | 213 int priority = bd.getUint16(offset); |
| 197 offset += 2; | 214 offset += 2; |
| 198 checkLength(offset + 2); | 215 checkLength(offset + 2); |
| 199 int weight = bd.getUint16(offset); | 216 int weight = bd.getUint16(offset); |
| 200 offset += 2; | 217 offset += 2; |
| 201 checkLength(offset + 2); | 218 checkLength(offset + 2); |
| 202 int port = bd.getUint16(offset); | 219 int port = bd.getUint16(offset); |
| 203 offset += 2; | 220 offset += 2; |
| 204 FQDNReadResult result = readFQDN(offset); | 221 FQDNReadResult result = _readFQDN(data, bd, offset, length); |
| 205 rData = result.fqdn.join('.'); | 222 rData = result.fqdn; |
| 206 offset += rDataLength - 6; | 223 offset += rDataLength - 6; |
| 207 break; | 224 break; |
| 208 case RRType.PTR: | 225 case RRType.PTR: |
| 209 checkLength(offset + rDataLength); | 226 checkLength(offset + rDataLength); |
| 210 FQDNReadResult result = readFQDN(offset); | 227 FQDNReadResult result = _readFQDN(data, bd, offset, length); |
| 211 offset += rDataLength; | 228 offset += rDataLength; |
| 212 rData = result.fqdn.join('.'); | 229 rData = result.fqdn; |
| 213 break; | 230 break; |
| 214 case RRType.TXT: | 231 case RRType.TXT: |
| 215 // TODO(karlklose): convert to a String or Map. | 232 // TODO(karlklose): convert to a String or Map. |
| 216 default: | 233 default: |
| 217 checkLength(offset + rDataLength); | 234 checkLength(offset + rDataLength); |
| 218 rData = new Uint8List.view(data.buffer, offset, rDataLength); | 235 rData = new Uint8List.view(data.buffer, offset, rDataLength); |
| 219 offset += rDataLength; | 236 offset += rDataLength; |
| 220 break; | 237 break; |
| 221 } | 238 } |
| 222 assert(rData != null); | 239 assert(rData != null); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 246 return result; | 263 return result; |
| 247 } | 264 } |
| 248 | 265 |
| 249 /// Exceptions thrown by decoder when the packet is invalid. | 266 /// Exceptions thrown by decoder when the packet is invalid. |
| 250 class MDnsDecodeException implements Exception { | 267 class MDnsDecodeException implements Exception { |
| 251 /// Exception message. | 268 /// Exception message. |
| 252 final int offset; | 269 final int offset; |
| 253 const MDnsDecodeException(this.offset); | 270 const MDnsDecodeException(this.offset); |
| 254 String toString() => 'Decoding error at $offset'; | 271 String toString() => 'Decoding error at $offset'; |
| 255 } | 272 } |
| OLD | NEW |