Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Unified Diff: pkg/mdns/lib/src/packet.dart

Issue 1426863003: Add a mDNS package (Closed) Base URL: git@github.com:dart-lang/fletch.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/mdns/lib/src/packet.dart
diff --git a/pkg/mdns/lib/src/packet.dart b/pkg/mdns/lib/src/packet.dart
new file mode 100644
index 0000000000000000000000000000000000000000..93fdb2db6a364e7235425c748a338a91ce8eecb9
--- /dev/null
+++ b/pkg/mdns/lib/src/packet.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mdns.src.packet;
+
+import 'dart:convert';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:mdns/src/constants.dart';
+
+// Encode a mDNS query packet.
+List<int> encodeMDNSQuery(String hostname) {
+ List parts = hostname.split('.');
+
+ // Calculate the size of the packet.
+ int size = headerSize;
+ for (int i = 0; i < parts.length; i++) {
+ parts[i] = UTF8.encode(parts[i]);
+ size += 1 + parts[i].length;
+ }
+ size += 1; // End with empty part
+ size += 4; // Trailer (QTYPE and QCLASS).
+ Uint8List data = new Uint8List(size);
+ ByteData bd = new ByteData.view(data.buffer);
+ bd.setUint16(idOffset, 0);
+ bd.setUint16(flagsOffset, 0);
+ bd.setUint16(qdcountOffset, 1);
karlklose 2015/10/28 10:14:30 Maybe add comments to describe the meaning of the
Søren Gjesse 2015/10/28 11:36:57 Done.
+ bd.setUint16(ancountOffset, 0);
+ bd.setUint16(nscountOffset, 0);
+ bd.setUint16(arcountOffset, 0);
+ int offset = headerSize;
+ for (int i = 0; i < parts.length; i++) {
+ data[offset++] = parts[i].length;
+ data.setRange(offset, offset + parts[i].length, parts[i]);
+ offset += parts[i].length;
+ }
+ data[offset] = 0; // Empty part.
+ offset++;
+ bd.setUint16(offset, 1); // QTYPE.
+ offset += 2;
+ bd.setUint16(offset, 1); // QCLASS.
+
+ return data;
+}
+
+/// FQDN and address decoded from response.
+class DecodeResult {
+ final String name;
+ final InternetAddress address;
+ DecodeResult(this.name, this.address);
+}
+
+/// Decode a mDNS package.
+///
+/// If decoding fails (e.g. die to an invalid packet) `null` is returned.
karlklose 2015/10/28 10:14:31 'die' -> 'due'.
Søren Gjesse 2015/10/28 11:36:57 Done.
+List<DecodeResult> decodeMDNSResponse(List<int> packet) {
+ int length = packet.length;
+ if (length < headerSize) return null;
+
+ Uint8List data =
+ packet is Uint8List ? packet : new Uint8List.fromList(packet);
+ ByteData bd = new ByteData.view(data.buffer);
+ int id = bd.getUint16(idOffset);
+ int flags = bd.getUint16(flagsOffset);
+
+ if (flags != responseFlags) return;
+ int qdcount = bd.getUint16(qdcountOffset);
+ int ancount = bd.getUint16(ancountOffset);
+ int nscount = bd.getUint16(nscountOffset);
+ int arcount = bd.getUint16(arcountOffset);
+ int offset = headerSize;
+
+ void checkLength(int required) {
+ if (length < required) throw new MDNSDecodeException(required);
+ }
+
+ // Read a FQDN at the given offset. Returns a pair with the FQDN
+ // parts and the number of bytes consumed.
+ //
+ // If decoding fails (e.g. die to an invalid packet) `null` is returned.
+ List readFQDN(int offset) {
karlklose 2015/10/28 10:14:30 I would prefer a few more type annotations on/in t
Søren Gjesse 2015/10/28 11:36:57 Added a type FQDNReadResult instead of using a two
+ var parts = [];
+ int prevOffset = offset;
+ while (true) {
+ // At least two bytes required.
+ checkLength(offset + 2);
+
+ // Check for compressed.
+ if (data[offset] & 0xc0 == 0xc0) {
+ // A compressed FQDN has a new offset in the lower 14 bits.
+ var pair = readFQDN(bd.getUint16(offset) & ~0xc000);
+ parts.addAll(pair[0]);
+ offset += 2;
+ break;
+ } else {
+ // A normal FQDN part has a length and a UTF-8 encoded name
+ // part. If the length is 0 this is the end of the FQDN.
+ var partLength = data[offset];
+ offset++;
+ if (partLength != 0) {
+ checkLength(offset + partLength);
+ var partBytes = new Uint8List.view(data.buffer, offset, partLength);
+ offset += partLength;
+ parts.add(UTF8.decode(partBytes));
+ } else {
+ break;
+ }
+ }
+ }
+ return [parts, offset - prevOffset];
+ }
+
+ DecodeResult readAddress() {
+ // First read the FQDN.
+ var pair = readFQDN(offset);
+ var fqdn = pair[0].join('.');
+ offset += pair[1];
+ checkLength(offset + 2);
+ int type = bd.getUint16(offset);
+ offset += 2;
+ checkLength(offset + 2);
+ int cls = bd.getUint16(offset);
+ offset += 2;
+ checkLength(offset + 4);
+ int ttl = bd.getInt32(offset);
+ offset += 4;
+ checkLength(offset + 2);
+ int addressLength = bd.getUint16(offset);
+ offset += 2;
+ checkLength(offset + addressLength);
+ var addressBytes = new Uint8List.view(data.buffer, offset, addressLength);
+ offset += addressLength;
+
+ if (type == ipV4AddressType && cls == ipV4Class && addressLength == 4) {
+ String addr = addressBytes.map((n) => n.toString()).join('.');
+ return new DecodeResult(fqdn, new InternetAddress(addr));
+ } else {
+ return null;
+ }
+ }
+
+ var result = [];
+ try {
+ while (data.length - offset >= 16) {
+ var address = readAddress();
+ if (address != null) result.add(address);
+ }
+ } on MDNSDecodeException catch (e, s) {
+ // If decoding fails return null.
+ return null;
+ }
+
+ return result;
+}
+
+/// Exceptions thrown by decoder when the packet is invalid.
+class MDNSDecodeException implements Exception {
+ /// Exception message.
+ final int offset;
+ const MDNSDecodeException(this.offset);
+ String toString() => 'Decoding error at $offset';
+}

Powered by Google App Engine
This is Rietveld 408576698