Open bugeats opened 4 years ago
Error seems to be coming from this bit of native code Socket_JoinMulticast
: https://github.com/dart-lang/sdk/blob/2.8.4/runtime/bin/socket.cc#L1174
EDIT: actual native code described here: https://github.com/dart-lang/sdk/issues/42250#issuecomment-642117024
@zanderso do you happen to know the best person to poke about this?
/cc @zichangg @sortie @a-siva
@zichangg
Thanks for reporting. Let me assign myself first!
@bugeats From your description, I think this is a problem of iOS instead of Dart. If it worked on iOS 10.3, it means our setup is good and something internal has changed since 10.3. But I was not able to find the relative docs or articles. Apple doesn't have a clear documentation on low-level POSIX APIs. I have no idea on how to mitigate the problem, unless we decide to change underlying implementation using higher-level APIs(They are well-documented!). Any suggestion? @dnfield @zanderso
@zichangg I opened a Feedback Assistant issue with Apple yesterday and pointed them to this ticket. I also reviewed iOS change logs and documentation and didn't find any mention of changes to POSIX sockets or anything like that.
My hope is that someone familiar with the Dart native internals like Socket_JoinMulticast
(linked above) can further isolate the issue down to the line so we actually know what we're working with if we do decide to take this to Apple.
@bugeats - it boils down to https://github.com/dart-lang/sdk/blob/97e291ba9fbf681f924e4fd71e7118ea14fecb8b/runtime/bin/socket_base_macos.cc#L447-L448 - a simple call to setsockopt.
It is possible that if you only tested this on the simulator, yo uwere actually using more like the host API than iOS API. Did this work for you on an iOS 10.3 device?
@dnfield I only tested simulators, bisecting iOS versions until I arrived at 10.3. This was after I discovered the failure on my 13.5 physical device. I'm going to go grab an old iPhone and install 10.3, but I suspect the simulator is a red herring.
EDIT: I'm afraid Apple does not allow downgrading devices. I can't repro on a 10.3 physical device without going down a jailbreak rabbit hole, and ain't nobody got time for that.
@dnfield I only tested simulators, bisecting iOS versions until I arrived at 10.3. This was after I discovered the failure on my 13.5 physical device. I'm going to go grab an old iPhone and install 10.3, but I suspect the simulator is a red herring.
EDIT: I'm afraid Apple does not allow downgrading devices. I can't repro on a 10.3 physical device without going down a jailbreak rabbit hole, and ain't nobody got time for that.
I meet the same problem, my iPhone version is 11.2.6; but the test on the Android phone is normal
Any Time Frame on a fix for this, It's a blocking issue for me atm.
Same here - this issue also extends to macOS, meaning any app that requires UDP multicasting/broadcasting can't be ported to any Apple devices.
@hacker1024 Which version of macOS you are running? Can you also provide a piece of reproduction code if possible? I'm using my macOS 10.15.4 to run the sample. It doesn't pop the error.
@zichangg - the error is happenning on iOS.
I haven't written up a minimal example of this, but it's sounding like Apple has broken this API. I'm not sure what Dart can do about that.
Same here - this issue also extends to macOS,
@hacker1024 has seen a failure on macOS. That's why I'm asking.
Oh sorry I missed that.
Hey all I finally got a response from the Apple ticket I submitted a month ago. Here it is verbatim:
Engineering has provided the following information regarding this issue: Usually the error "Can't assign requested address", errno = 49 (aka EADDRNOTAVAIL) is returned when the interface is not up. You need to make sure the interface he want to use is up.
@bugeats Our network interfaces are definitely up, I made sure of that as that seems to cause this issue in unrelated apps.
@zichangg I'm on Big Sur Beta 2, so perhaps it's only broken in that version. For sample code, I'm calling the following method in the udp
package:
UDP.bind(Endpoint.broadcast(port: 2925));
I tried a range of ports, including 25565 (which I know the Minecraft server binds to for UDP broadcasts), so I don't think the port has anything to do with it - some people report that the port can cause the error in other non-Flutter circumstances.
Just tried reproducing on xcode 11.3 simulator, but it seems to work fine.
import 'dart:io';
import 'dart:async';
import 'package:flutter/widgets.dart';
Future<void> demonstrateFailure() async {
final interfaces = await NetworkInterface.list(
includeLinkLocal: true,
type: InternetAddressType.IPv4,
includeLoopback: true,
);
final interface = interfaces[1];
final address = InternetAddress('224.0.0.251');
final port = 12347;
RawDatagramSocket.bind(InternetAddress.anyIPv4, 0).then((RawDatagramSocket socket){
print('Sending from ${socket.address.address}:${socket.port}');
Timer.periodic(Duration(seconds: 1), (_) {
print('Sent ${DateTime.now()}');
socket.send('${DateTime.now()}'.codeUnits, address, port);
});
});
final socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, port);
print("interface: $interface, socket: $socket, port:${socket.port}");
// FAILS HERE
// OS Error: Can't assign requested address, errno = 49;
socket.joinMulticast(address, interface);
print("joined multicast");
socket.listen((_) {
final d = socket.receive();
if (d == null) return;
print('Datagram from ${d.address.address}:${d.port}: ${String.fromCharCodes(d.data)}');
});
}
void main() async {
await demonstrateFailure();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container();
}
}
and it seems to work fine(after I accept pop-up permissions dialog):
Xcode build done. 16.6s
flutter: Sending from 0.0.0.0:49617
flutter: interface: NetworkInterface('en7', [InternetAddress('192.168.86.23', IPv4)]), socket: Instance of '_RawDatagramSocket', port:12347
flutter: joined multicast
Waiting for iPhone 11 Pro Max to report its views... 4ms
Syncing files to device iPhone 11 Pro Max... 132ms
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h Repeat this help message.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
An Observatory debugger and profiler on iPhone 11 Pro Max is available at: http://127.0.0.1:52747/mYAJoA1-HSM=/
flutter: Sent 2020-07-08 20:55:58.943408
flutter: Sent 2020-07-08 20:55:59.941748
flutter: Sent 2020-07-08 20:56:00.942935
flutter: Datagram from 192.168.86.23:49617: 2020-07-08 20:55:58.945481
flutter: Datagram from 192.168.86.23:49617: 2020-07-08 20:55:59.941989
flutter: Datagram from 192.168.86.23:49617: 2020-07-08 20:56:00.943231
flutter: Sent 2020-07-08 20:56:01.942048
@aam simulator always fine on my side, but physical device is not
@CimZzz are you able to run repro sample from above on your device? Can you share the output/how does it fail?
I tried it in my current project and I also get an error 49. But i get the name of the network interface (en0) and an ip address, so the answer @bugeats did get from apple is not 100% correct I assume.
Looks like no evidence proves this is a VM bug. I'll unassign myself first.
@aam
dart is 2.9.3 my device is ipad, 13.7
error is below:
2020-09-27 18:21:00.870410+0800 Runner[852:721510] flutter: InternetAddress('224.0.0.251', IPv4) 2020-09-27 18:21:00.877627+0800 Runner[852:721510] [VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: OS Error: Can't assign requested address, errno = 49
Does it work with https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_multicast?
this entitlement requires ios 14.0+, my ios is 13.7; and the entitlement is requied when use the nwconnectiongroup, https://developer.apple.com/documentation/network/nwconnectiongroup
This issue exists on my super old iPad 3 which does not update beyond iOS 9.3.6. I bet the issue is not about iOS version, but the macOS/iOS specifics, which is already mentioned in the code. Take a look at the multicastAddress
method, it has special handing for macOS but not iOS:
Since both macOS and iOS share the same underlying native implementation it should be:
if ((Platform.isMacOS || Platform.isIOS) && addr.type == InternetAddressType.IPv4) {
Someone asked if it ever worked in iOS. Well you can make it work right now. Joining a multicast group is actually setting special socket option. joinMulticast
does that for you with some additional processing (that part is broken in case of iOS). So you need to replace it with setRawOption
and provide it with byte representation of struct ip_mreq
. Here is a simplified example for multicast_dns
package:
// Join multicast on this interface.
// _incoming.joinMulticast(_mDnsAddress, interface);
final value = Uint8List.fromList(_mDnsAddress.rawAddress + interface.addresses[0].rawAddress);
_incoming.setRawOption(RawSocketOption(RawSocketOption.levelIPv4, 12, value));
Starting with iOS 11.0 the
dart:io
network interface library fails with an OS error when trying to join a multicast group:I have confirmed that the above works as expected with Flutter in Linux, and on an iOS simulator running 10.3.
This bug affects the Flutter multicast_dns package as described in https://github.com/flutter/flutter/issues/42102 , but it appears to be fundamentally a Dart SDK issue. As of this moment, the current version of iOS is 13.5.1 so this regression has snuck by several major updates.