Skip to content
This repository was archived by the owner on Sep 15, 2022. It is now read-only.

Commit 7b02aed

Browse files
committed
Extend the mDNS package with a native extension used on Mac OS
On Mac OS the mDNSResponder daemon has opened port 5353 exclusively. Therefore it is not possible to speak the mDNS protocol from Dart using datagram sockets. The native extension uses the Mac OS DNS Service Discovery C API. Updated the copyright notice to use Fletch instead of Dart. BUG= #225 [email protected], [email protected] Review URL: https://codereview.chromium.org/1407123010 .
1 parent f5f9558 commit 7b02aed

25 files changed

+670
-98
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ packages
1717
/fletch.xcodeproj/
1818
/jniLibs/
1919
/out/
20+
/pkg/mdns/lib/native/libmdns_extension_lib.so
21+
/pkg/mdns/lib/native/libmdns_extension_lib.dylib
2022
/samples/buildbot/java/
2123
/samples/github/generated/
2224
/samples/todomvc/java/

DEPS

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,19 @@ hooks = [
134134
'fletch/tools/testing/bin',
135135
],
136136
},
137+
{
138+
'name': 'mdns_native_extension_binaries',
139+
'pattern': '.',
140+
'action': [
141+
'download_from_google_storage',
142+
'--no_auth',
143+
'--no_resume',
144+
'--bucket',
145+
'dart-dependencies-fletch',
146+
'-d',
147+
'fletch/pkg/mdns/lib/native',
148+
],
149+
},
137150
{
138151
'name': 'third_party_qemu',
139152
'pattern': '.',

build.ninja

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ build $
6565
src/shared/shared.gyp $
6666
src/tools/driver/driver.gyp $
6767
src/vm/vm.gyp $
68+
src/pkg/mdns/mdns.gyp $
6869

6970
build gyp: phony$
7071
out/DebugIA32/build.ninja $

fletch.gyp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
'src/vm/vm.gyp:ffi_test_library',
1919
],
2020
},
21+
{
22+
'target_name': 'mdns_extension_lib',
23+
'type': 'none',
24+
'dependencies': [
25+
'src/pkg/mdns/mdns.gyp:mdns_extension_lib',
26+
],
27+
},
2128
{
2229
'target_name': 'natives_json',
2330
'type': 'none',

pkg/mdns/lib/mdns.dart

Lines changed: 32 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,48 @@
1-
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file
22
// 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.md file.
4+
5+
library mdns;
46

57
import 'dart:async';
68
import 'dart:collection';
79
import 'dart:io';
810
import 'dart:typed_data';
911

10-
import 'package:mdns/src/constants.dart';
11-
import 'package:mdns/src/lookup_resolver.dart';
12-
import 'package:mdns/src/packet.dart';
12+
import 'package:mdns/src/native_extension_client.dart';
13+
import 'package:mdns/src/native_protocol_client.dart';
1314

1415
/// Client for DNS lookup using the mDNS protocol.
1516
///
1617
/// This client only support "One-Shot Multicast DNS Queries" as described in
1718
/// section 5.1 of https://tools.ietf.org/html/rfc6762
18-
class MDnsClient {
19-
bool _starting = false;
20-
bool _started = false;
21-
RawDatagramSocket _incoming;
22-
final List<RawDatagramSocket> _sockets = <RawDatagramSocket>[];
23-
final LookupResolver _resolver = new LookupResolver();
24-
25-
/// Start the mDNS client.
26-
Future start() async {
27-
if (_started && _starting) {
28-
throw new StateError('mDNS client already started');
29-
}
30-
_starting = true;
31-
32-
// Listen on all addresses.
33-
_incoming = await RawDatagramSocket.bind(
34-
InternetAddress.ANY_IP_V4, mDnsPort, reuseAddress: true);
35-
36-
// Find all network interfaces with an IPv4 address.
37-
var interfaces =
38-
await NetworkInterface.list(type: InternetAddressType.IP_V4);
39-
for (NetworkInterface interface in interfaces) {
40-
// Create a socket for sending on each adapter.
41-
var socket = await RawDatagramSocket.bind(
42-
interface.addresses[0], mDnsPort, reuseAddress: true);
43-
_sockets.add(socket);
44-
45-
// Join multicast on this interface.
46-
_incoming.joinMulticast(mDnsAddress, interface);
19+
abstract class MDnsClient {
20+
// Instantiate Client for DNS lookup using the mDNS protocol.
21+
//
22+
// On Mac OS a native extension is used as the mDNSResponder opens the mDNS
23+
// port in exclusive mode. To test the protocol implementation on Mac OS
24+
// one can turn off mDNSResponder:
25+
//
26+
// sudo launchctl unload -w \
27+
// /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
28+
//
29+
// And turn it on again:
30+
//
31+
// sudo launchctl load -w \
32+
// /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
33+
factory MDnsClient() {
34+
if (Platform.isMacOS) {
35+
return new NativeExtensionMDnsClient();
36+
} else {
37+
return new NativeProtocolMDnsClient();
4738
}
48-
_incoming.listen(_handleIncoming);
49-
50-
_starting = false;
51-
_started = true;
5239
}
5340

54-
/// Stop the mDNS client.
55-
void stop() {
56-
if (!_started) return;
57-
if (_starting) {
58-
throw new StateError('Cannot stop mDNS client wile it is starting');
59-
}
60-
61-
_sockets.forEach((socket) => socket.close());
62-
_incoming.close();
41+
/// Start the mDNS client.
42+
Future start();
6343

64-
_started = false;
65-
}
44+
/// Stop the mDNS client.
45+
void stop();
6646

6747
/// Lookup [hostname] using mDNS.
6848
///
@@ -72,40 +52,14 @@ class MDnsClient {
7252
/// If no answer has been received within the specified [timeout]
7353
/// this method will complete with the value `null`.
7454
Future<InternetAddress> lookup(
75-
String hostname, {Duration timeout: const Duration(seconds: 5)}) {
76-
if (!_started) {
77-
throw new StateError('mDNS client is not started');
78-
}
79-
80-
// Add the pending request before sending the query.
81-
var future = _resolver.addPendingRequest(hostname, timeout);
82-
83-
// Send the request on all interfaces.
84-
List<int> packet = encodeMDnsQuery(hostname);
85-
for (int i = 0; i < _sockets.length; i++) {
86-
_sockets[i].send(packet, mDnsAddress, mDnsPort);
87-
}
88-
89-
return future;
90-
}
91-
92-
// Process incoming datagrams.
93-
_handleIncoming(event) {
94-
if (event == RawSocketEvent.READ) {
95-
var data = _incoming.receive();
96-
var response = decodeMDnsResponse(data.data);
97-
if (response != null) {
98-
_resolver.handleResponse(response);
99-
}
100-
}
101-
}
55+
String hostname, {Duration timeout: const Duration(seconds: 5)});
10256
}
10357

10458
// Simple standalone test.
105-
main() async {
59+
Future main(List<String> args) async {
10660
var client = new MDnsClient();
10761
await client.start();
108-
var address = await client.lookup('raspberrypi.local');
62+
var address = await client.lookup(args[0]);
10963
client.stop();
11064
print(address);
11165
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22e12795d48159983c756e96b4355ca63b020676
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a1bcfd86ed70bf854de519d29cf354871459d825

pkg/mdns/lib/src/constants.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file
22
// 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.md file.
44

55
library mdns.src.constants;
66

pkg/mdns/lib/src/lookup_resolver.dart

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file
22
// 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.md file.
44

55
library mdns.src.lookup_resolver;
66

@@ -34,13 +34,16 @@ class LookupResolver {
3434

3535
void handleResponse(List<DecodeResult> response) {
3636
for (var r in response) {
37+
var name = r.name.toLowerCase();
38+
if (name.endsWith('.')) name = name.substring(0, name.length - 1);
3739
pendingRequests
38-
.where((pendingRequest) => pendingRequest.hostname == r.name)
40+
.where((pendingRequest) {
41+
return pendingRequest.hostname.toLowerCase() == name;
42+
})
3943
.forEach((pendingRequest) {
4044
pendingRequest.completer.complete(r.address);
4145
pendingRequest.unlink();
42-
});
43-
}
46+
});
47+
}
4448
}
4549
}
46-
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file
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.md file.
4+
5+
library mdns.src.native_extension_client;
6+
7+
import 'dart:async';
8+
import 'dart:io';
9+
import 'dart:isolate';
10+
11+
import 'package:mdns/mdns.dart';
12+
import 'package:mdns/src/lookup_resolver.dart';
13+
import 'package:mdns/src/packet.dart';
14+
15+
import "dart-ext:../native/mdns_extension_lib";
16+
17+
// Requests Ids. This should be aligned with the C code.
18+
enum RequestType {
19+
echoRequest, // 0
20+
lookupRequest, // 1
21+
}
22+
23+
// Implementation of mDNS client using a native extension.
24+
class NativeExtensionMDnsClient implements MDnsClient {
25+
bool _starting = false;
26+
bool _started = false;
27+
SendPort _service;
28+
ReceivePort _incoming;
29+
final LookupResolver _resolver = new LookupResolver();
30+
31+
/// Start the mDNS client.
32+
Future start() async {
33+
if (_started && _starting) {
34+
throw new StateError('mDNS client already started');
35+
}
36+
_starting = true;
37+
38+
_service = servicePort();
39+
_incoming = new ReceivePort();
40+
_incoming.listen(_handleIncoming);
41+
42+
_starting = false;
43+
_started = true;
44+
}
45+
46+
void stop() {
47+
if (!_started) return;
48+
if (_starting) {
49+
throw new StateError('Cannot stop mDNS client wile it is starting');
50+
}
51+
52+
_incoming.close();
53+
54+
_started = false;
55+
}
56+
57+
Future<InternetAddress> lookup(
58+
String hostname, {Duration timeout: const Duration(seconds: 5)}) {
59+
if (!_started) {
60+
throw new StateError('mDNS client is not started');
61+
}
62+
63+
// Add the pending request before sending the query.
64+
var future = _resolver.addPendingRequest(hostname, timeout);
65+
66+
// Send the request.
67+
_service.send([_incoming.sendPort,
68+
RequestType.lookupRequest.index,
69+
hostname]);
70+
71+
return future;
72+
}
73+
74+
// Process incoming responses.
75+
_handleIncoming(response) {
76+
// Right not the only response we can get is the response to a
77+
// lookupRequest where the response looks like this:
78+
//
79+
// response[0]: hostname (String)
80+
// response[1]: IPv4 address (Uint8List)
81+
if (response is List && response.length == 2) {
82+
if (response[0] is String &&
83+
response[1] is List && response[1].length == 4) {
84+
response = new DecodeResult(response[0],
85+
new InternetAddress(response[1].join('.')));
86+
_resolver.handleResponse([response]);
87+
} else {
88+
// TODO(sgjesse): Improve the error handling.
89+
print('mDNS Response not understood');
90+
}
91+
}
92+
}
93+
}
94+
95+
Future nativeExtensionEchoTest(dynamic message) async {
96+
SendPort service = servicePort();
97+
ReceivePort port = new ReceivePort();
98+
try {
99+
service.send([port.sendPort, RequestType.echoRequest.index, message]);
100+
return await port.first;
101+
} finally {
102+
port.close();
103+
}
104+
}
105+
106+
SendPort servicePort() native 'MDnsExtension_ServicePort';

0 commit comments

Comments
 (0)