Hi all,
I am overhauling code of an iPadOS app that discovers devices on a network using a custom UDP broadcast based discovery protocol.
This is how the discovery mechanism should work: The iPad sends an IPv4 broadcast message to the network's broadcast address using a fixed destination port, but a random source port (determined at bind time). The device responds with a unicast message to the source IP address and port of the discovery message.
Until now the code is based on BSD sockets using GCDAsyncUdpSocket and has been working well for around ten years with a single socket that was used to both send and receive the discovery messages and replies.
We would like to make the move to the Network Framework now and I tried to recreate this discovery mechanism with the Network Framework in Objective-C.
I am able to create an nw_connection_t with the broadcast address as hostname and the specific destination port as port. I am able to send discovery messages to the device and the device sends a reply (verified with Wireshark). But calling nw_connection_receive_message(...) never fires. Also in Wireshark the iPad replies with Destination unreachable (Port unreachable).
When I create the connection with the unicast address of the device, the reply is received.
It seems to me, that the connection doesn't accept replies from addresses / ports that don't match, what was set when the connection was created. Is there a way to also accept messages from other sources? E.g. there is nw_multicast_group_descriptor_set_disable_unicast_traffic when doing multicast. This seems to solve this problems when doing mutlicast.
This is a code excerpt of what I tried:
// Create default UDP parameters without DTLS
nw_parameters_t params = nw_parameters_create_secure_udp(NW_PARAMETERS_DISABLE_PROTOCOL, NW_PARAMETERS_DEFAULT_CONFIGURATION);
// Enable P2P (should enable broadcast and multicast)
nw_parameters_set_include_peer_to_peer(params, true);
// Require the active interface
// The active interface comes from a path monitor callback
nw_parameters_require_interface(params, self.networkUtils.activeInterface.interface);
// Setup the remote endpoint with the "ping" (discovery) broadcast IP address and port
const char *endpointAddress = [pingAddress.ipAddress cStringUsingEncoding:NSUTF8StringEncoding];
NSString *portString = @(pingAddress.port).stringValue;
const char *endpointPort = [portString cStringUsingEncoding:NSUTF8StringEncoding];
nw_endpoint_t broadcastEndpoint = nw_endpoint_create_host(endpointAddress, endpointPort);
nw_connection_t tmpConnection = nw_connection_create(broadcastEndpoint, params);
__weak __typeof(self) weakSelf = self;
nw_connection_set_state_changed_handler(tmpConnection, ^(nw_connection_state_t state, nw_error_t _Nullable error) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
MSLogVerbose("State changed: %d; error: %@", state, error);
strongSelf.connectionState = state;
if (state == nw_connection_state_ready) {
[strongSelf receiveMessageForConnection:tmpConnection];
}
});
nw_connection_set_queue(tmpConnection, AGGalileoBrowser.browserQueue);
nw_connection_start(tmpConnection);
Thanks for your help!
Arno
Networking
RSS for tagExplore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm attempting to create a service that:
Listens on iOS device A using NWListener
Broadcasts the NWService ( using NWListener(service:using:)) ) on Bonjour
Allows a separate device, iOS device B, to receive information about that service via an NWBrowser
Connect to that service using the information contained in NWBrowser.Result 's NWEndpoint
I've been able to successfully do this using a SwiftNIO service, in the following environments:
iOS device A and iOS device B are physical iOS devices on the same WiFi network. This works.
iOS device A and iOS device B are iOS simulators on the same machine. This works.
iOS device A is a physical device, and iOS device B is a simulator. iOS device A is not connected to a WiFi network, iOS device B is connected to a WiFi network. This works.
However, when iOS device A and iOS device B are physical devices that are not connected to a WiFi network, I encounter the following behavior:
The Bonjour service is correctly advertised, and iOS device A and iOS device B are able to observe the advertisement of the service.
In both cases, iOS device A and iOS device B, while able to resolve an NWEndpoint for the Bonjour service, are not able to connect to each other, and the connection attempt hangs.
My setup for the listener side of things looks roughly like:
let opts: NWParameters = .tcp
opts.includePeerToPeer = true
opts.allowLocalEndpointReuse = true
let service = NWListener.Service(name: "aux", type: BONJOUR_SERVICE_TYPE, domain: "")
try bootstrap.withNWListener(NWListener(service: service, using: opts)).wait() // bootstrap is an artifact of using SwiftNIO
Similarly, my setup on the discovery side of things looks like:
let params: NWParameters = .tcp
params.includePeerToPeer = true
let browser = NWBrowser(for: .bonjour(type: BONJOUR_SERVICE_TYPE, domain: BONJOUR_SERVICE_DOMAIN), using: params)
browser.browseResultsChangedHandler = { (searchResults, changed) in
// save the result to pass on its NWEndpoint later
}
and finally, where I have an NWEndpoint, I use SwiftNIO's NIOTSConnectionBootstrap.connect(endpoint:) to initialize a connection to my TCP service ( a web socket server ).
The fact that I am able to get P2P networking (presumably over an awdl interface?) between the simulator and the iOS device suggests to me that I haven't done anything obviously wrong in my setup. Similarly, the fact that it works over the same WiFi network and that, in P2P, I am able to at least observe the Bonjour advertisement, strikes me that I'm somewhere in the right neighborhood of getting this to work. I've also ensured that my Info.plist for the app has a NSLocalNetworkUsageDescription and NSBonjourServices for the Bonjour service type I'm browsing for.
I've even attempted to exercise the "Local Network Permission" dialog by using a hacky attempt that sends data to a local IP in order to trigger a permissions dialog, though the hack does not appear to actually force the dialog to appear.
Is there some trick or other piece of knowledge regarding allowing the use of P2P w/ Network.framework and TCP connections to services?
Hi,
For one our requirement sendProviderMessage is been used to send some event/message from app to system extension, In my requirement, responseHandler in system extension would get explicitly called approximately after 1 min due to some async download file task.
But observing some strange behavior that responseHandler is getting called implicitly after ~20-30 seconds even before the code hit the place where its called explicitly. And that is the only place I'm calling responseHandler.
Can somebody please help about this strange behavior, Is there any implicit timeout interval associated with the responseHandler.
Thanks & Regards,
Preethi
System extensions on iOS have very low limits on allowed memory. For instance the DNS proxy extensions seem to be limited to 15MB. When I try to monitor the extension with instruments it quickly runs out of memory, most likely due to the way instruments tracks memory usage.
I did find that there are two entitlements related to memory usage but it is unclear if these would work for extensions or only for applications.
What are the best techniques for debugging extensions that run out of memory?
Is there a way to temporarily increase the limit while debugging?
Hi, I have been working on the app that implements DNS Proxy Extension for a while now, and after a couple builds to TestFlight I noticed that I got a couple crashes that seem to be triggered by EXC_BREAKPOINT (SIGTRAP)
After some investigation, it was found that crashes are connected to CFNetwork framework. So, I decided to additionally look into memory issues, but I found the app has no obvious memory leaks, no memory regression (within recommended 25%, actual value is at 20% as of right now), but the app still uses 11mb of memory footprint and most of it (6.5 mb is Swift metadata).
At this point, not sure what's triggering those crashes, but I noticed that sometimes app will return message like this to the console (this example is for PostHog api that I use in the app):
Task <0ABDCF4A-9653-4583-9150-EC11D852CA9E>.<1> finished with error [18 446 744 073 709 550 613] Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={_kCFStreamErrorCodeKey=8, NSUnderlyingError=0x1072df0f0 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_kCFStreamErrorDomainKey=12, _kCFStreamErrorCodeKey=8, _NSURLErrorNWResolutionReportKey=Resolved 0 endpoints in 2ms using unknown from cache, _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: en0[802.11], ipv4, dns, uses wifi}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalUploadTask <0ABDCF4A-9653-4583-9150-EC11D852CA9E>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalUploadTask <0ABDCF4A-9653-4583-9150-EC11D852CA9E>.<1>"
), NSLocalizedDescription=A server with the specified hostname could not be found., NSErrorFailingURLStringKey=https://us.i.posthog.com/batch, NSErrorFailingURLKey=https://us.i.posthog.com/batch, _kCFStreamErrorDomainKey=12}
If DNS Proxy Provider uses custom DoH server for resolving packets, could the cache policy for URLSession be a reason?
I had a couple other ideas (HTTP3 failure, CFNetwork core issues like described here) but not sure if they are valid
Would be grateful if someone could give me a hint of what I should look at
Topic:
App & System Services
SubTopic:
Networking
Tags:
Extensions
Network
Network Extension
CFNetwork
I have tried filing a feedback, FB15509991, for help with this and that didn't go anywhere. Figured I would try the developer forums.
Overview
I am working on a matter device using the Matter SDK and the matter device basically consists of both a matter bridge and matter controller functionality.
The bridge part is currently a none-issue, however trying to have our device be an additional controller for the existing matter fabric.
The overall idea for our device as a matter controller is that it can be commissioned with Apple Home (via Matter BLE commissioning) and then view and control existing matter devices (over Wi-Fi network) on the Homekit matter fabric (convenient user experience), instead of our device having to form a matter fabric of its own and then having the user re-commission all their devices to add them our controller (difficult and possibly frustrating user experience), in order to have a consistent control experience between our device's display and Apple Home app.
The big problem
When we onboard our device via Apple Home app it does not have attribute write permission to other devices on the same fabric as we are seeing Unsupported Access (IM:0x0000057E) responses instead of expected attribute changes. Same for attempts to read valid endpoint/cluster/attributes.
The possible solution
Our operational device needs to be added to the access control list (ACL) with View and Operator permissions and then the ACL update pushed to all the fabric devices in order to give our device controller access to them.
The next problem
My question is what do we have to do in order for our device will be given control access permissions (View + Operator) in an ACL (access control list) update to other devices after our device has been commissioned?
Because the matter specification does not define a "Controller Cluster" that could be used to type a device as a matter controller to make it obvious that the device wishes to have controller permissions post commissioning. So that means its up to each fabric administrator implementer as to how to accomplish what I'm requesting to do.
I'm hoping somebody in the Apple team responsible for the Matter + HomeKit integration could give me some insight as to whether this is even possible at this time.
Test environment
The environment consists of:
iPhone running iOS 17.7
iPad running iPadOS 18.0.1
HomePod Mini with software version 18.0
Realtek WiFi module running Matter Fan+Light firmware (Matter SDK 1.3) for target/controlee
[our device] LCD display unit + Realtek WiFi module (Matter SDK 1.3) for controller.
I applied to Apple for authorization for the following page about 3 weeks ago, but have not received the results yet
https://developer.apple.com/documentation/networkextension/local_push_connectivity
Should I try to submit the application again with the same information or can I wait a little longer?
I would appreciate it if you could give me a little information about the same application or even another authority, such as the time it took to reply to that application.
Here is the actual page I applied for
https://developer.apple.com/contact/request/local-push-connectivity
I am developing an App using the Networking framework, which can be either a Socket Server or a Socket Client, such that 2 devices can communicate remotely.
I would like to include the Client's userUID when creating a NWConnection, such that when the SocketServer accepts the connection, it knows immediately which user is connected.
(Currently I achieve this by sending the UserUID in Welcome/Introduction messages, which seems an unnecessary overhead, and because I am using UDP, I also have to make sure these messages are acknowledged, before safely using the connection.)
Is there a way to add this custom data into the NWConnection?
I am developing an App using the Networking framework, which can be either a Socket Server or a Socket Client, such that 2 devices can communicate remotely. For the most part I have it working, except:
I am not sure of the best way to determine the IP Address for the Socket Server in order to allow the Client app to connect. I am currently using either of Cloud Functions, or lookup webpages (such as ipify.org) and even reading the IP addresses locally from within the device (this returns many, but not all of them connect successfully).
These options seem to work if the Socket Server app is connected to the internet with an IPv6 address, but I find that when the Socket Server app is connected with an IPv4 address, the Client app never successfully connects.
How should I:
a) force the Socket Server app to have/use an IPV6 address at all times?
or
b) allow the Client app to connect successfully via an IPv4 address?
And is there a simple way to know what IP Address the Socket Server is listening from?
Hi there, I have some thread related questions regards to network framework completion callbacks. In short, how should I process cross thread data in the completion callbacks?
Here are more details. I have a background serial dispatch queue (call it dispatch queue A) to sequentially process the nw_connection and any network io events. Meanwhile, user inputs are handled by serial dispatch queue ( dispatch queue B). How should I handle the cross thread user data in this case?
(I write some simplified sample code below)
struct {
int client_status;
char* message_to_sent;
}user_data;
nw_connection_t nw_connection;
dispatch_queue_t dispatch_queue_A
static void send_message(){
dispatch_data_t data = dispatch_data_create(message, len(message), dispath_event_loop->dispatch_queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
nw_connection_send(
nw_connection, data, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, false, ^(nw_error_t error) {
user_data.client_status = SENT;
mem_release(user_data.message_to_sent); });
});
}
static void setup_connection(){
dispatch_queue_A=
dispatch_queue_create("unique_id_a", DISPATCH_QUEUE_SERIAL);
nw_connection = nw_connection_create(endpoint, params);
nw_connection_set_state_changed_handler(){
if (state == nw_connection_state_ready) {
user_data.client_status = CONNECTED
}
// ... other operations ...
}
nw_connection_start(nw_connection);
nw_retain(nw_connection);
}
static void user_main(){
setup_connection()
user_data.client_status = INIT;
dispatch_queue_t dispatch_queue_B = dispatch_queue_create("unique_id_b", DISPATCH_QUEUE_SERIAL);
// write socket
dispatch_async(dispatch_queue_B, ^(){
if (user_data.client_status != CONNECTED ) return;
user_data.message_to_sent = malloc(XX,***)
// I would like to have all io events processed on dispatch queue A so that the io events would not interacted with the user events
dispatch_async_f(dispatch_queue_A, send_message);
// Disconnect block
dispatch_async(dispatch_queue_B, ^(){
dispatch_async_f(dispatch_queue_A, ^(){
nw_connection_cancel(nw_connection)
});
user_data.client_status = DISCONNECTING;
});
// clean up connection and so on...
}
To be more specific, my questions would be:
As I was using serial dispatch queue, I didn't protect the user_data here. However, which thread would the send_completion_handler get called? Would it be a data race condition where the Disconnect block and send_completion_handler both access user_data?
If I protect the user_data with lock, it might block the thread. How does the dispatch queue make sure it would NOT put a related execution block onto the "blocked thread"?
Didn't get any response last time so reposting my query. I know procID is of no use in IOS but just for curiosity I am logging proc ID in control Filter and data Filter.
I was trying to log the flow description using control filter and data filter. But when I am trying to log the proc ID in control filter, it is always 0, but in data filter, it logs some value. Same goes with the eproc ID. I want to use the flow description data in some other target so I will be sending the data using sockets and I cannot share data from data filter due to its restrictions and control filter isn't providing the proc ID. What should I do?
I have the following snippet of code for receiving incoming data on a NWConnection:
self.Connection.receive(minimumIncompleteLength: 1, maximumLength: self.MAX_INTAKE) {
(data, context, isComplete, error) in
if let err = error {
// receive <error> returned non-nil
self.Connection.cancel()
return // exit completion handler
}
...
}
This generally works and rarely receives an error. But seemingly at random, will return 89. When this happens I've been sending a .cancel before returning from the completion handler.
It will work great for tens of thousands of connections, then suddenly return 89 error codes.
My question is: Should I be canceling the connection here or simply let NWFramwwork do as it will? Canceling the connection seems to throw my NGINX reverse proxy into fits, from which it never recovers without a restart.
In short what is the best practice for handling errors when receiving bytes in NWFramework?
Topic:
App & System Services
SubTopic:
Networking
We are developing a tunnel based on transparent proxy system extension. We want to be able to decide whether to handle certain TCP flows based on FQDN.
So, is there a way to peek into TCPFlow data like we can in ContentFilter which will allow use to parse and check for SNI or Host-header?
As far as I understand, we can read data from flows until we have returned a decision from handleNewFlow.
Hello everyone,
I have a question regarding the behavior of network listeners in my application. Here's the scenario I'm seeing:
When I open a .v6 listener, it accepts both IPv4 and IPv6 traffic. However, when I run the netstat -tln command, the socket is shown as udp6.
When I open a NWListener with the IP version set to .any, I receive both IPv4 and IPv6 traffic on the listener. In this case, running netstat -tln shows a udp46 socket.
My understanding is that if I create a socket with .v6, it should only accept IPv6 connections, not both IPv4 and IPv6. However, the .v6 listener appears to be accepting both types of traffic, which is causing some confusion.
Additionally, I am seeking to understand the difference between a udp6 socket and a udp46 socket, and also the difference between sockets created using .v6 and .any. What exactly does udp46 represent, and how is it different from udp6 in terms of accepting traffic?
Is this expected behavior, or is there something I am missing in how the listeners are set up?
Looking forward to hearing your insights!
Hi,
We have been granted the com.apple.managed.vpn.shared entitlement and are able to use it for builds/TestFlight builds. We can access the cert in a mobile config. and everything works fine.
However when we try to archive a build and distribute for App Store Connect it fails if the entitlement file contains this entry. If we take it out the upload succeeds but the app can't load the cert from the keychain.
The Distribution profile has the entry:
keychain-access-groups: [TEAM ID].*, com.apple.managed.vpn.shared
Is there an extra step for App Store Connect builds?
Thanks,
Dave
Hey,I want to get nearby Wi-Fi network's SSID into the app using network extension framework.Right now I can get scan list by visiting the setting---&gt;Wifi Screen but I want to get those Scan Result into the app without visiting the setting wifi screen.If anyone idea about it please let me know
Feedback Ticket: FB13812251
Problem Statement: We are currently facing internet connectivity issue with our VPN application where we try to disconnect the VPN from the Packet Tunnel Network Extension using - (void)cancelTunnelWithError:(nullable NSError *)error. Which API to use to disconnect the VPN from Packet Tunnel as VPN app is not running such that device retains its internet connectivity as soon as VPN disconnects.
Configuration: We have configured PacketTunnelProvider with the following settings:
(NETunnelProviderManager *)tunnelProvider.protocolConfiguration.includeAllNetworks = YES;
(NETunnelProviderManager *)tunnelProvider.protocolConfiguration.excludeLocalNetworks = NO;
(NETunnelProviderManager *)tunnelProvider.protocolConfiguration.enforceRoutes = NO;
These settings are applied from the VPN app and allow us to successfully establish a VPN connection, with all traffic being routed through the tunnel as expected.We are setting above properties to address local net attack.
Issue we are facing:
However, we encounter a problem when we attempt to disconnect the VPN from. When we call the following method from PacketTunnel network extension:
(void)cancelTunnelWithError:(nullable NSError *)error
Upon calling this method, the VPN disconnects as expected, but the device loses all internet connectivity and is unable to access any resources. This is not the desired behavior.
Observation : Interestingly, when we call the following method from the app side. The VPN disconnects and the device retains its internet connectivity.
[enabledConfig.connection stopVPNTunnel];
We would like to achieve the same behavior when disconnecting the VPN from the Network Extension. So we are looking for an API that could be called from NE without causing any internet connectivity issue.
Any guidance on how to resolve this issue would be greatly appreciated.
I've implemented a custom VPN for macOS (system extension, Packet Tunnel Provider, Developer ID). My tunneling logic uses BSD sockets.
My VPN is configured with on-demand and should always connect when there's traffic:
targetManager?.isOnDemandEnabled = true
targetManager?.onDemandRules = [NEOnDemandRuleConnect()]
I have encountered some issues when the device enters sleep (or waking up from sleep). I've tried two scenarios.
Scenario 1:
protocolConfiguration?.disconnectOnSleep = true
With this flag set, the OS will disconnect the VPN just before entering to sleep. However, there were cases when the OS disconnected the VPN but immediately restarted it - probably because of how I defined the on-demand rules. This resulted in the VPN disconnection, then trying to reconnect, and then the Mac entered sleep.
When the Mac woke up, the VPN didn't work well.
Is there a way to avoid waking up, just before the Mac enters sleep?
Scenario 2:
protocolConfiguration?.disconnectOnSleep = false
Disconnect on sleep is unset, and I've implemented the sleep/wake functions at the provider.
With this configuration, the OS won't disconnect the VPN, so even in sleep, the extension should stay 'alive,' so it won't have the problem from (1).
But in this case, I had other problems:
On sleep, I'm disconnecting the tunnel. But sometimes, on wake(), all my network calls fail. Are the interfaces still down? How can I detect this case from the system extension?
Is it possible that the OS would call sleep and then quickly call wake?
Is it possible that after sleep, the OS would call the startTunnelWithOptions() function?
Is it possible to restart the extension from a clean state right from the wake() function?
Hello there,
I'm trying to consume graph.microsoft.com API by using URLSession. But I'm getting Error 400 from the server every time when I send a request with URLSession, but not if I use Postman for the same request. And when I examined the requests with Fiddler, I noticed that URLSession use HTTP/2, and Postman use HTTP/1.1. According to the Microsoft's documentation graph.microsoft.com supports only HTTP/1.1. As I understand URLSession decides to which version of HTTP to use during ALPN and will use HTTP/2 only if the server support it. My question is it possible the graph server to advertise itself as its support HTTP/2 and how to determinate this? Or maybe, which is more likely URLSession has bug that make it to do the wrong assumption about the HTTP/2. And most important there is there a mechanism to make URLSession to use a certain version of HTTP?
Thank you in advance,
Emil
I'm working with an app that was developed with the CocoaAsyncSocket library, which uses <sys/socket.h> style socket programming. We have a customer where certain features are not working if iOS "adapts" to v3 IGMP but seems to work if v2 IGMP is used.
I can't say I understand all the low level aspects and am trying to inform myself. I've been searching online to find out about socket and IGMP but don't see anything coming up.
Is there a way to force iOS to use IGMP v2 for broadcast? Our company also makes a hardware communicator and their code forces v2 and we've been asked to see if we can do the same.
Thanks for any leads on where to go to inform myself. more or code snippets on how to do this.