Hello,
Since the release of iOS 26.0, we are seeing DNS traffic being blocked from within our NEPacketTunnelExtension on some devices. We have not isolated exact reproduction steps, but DNS resolves successfully for a period of time after enabling "iCloud Private Relay" (varying from 1-day to 2-weeks), until it then fails as MDNSResponder then returns:
mDNSResponder [Q37046] DetermineUnicastQuerySuppression: Query suppressed for <mask.hash: 'REDACTED'> Addr (blocked by policy)
DNS resolution continues to fail for all domains with the above until the device is rebooted.
The Packet Tunnel intentionally does not have a DNS server set and this occurs for traffic from the Extension yet off-tunnel, which needs resolution from the system DNS server (and this configuration works perfectly for a period of time before being "blocked by policy").
The following do not resolve the issue once DNS queries are being "blocked by policy" on affected devices: disconnecting then reconnecting the vpn; toggling airplane mode for 10+ seconds; switching connection between WiFi & cellular data; disabling iCloud Private Relay.
We have currently only seen this on unmanaged devices running iOS 26.0 or 26.1 beta and with iCloud Private Relay enabled. We did not see this issue on iOS 16,17 nor 18. We also have not yet seen this when iCloud Private Relay is disabled nor on iOS 26.0.1, however we cannot confirm whether they too are also affected.
Is there a known a bug with iOS 26.0 & 26.1 Beta 1 that could cause this? How can we prevent DNS requests from NEPacketTunnelExtension being sporadically "blocked by policy" until the device is rebooted?
Many thanks in advance.
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
While updating our test devices to iOS 26, we noticed that the connection between devices are flaky. Often when connecting to a Peer from a device running iOS 26 we can observe the invite coming through and when accepting said invite, both ends going to .connecting state and a while later going back to .notConnected within the peer(_ peerID: MCPeerID, didChange state: MCSessionState) function. This happens regularly and retrying the invitation process several times usually resolves it. Do anyone have any information or guidance on how to resolve this issue?
Hello, we are developing hardware that needs to connect to an iPhone via Wi-Fi to send requests to a server. On Android, we have managed to create a programmatic local hotspot within the app to facilitate connection and improve the user experience.
On iOS, however, Personal Hotspot must be manually enabled from the system settings, and the user must manually enter the SSID and password, which significantly degrades the UX.
My questions are:
Is there a workaround, unofficial method, or private API to generate a local hotspot from an app on iOS, similar to what can be done on Android?
Is there an alternative within the MFi program or through specific frameworks to facilitate a quick and automatic connection between the hardware and the iPhone without relying on the manual Personal Hotspot?
Are there any best practices for improving the local Wi-Fi connection experience between an accessory and an iPhone in the absence of hotspot controls?
I would appreciate any guidance, experience, or resources that would help me better understand the feasible options in iOS for scenarios where fast and direct communication between hardware and mobile devices via Wi-Fi is required.
When I try to implement the new Background Task options in the same way as they show in the WWDC video (on watchOS) likes this:
let config = URLSessionConfiguration.background(withIdentifier: "SESSION_ID")
config.sessionSendsLaunchEvents = true
let session = URLSession(configuration: config)
let response = await withTaskCancellationHandler {
try? await session.data(for: request)
} onCancel: {
let task = session.downloadTask(with: request))
task.resume()
}
I'm receiving the following error:
Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
Did I forget something?
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'm developing an application using the accessory setup kit (BLE) on iOS 18+. An important aspect of the connection process is being able to find and choose the correct device.
I noticed on iOS 18.2 that I was able to both scroll through the discovered accessories as well as view the advertised name. However, after upgrading to 18.7.2, only a single device is viewable and the advertised name is no longer available. Is there a trigger for this feature that I need to enable or was this "multiple discovery" feature removed? If so, why?
I'm trying to implement support for grpc http/2 streams using NSURLSession. Almost everything works fine, data streaming is flowing from the server and from the client and responses are coming through my NSURLSessionTaskDelegate. I'm getting the responses and streamed data through the appropriate handlers (didReceiveData, didReceiveResponse).
However, I cannot seem to find an API to access the trailers expected by grpc. Specifically, the expected trailer "grpc-status: 0" is in the response, but after the data. Is there no way to gain access to trailers in the NSURLSession Framework?
The environment:
macOS 12.0 ~ 15.6
A NetworkExtension NEFilterDataProvider configured with filterSockets = YES, filterPackets = NO, and it doesn't actually block any network connection.
QQMusic (download: https://y.qq.com/n/ryqq/download_detail/mac?ADTAG=YQQ) is constantly playing.
Any of the following operations can reproduce the issue:
Kill the NetworkExtension process and then restarted by the system.
Disable the NEFilterDataProvider, and then enable it.
When this problem occurs, there are two different phenomena on the NetworkExtension process:
It is zombie, or is in high CPU state (100%).
When the NetworkExtension process is zombie, obviously, the new network connections will enter it, and they can't be disposed by the old zombie process, so the network is disconnected.
Spindump-qqmusic-ne-zombie
When the NetworkExtension process is in high CPU state, its thread DispatchQueue "NEFilterExtensionProviderContext queue" is blocked in the kernel when calling close.
Spindump-qqmusic-ne-cpuhigh
In most cases, the network will recover after stopping QQ Music, that is the suspended zombie NetworkExtension process will exist or the cpu of it return to normal.
To reproduce the issue in a simple environment, I have tried many ways to simulate the network behavior of QQMusic, but all failed.
It seems that this issue is caused by UDP traffic of QQMusic, because everything is ok after blocking the UDP connections of QQMusic (the music is still playing at this time) in the NEFilterDataProvider.
I have some confusion around the usage of DeviceDiscoveryUI. The documentation suggests that it is available only on TVOS. But with the recent announcement of WifiAware, it has been used in iOS devices as well. Within DeviceDiscoveryUI, the DevicePicker or the DevicePairingView documentation seems to be available with iOS. Is this just a documentation mistake?
Followup - Can I use DeviceDiscoveryUI's DevicePicker/ DevicePairingView to discover devices through Bonjour and then establish a connection through Network framework?
1) Blocked page UX
When a URL is blocked, the browser typically shows a generic error like “"Safari cannot open the page because it couldn’t load any data,” with no indication that the page was blocked by a policy.
Is there any plan to add an API that allows developers to present a custom “blocked” page or remediation action, similar to NEFilterControlProvider’s remediationMap?
Even a minimal hook (custom HTML, deep link, or support URL) would make the experience clearer for users.
2) Cross‑app link‑opening behavior
With a block rule in place, direct navigation in Safari is blocked as expected. However, tapping the same URL in a messaging app (e.g., WhatsApp) opens Safari - and the page loads, not blocked.
Repro steps:
Configure a URL Filter extension that blocks https://example.com.
Case A: Open a browser and type the URL in the address bar → blocked (expected).
Case B: Tap the same URL in WhatsApp (or another messenger) → a browser opens and the page loads (unexpected).
iOS version - 26.0
I have been playing around with the new URL Filtering API. I have successfully installed and configured the sample code, Installed the example app to my iPhone, and am also running the PIR server locally on my Mac.
In my input.txtpb file, I simply have 2 endpoints:
rows: [{
keyword: "instagram.com",
value: "1"
},
{
keyword: "youtube.com/shorts",
value: "1"
}]
Neither of these are blocked when I attempt to load them from either a browser, or their dedicated apps.
Are there any debugging tips I should know about?
Additionally, I have also noticed a few times I have left the filter running on my phone, after leaving my LAN (where the PIR server is running), suddenly throughout the day I'm having random, completely unrelated endpoints blocked on my phone. I thought this API was never supposed to produce false positives (without calling back to the PIR server for confirmation).
I encountered an undesired DNS behaviour when using L2TP/ipsec VPN.
I have DHCP configured Wi-Fi connection, which send dns servers and search domain (192.168.0.10, lan)
VPN sends its own DNS server (10.0.0.2), search domain (intranet) is added manually in VPN DNS config settings.
I expect, when VPN is connected, to be still able to resolve local names, i.e some-host.lan. However, they become unresolvable.
quick check shows that the remote dns server is used to resolve local names.
shilishper@mac ~ % host -v some-host.lan
Trying "some-host.lan"
Host some-host.lan not found: 3(NXDOMAIN)
Received 106 bytes from 10.0.0.2#53 in 16 ms
Received 106 bytes from 10.0.0.2#53 in 16 ms
Actually, all dns queries are going to the remote server. I would expect that only queries for the configured domain (intranet) should go to that server. I played with the service order, but it didn't change anything.
Is anything can be done about this, programmatically?
PS new to macOS, but have extensive linux knowledge
Hi,
We’re seeing our build system (Gradle) get stuck in sendto system calls while trying to communicate with other processes via the local interface over UDP. To the end user it appears that the build is stuck or they will receive an error “Timeout waiting to lock XXX. It is currently in use by another Gradle instance”. But when the process is sampled/profiled, we can see one of the threads is stuck in a sendto system call. The only way to resolve the issue is to kill -s KILL <pid> the stuck Gradle process.
A part of the JVM level stack trace:
"jar transforms Thread 12" #90 prio=5 os_prio=31 cpu=0.85ms elapsed=1257.67s tid=0x000000012e6cd400 nid=0x10f03 runnable [0x0000000332f0d000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.DatagramChannelImpl.send0(java.base@17.0.10/Native Method)
at sun.nio.ch.DatagramChannelImpl.sendFromNativeBuffer(java.base@17.0.10/DatagramChannelImpl.java:901)
at sun.nio.ch.DatagramChannelImpl.send(java.base@17.0.10/DatagramChannelImpl.java:863)
at sun.nio.ch.DatagramChannelImpl.send(java.base@17.0.10/DatagramChannelImpl.java:821)
at sun.nio.ch.DatagramChannelImpl.blockingSend(java.base@17.0.10/DatagramChannelImpl.java:853)
at sun.nio.ch.DatagramSocketAdaptor.send(java.base@17.0.10/DatagramSocketAdaptor.java:218)
at java.net.DatagramSocket.send(java.base@17.0.10/DatagramSocket.java:664)
at org.gradle.cache.internal.locklistener.FileLockCommunicator.pingOwner(FileLockCommunicator.java:61)
at org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler.maybePingOwner(DefaultFileLockContentionHandler.java:203)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock$1.run(DefaultFileLockManager.java:380)
at org.gradle.internal.io.ExponentialBackoff.retryUntil(ExponentialBackoff.java:72)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.lockStateRegion(DefaultFileLockManager.java:362)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.lock(DefaultFileLockManager.java:293)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.<init>(DefaultFileLockManager.java:164)
at org.gradle.cache.internal.DefaultFileLockManager.lock(DefaultFileLockManager.java:110)
at org.gradle.cache.internal.LockOnDemandCrossProcessCacheAccess.incrementLockCount(LockOnDemandCrossProcessCacheAccess.java:106)
at org.gradle.cache.internal.LockOnDemandCrossProcessCacheAccess.acquireFileLock(LockOnDemandCrossProcessCacheAccess.java:168)
at org.gradle.cache.internal.CrossProcessSynchronizingCache.put(CrossProcessSynchronizingCache.java:57)
at org.gradle.api.internal.changedetection.state.DefaultFileAccessTimeJournal.setLastAccessTime(DefaultFileAccessTimeJournal.java:85)
at org.gradle.internal.file.impl.SingleDepthFileAccessTracker.markAccessed(SingleDepthFileAccessTracker.java:51)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.markAccessed(DefaultCachedClasspathTransformer.java:209)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.transformFile(DefaultCachedClasspathTransformer.java:194)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.lambda$cachedFile$6(DefaultCachedClasspathTransformer.java:186)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer$$Lambda$368/0x0000007001393a78.call(Unknown Source)
at org.gradle.internal.UncheckedException.unchecked(UncheckedException.java:74)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.lambda$transformAll$8(DefaultCachedClasspathTransformer.java:233)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer$$Lambda$372/0x0000007001398470.call(Unknown Source)
at java.util.concurrent.FutureTask.run(java.base@17.0.10/FutureTask.java:264)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@17.0.10/ThreadPoolExecutor.java:1136)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@17.0.10/ThreadPoolExecutor.java:635)
at java.lang.Thread.run(java.base@17.0.10/Thread.java:840)
A part of the process sample:
2097 Thread_3879661: Java: jar transforms Thread 12
+ 2097 thread_start (in libsystem_pthread.dylib) + 8 [0x18c42eb80]
...removed for brevity...
+ 2097 Java_sun_nio_ch_DatagramChannelImpl_send0 (in libnio.dylib) + 84 [0x102ef371c]
+ 2097 __sendto (in libsystem_kernel.dylib) + 8 [0x18c3f612c]
We have observed the following system logs around the time the issue manifests:
2025-08-26 22:03:23.280255+0100 0x3b2c00 Default 0x0 0 0 kernel: cfil_hash_entry_log:6088 <CFIL: Error: sosend_reinject() failed>: [4628 java] <UDP(17) in so 9e934ceda1c13379 50826943645358435 50826943645358435 ag>
2025-08-26 22:03:23.280267+0100 0x3b2c00 Default 0x0 0 0 kernel: cfil_service_inject_queue:4472 CFIL: sosend() failed 22
The issue seems to be rooted in the built-in Application Firewall, as disabling it “fixes” the issue. It doesn’t seem to matter that the process is on the “allow” list.
We’re using Gradle 7.6.4, 8.0.2 and 8.14.1 in various repositories, so the version doesn’t seem to matter, neither does which repo we use.
The most reliable way to reproduce is to run two Gradle builds at the same time or very quickly after each other.
We would really appreciate a fix for this as it really negatively affects the developer experience. I've raised FB19916240 for this.
Many thanks,
We currently supporting proxy app with Tunnel.appEx and PacketTunnelProvider.
Some users report about constant error "The VPN session failed because an internal error occurred." on VPN start (which fails rapidly).
This error occur mostly after user updated app with active VPN.
Rebooting device solves the problem and it doesnt come again, but it is still very frustrating.
I can provide any required info about app setup to solve this issue if you need. Thanks
I'm building an app that helps manage my own wifi access points. Now, all my wifis emit SSIDs starting with the same prefix. Is it possible for me to list down all the SSIDs near me that start with that prefix, so that determine which of my wifis are near me? (Swift)
Can NEHotspotHelper or NEHotspotConfigurationManager help in this regard?
iPhone 12 pro with iOS 26.0 (23A5276f)
App: https://developer.apple.com/documentation/wifiaware/building-peer-to-peer-apps
We aim to use Wi-Fi Aware to establish file transfer between Android and Apple devices.
Apple will act as the Publisher, and Android will act as the Subscriber.
According to the pairing process outlined in the Wi-Fi Aware protocol (Figure 49 in the Wi-Fi Aware 4.0 specification), the three PASN Authentication frames have been successfully exchanged. Subsequently, Android sends the encrypted Follow-up PMF to Apple, but the Apple log shows: Failed to parse event. Please refer to the attached complete log.
We request Apple to provide a solution.
apple Log-20250808a.txt
This is a major issue. After my iPhone 12 Pro was upgraded to iOS 26 beta 6, Apple's official Wi-Fi Aware Sample APP crashed immediately and couldn't be opened. It just force closes.
Has any developer encountered this problem?
how can I prevent handshake when certificate is user installed
for example if user is using Proxyman or Charles proxy and they install their own certificates
now system is trusting those certificates
I wanna prevent that, and exclude those certificates that are installed by user,
and accept the handshake if CA certificate is in a real valid certificate defined in OS
I know this can be done in android by setting something like
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
For a past few days, I have been exploring control Filter and data filter. I am unable to understand how control moves from various functions of data filter to control Filter.
One thing that I am unable to figure out is that when I pass verdict as .allow in dataFilter's handleNewFlow and mark .shouldReport as true, I get inBytes and outbytes in the flow report of handle() in controlFilter. But when I pass verdict as needRules and wait till the handle is called in controlFilter when the report.event == .flowClosed, I don't get inBytes and outBytes.
I am unable to understand this complete flow of network extension from the apple documentation.
Can someone provide me with some flow chart or some pictorial representation or detailed explanation of network extension for iOS?
Also is there some way to imitate the ..statisticsReportFrequency for iOS as it is not available for iOS?
I can develop a PacketTunnelProvider on Mac with xcode.
I work with my self codesign.
But when I sign it with Developer ID after read https://developer.apple.com/forums/thread/737894 , it still fail when I turn on the vpn .