Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binding to multicast socket fails in iOS >= 11.0 #42250

Open
bugeats opened this issue Jun 9, 2020 · 27 comments
Open

Binding to multicast socket fails in iOS >= 11.0 #42250

bugeats opened this issue Jun 9, 2020 · 27 comments
Labels
area-vm Use area-vm for VM related issues, including code coverage, FFI, and the AOT and JIT backends. customer-flutter library-io os-osx type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@bugeats
Copy link

bugeats commented Jun 9, 2020

Starting with iOS 11.0 the dart:io network interface library fails with an OS error when trying to join a multicast group:

[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: OS Error: Can't assign requested address, errno = 49
#0 _NativeSocket.nativeJoinMulticast (dart:io-patch/socket_patch.dart:1379:56)
#1 _NativeSocket.joinMulticast (dart:io-patch/socket_patch.dart:1331:5)
#2 _RawDatagramSocket.joinMulticast (dart:io-patch/socket_patch.dart:2127:13)
#3 demonstrateFailure (package:sockfix/main.dart:18:10)

import 'dart:io';
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 socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 12347);

  // FAILS HERE
  // OS Error: Can't assign requested address, errno = 49;
  socket.joinMulticast(address, interface);
}

void main() async {
  await demonstrateFailure();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

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 flutter/flutter#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.

@bugeats
Copy link
Author

bugeats commented Jun 9, 2020

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: #42250 (comment)

@jmagman
Copy link

jmagman commented Jun 9, 2020

@zanderso do you happen to know the best person to poke about this?

@zanderso zanderso added area-vm Use area-vm for VM related issues, including code coverage, FFI, and the AOT and JIT backends. customer-flutter library-io os-osx type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Jun 10, 2020
@zanderso
Copy link
Member

/cc @zichangg @sortie @a-siva

@dnfield
Copy link
Contributor

dnfield commented Jun 10, 2020

@zichangg

@zichangg
Copy link
Contributor

Thanks for reporting. Let me assign myself first!

@zichangg zichangg self-assigned this Jun 10, 2020
@zichangg
Copy link
Contributor

@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

@bugeats
Copy link
Author

bugeats commented Jun 10, 2020

@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.

@dnfield
Copy link
Contributor

dnfield commented Jun 10, 2020

@bugeats - it boils down to

return NO_RETRY_EXPECTED(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq))) == 0;
- 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?

@bugeats
Copy link
Author

bugeats commented Jun 10, 2020

@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.

@zzdota
Copy link

zzdota commented Jun 16, 2020

@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

@clarkap
Copy link

clarkap commented Jul 7, 2020

Any Time Frame on a fix for this, It's a blocking issue for me atm.

@hacker1024
Copy link
Contributor

Same here - this issue also extends to macOS, meaning any app that requires UDP multicasting/broadcasting can't be ported to any Apple devices.

@zichangg
Copy link
Contributor

zichangg commented Jul 8, 2020

@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.

@dnfield

This comment has been minimized.

@zichangg

This comment has been minimized.

@dnfield

This comment has been minimized.

@bugeats
Copy link
Author

bugeats commented Jul 8, 2020

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.

@hacker1024
Copy link
Contributor

@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.

@aam
Copy link
Contributor

aam commented Jul 9, 2020

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

@CimZzz
Copy link

CimZzz commented Jul 27, 2020

@aam simulator always fine on my side, but physical device is not

@aam
Copy link
Contributor

aam commented Jul 27, 2020

@CimZzz are you able to run repro sample from above on your device? Can you share the output/how does it fail?

@johannesschrott
Copy link

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.

@zichangg
Copy link
Contributor

zichangg commented Sep 1, 2020

Looks like no evidence proves this is a VM bug. I'll unassign myself first.

@zichangg zichangg removed their assignment Sep 1, 2020
@liufeihe
Copy link

@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
#0 _NativeSocket.nativeJoinMulticast (dart:io-patch/socket_patch.dart:1420:56)
#1 _NativeSocket.joinMulticast (dart:io-patch/socket_patch.dart:1371:5)
#2 _RawDatagramSocket.joinMulticast (dart:io-patch/socket_patch.dart:2163:13)
#3 MDnsClient.start (package:play_flutter/multi_dns/multi_dns.dart:152:17)

@dnfield
Copy link
Contributor

dnfield commented Sep 28, 2020

@liufeihe
Copy link

liufeihe commented Sep 29, 2020

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

@alexbatalov
Copy link

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:

// On Mac OS using the interface index for joining IPv4 multicast groups
// is not supported. Here the IP address of the interface is needed.
if (Platform.isMacOS && addr.type == InternetAddressType.IPv4) {

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));

dart-bot pushed a commit that referenced this issue Jul 13, 2021
The socket implementation has a workaround for MacOS, which is also required on iOS. @alexbatalov suggested this fix in #42250 (comment).

Verified on iPhone 11 with iOS 14.6.

Bug: http://dartbug.com/42250

TEST=Existing test base. Manually verified on iOS

Change-Id: I9942ccaf3bdd65e04842a38697eadc19feb317d9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206020
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Auto-Submit: Marcin Radomski <dextero@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-vm Use area-vm for VM related issues, including code coverage, FFI, and the AOT and JIT backends. customer-flutter library-io os-osx type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests