POSIX sendto fails due to Sequoia's new LAN Privacy & Security permission request

PLATFORM AND VERSION iOS

Development environment: Xcode Version 16.0 (16A242d), macOS 15.0 (24A335)

Run-time configuration: macOS 15.0 (24A335)

DESCRIPTION OF PROBLEM

macOS Sequoia has new Privacy & Security requirements for local area network access. This causes a call to POSIX socket function 'sendto' to fail immediately with EHOSTUNREACH on the first execution of any app that calls it. That failure occurs even if the socket is set to block for well over the time that it would typically take for the user to click "Allow" when presented with a system dialogue box that requests new permissions for the app.

A test XCode project has been provided that is capable of reproducing the issue (see macOSsocketfail.zip at https://drive.google.com/file/d/14VxkT03ddm48RCXikLHf-aWgdqxwnpAB/view?usp=sharing). It will generate a log file that contains time-stamped messages. They report each step in the creation of a blocking UDP socket – and which system call has failed as a result of which error. The message time stamps demonstrate that macOS has terminated the sendto function call after tens of microseconds, well before the 5 minute timeout on the socket, and that it terminates with errno EHOSTUNREACH. That error is misleading, because the destination is pingable on my setup at the time of execution. The second execution of the app functions without error if "Allow" has been selected during the first run.

This specific macOS behaviour does not appear to be documented anywhere that I have yet encountered; e.g. the sendto man page, the Privacy & Security LAN FAQ, etc. It is, however, highly disruptive to the use of our product, which hinges on LAN access. We have a situation in which a relatively large collection of apps are using the same shared library to manage network access. All of them must now receive manual permission from an administrator to work; but all of them will fail on their first execution. The problem is amplified because our customers use our framework to build their own apps, and not every user is an administrator. In contrast, apps that use our framework would simply work without issue on their first execution when run on macOS versions that precede Sequoia.

We must support our software across multiple platforms, hence the reason that we are using POSIX function calls to implement networking. Unfortunately, the use of an Apple-specific networking API is not a viable solution for us.

How should we mitigate this problem? Is there some way to configure an Xcode project so that the build product will already have Sequoia LAN permissions? I have read about the com.apple.developer.networking.multicast entitlement, but it is unclear whether it will help us, from the material that is available.

STEPS TO REPRODUCE

POSIX function call sequence

For the following, addr has type struct sockaddr_in, and it is set appropriately for binding or broadcasting using standard library macros and functions. &addr is cast to a const struct sockaddr pointer and assigned to saddr.

  1. sock = socket( PF_INET, SOCK_DGRAM , 0 ) ;
  2. bind( sock, saddr, sizeof( addr ) ) ;
  3. r = 1 ; setsockopt( sock, SOL_SOCKET, SO_BROADCAST, &r, sizeof( r ) );
  4. struct timeval timeout = { 300 , 0 }; setsockopt( sock , SOL_SOCKET , SO_RCVTIMEO , &timeout , sizeof( timeout ) );
  5. sendto( sock, msg, strlen( msg ) + 1, 0, saddr , sizeof( addr ) ) ;

Test program, presuming that a device with IP 100.1.1.1 exists on the LAN that the mac is also connected to.

  1. Open Xcode project macOSsocketfail.
  2. Build project.
  3. Execute first run of macOSsocketfail.app.
  4. Click "Allow" when asked for LAN permissions.
  5. Search for macOSsocketfail_log.txt, and make a copy with a distinct name e.g. macOSsocketfail_log_firstrun.txt.
  6. Execute second run of macOSsocketfail.app.
  7. Search for macOSsocketfail_log.txt, and make a copy with a distinct name e.g. macOSsocketfail_log_secondrun.txt.
  8. Examine log files.

The first run log file will contain the following messages (time stamping will naturally differ):

1727710614.064009: Running udptest

1727710614.064015: Creating socket

1727710614.064030: Bind socket to port: 4000

1727710614.064061: Enable socket broadcast

1727710614.064064: Set socket timeout to 300.000000sec

1727710614.064067: Attempt to send blocking UDP connection packet to 100.1.1.1:589

1727710614.064124: sendto: No route to host

Error during call to sendto: errno is EHOSTUNREACH: No route to host

The second run log file will record a different outcome:

1727713660.733431: Running udptest

1727713660.733436: Creating socket

1727713660.733451: Bind socket to port: 4000

1727713660.733476: Enable socket broadcast

1727713660.733479: Set socket timeout to 300.000000sec

1727713660.733482: Attempt to send blocking UDP connection packet to 100.1.1.1:589

1727713660.733540: Ran to completion with no error detected

Note that each line of the log files begins with a timestamp. The unit is seconds, and the resolution is to the nearest microsecond. Time values are obtained using gettimeofday().

RELEVANT LINKS

https://developer.apple.com/forums/thread/663858

https://forums.developer.apple.com/forums/thread/757824

https://developer.apple.com/forums/thread/760964

https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_multicast/

https://developer.apple.com/forums/thread/657887

https://developer.apple.com/forums/thread/655920

This issue was mentioned in this WWDC video. It refers you to this older video from WWDC 2020. You already mentioned two of the other links I was going to refer to you.

I tried your app and it said it ran to completion with no error. It doesn't seem like it should work at all since you don't seem to have the proper entitlement and usage string. I thought I had changed the destination IP to be a local IP address. But I was testing in a VM, so maybe the networking is too funky and it was treating it as an internet connection.

You don't need to be administrator to approve local network access.

Obviously, for issues like this, Apple does not provide any way for an app to bypass user consent. That would defeat the purpose. Apple does usually provide some MDM key to preset the value for managed devices.

If this is library code, I don't see how it would be a problem. It's the app developer's responsibility to manage this.

POSIX sendto fails due to Sequoia's new LAN Privacy & Security permission request
 
 
Q