diff --git a/example/get_resource_multicast.dart b/example/get_resource_multicast.dart index 9ad36b1c..9e591a4c 100644 --- a/example/get_resource_multicast.dart +++ b/example/get_resource_multicast.dart @@ -14,14 +14,22 @@ */ import 'dart:async'; +import 'dart:io'; import 'package:coap/coap.dart'; FutureOr main() async { - final uri = Uri.parse('coap://${MulticastAddress.allNodesLinkLocalIPV6}'); + final iface = Platform.environment['COAP_IFACE']; + final uri = + iface == null + ? Uri.parse('coap://${MulticastAddress.allCOAPNodesLinkLocalIPV6}') + : Uri.parse( + 'coap://[${MulticastAddress.allCOAPNodesLinkLocalIPV6.address}%$iface]', + ); final client = CoapClient(uri); try { final request = CoapRequest.get(Uri(path: '/.well-known/core')); + request.token = CoapConstants.emptyToken; await for (final response in client.sendMulticast(request)) { print(response.payloadString); diff --git a/lib/src/coap_client.dart b/lib/src/coap_client.dart index bcfe98e4..ae46d888 100644 --- a/lib/src/coap_client.dart +++ b/lib/src/coap_client.dart @@ -661,12 +661,16 @@ class CoapClient { final String host, final InternetAddressType addressType, ) async { - final parsedAddress = InternetAddress.tryParse(host); + final decodedHost = Uri.decodeComponent(host); + final parsedAddress = InternetAddress.tryParse(decodedHost); if (parsedAddress != null) { return parsedAddress; } - final addresses = await InternetAddress.lookup(host, type: addressType); + final addresses = await InternetAddress.lookup( + decodedHost, + type: addressType, + ); if (addresses.isNotEmpty) { return addresses.first; } diff --git a/lib/src/network/coap_network_udp.dart b/lib/src/network/coap_network_udp.dart index 10f9567e..caf454f5 100644 --- a/lib/src/network/coap_network_udp.dart +++ b/lib/src/network/coap_network_udp.dart @@ -82,6 +82,44 @@ class CoapNetworkUDP implements CoapINetwork { // Use port 0 to generate a random source port _socket = await RawDatagramSocket.bind(bindAddress, 0); + _socket!.multicastLoopback = false; + + if (address.isMulticast) { + final iface = await _findInterfaceFor(address); + if (iface != null) { + _socket!.setRawOption( + RawSocketOption.fromInt( + RawSocketOption.levelIPv6, + RawSocketOption.IPv6MulticastInterface, + iface.index, + ), + ); + _socket!.joinMulticast(address, iface); + } else { + _socket!.joinMulticast(address); + } + } + } + + Future _findInterfaceFor( + final InternetAddress multicastAddress, + ) async { + final zoneIndex = multicastAddress.address.split('%'); + if (zoneIndex.length != 2) { + return null; + } + + final ifaceName = zoneIndex.last; + final interfaces = await NetworkInterface.list( + type: InternetAddressType.any, + includeLinkLocal: true, + ); + + try { + return interfaces.firstWhere((final i) => i.name == ifaceName); + } on StateError { + return null; + } } void _receive() {