diff --git a/packages/multicast_dns/CHANGELOG.md b/packages/multicast_dns/CHANGELOG.md index 03dcad8632..29e2aefc1d 100644 --- a/packages/multicast_dns/CHANGELOG.md +++ b/packages/multicast_dns/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.2.0 +* Allow configuration of the port and address the mdns query is performed on. + ## 0.1.1 * Fixes [flutter/issue/31854](https://github.com/flutter/flutter/issues/31854) where `decodeMDnsResponse` advanced to incorrect code points and ignored some records. diff --git a/packages/multicast_dns/lib/multicast_dns.dart b/packages/multicast_dns/lib/multicast_dns.dart index eeb71d1262..3b8fa47abf 100644 --- a/packages/multicast_dns/lib/multicast_dns.dart +++ b/packages/multicast_dns/lib/multicast_dns.dart @@ -24,6 +24,14 @@ export 'package:multicast_dns/src/resource_record.dart'; typedef NetworkInterfacesFactory = Future> Function( InternetAddressType type); +/// A factory for construction of datagram sockets. +/// +/// This can be injected into the [MDnsClient] to provide alternative +/// implementations of [RawDatagramSocket.bind]. +typedef RawDatagramSocketFactory = Future Function( + dynamic host, int port, + {bool reuseAddress, bool reusePort, int ttl}); + /// Client for DNS lookup and publishing using the mDNS protocol. /// /// Users should call [MDnsQuerier.start] when ready to start querying and @@ -33,13 +41,21 @@ typedef NetworkInterfacesFactory = Future> Function( /// This client only supports "One-Shot Multicast DNS Queries" as described in /// section 5.1 of [RFC 6762](https://tools.ietf.org/html/rfc6762). class MDnsClient { + /// Create a new [MDnsClient]. + MDnsClient({ + RawDatagramSocketFactory rawDatagramSocketFactory = RawDatagramSocket.bind, + }) : _rawDatagramSocketFactory = rawDatagramSocketFactory; + bool _starting = false; bool _started = false; RawDatagramSocket _incoming; final List _sockets = []; final LookupResolver _resolver = LookupResolver(); final ResourceRecordCache _cache = ResourceRecordCache(); + final RawDatagramSocketFactory _rawDatagramSocketFactory; + InternetAddress _mDnsAddress; + int _mDnsPort; /// Find all network interfaces with an the [InternetAddressType] specified. static NetworkInterfacesFactory allInterfacesFactory = @@ -58,12 +74,23 @@ class MDnsClient { /// [InternetAddress.anyIPv6], and will default to anyIPv4. /// /// The [interfaceFactory] defaults to [allInterfacesFactory]. + /// + /// The [mDnsPort] allows configuring what port is used for the mDNS + /// query. If not provided, defaults to `5353`. + /// + /// The [mDnsAddress] allows configuring what internet address is used + /// for the mDNS query. If not provided, defaults to either `224.0.0.251` or + /// or `FF02::FB`. Future start({ InternetAddress listenAddress, NetworkInterfacesFactory interfacesFactory, + int mDnsPort = mDnsPort, + InternetAddress mDnsAddress, }) async { listenAddress ??= InternetAddress.anyIPv4; interfacesFactory ??= allInterfacesFactory; + _mDnsPort = mDnsPort; + _mDnsAddress = mDnsAddress; assert(listenAddress.address == InternetAddress.anyIPv4.address || listenAddress.address == InternetAddress.anyIPv6.address); @@ -74,9 +101,9 @@ class MDnsClient { _starting = true; // Listen on all addresses. - _incoming = await RawDatagramSocket.bind( + _incoming = await _rawDatagramSocketFactory( listenAddress.address, - mDnsPort, + _mDnsPort, reuseAddress: true, reusePort: true, ttl: 255, @@ -87,7 +114,7 @@ class MDnsClient { _sockets.add(_incoming); } - _mDnsAddress = _incoming.address.type == InternetAddressType.IPv4 + _mDnsAddress ??= _incoming.address.type == InternetAddressType.IPv4 ? mDnsAddressIPv4 : mDnsAddressIPv6; @@ -97,9 +124,9 @@ class MDnsClient { for (NetworkInterface interface in interfaces) { // Create a socket for sending on each adapter. final InternetAddress targetAddress = interface.addresses[0]; - final RawDatagramSocket socket = await RawDatagramSocket.bind( + final RawDatagramSocket socket = await _rawDatagramSocketFactory( targetAddress, - mDnsPort, + _mDnsPort, reuseAddress: true, reusePort: true, ttl: 255, @@ -180,7 +207,7 @@ class MDnsClient { // Send the request on all interfaces. final List packet = query.encode(); for (RawDatagramSocket socket in _sockets) { - socket.send(packet, _mDnsAddress, mDnsPort); + socket.send(packet, _mDnsAddress, _mDnsPort); } return results; } diff --git a/packages/multicast_dns/pubspec.yaml b/packages/multicast_dns/pubspec.yaml index c1a117400b..8eb23b7976 100644 --- a/packages/multicast_dns/pubspec.yaml +++ b/packages/multicast_dns/pubspec.yaml @@ -2,13 +2,14 @@ name: multicast_dns description: Dart package for mDNS queries (e.g. Bonjour, Avahi). author: Flutter Team homepage: https://github.com/flutter/packages/tree/master/packages/multicast_dns -version: 0.1.1 +version: 0.2.0 dependencies: meta: ^1.1.6 dev_dependencies: test: "^1.3.4" + mockito: "^4.1.0" environment: sdk: ">=2.1.1-dev.2.0 <3.0.0" diff --git a/packages/multicast_dns/test/client_test.dart b/packages/multicast_dns/test/client_test.dart new file mode 100644 index 0000000000..f327c20e09 --- /dev/null +++ b/packages/multicast_dns/test/client_test.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2015, the Dartino 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. + +import 'dart:io'; + +import 'package:multicast_dns/multicast_dns.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +void main() { + test('Can inject datagram socket factory and configure mdns port', () async { + int lastPort; + final MockRawDatagramSocket mockRawDatagramSocket = MockRawDatagramSocket(); + final MDnsClient client = MDnsClient(rawDatagramSocketFactory: + (dynamic host, int port, + {bool reuseAddress, bool reusePort, int ttl = 1}) async { + lastPort = port; + return mockRawDatagramSocket; + }); + when(mockRawDatagramSocket.address).thenReturn(InternetAddress.anyIPv4); + + await client.start( + mDnsPort: 1234, + interfacesFactory: (InternetAddressType type) async => + []); + + expect(lastPort, 1234); + }); +} + +class MockRawDatagramSocket extends Mock implements RawDatagramSocket {}