I am learning about layer 3 VPN implementations for MacOS, and am slowly making my way through docs and tutorials. I noticed that part of creating an instance of NETunnelProviderManager on the app side of the project requires the specification of protocolConfiguration via an instance of NETunnelProviderProtocol. One of the arguments for this class is serverAdress, which to my understanding, tells the OS where to route traffic towards at the end of the day.
My question: many VPNs these days allow the option to specify the location for which you want your traffic to be routed through. I imagine this would necessitate changing this serverAddress field in the backend. However, setting this option (on a commercially available VPN) doesn't typically prompt the OS notification that you get when initially installing a VPN configuration for the first time. How is this functionality achieved? I could see one possible solution being that most VPN providers route through a main service beforehand (so the first IP in the chain never has to change), though I could see this being problematic for a number of other reasons.
Assuming you have a valid NETunnelProviderManager object called manager, is this valid?
self.manager?.protocolConfiguration?.serverAddress = "somewhereElse"
Even if it compiles, will the traffic be properly re-routed?
My understanding of the flow right now is that in order to "lock in" a new configuration, or modify it, you need to call manager.saveToPreferences, which triggers the OS notification I mentioned earlier.
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
in the local testing, if the binary data length is small like 300 bytes, then it can send binary data success, but if the data length is more that 3000 bytes, then it will failed and print the error like in the screen capture
Topic:
App & System Services
SubTopic:
Networking
hello
I am testing the use of network extension. When we use dnsproxy to proxy DNS requests,
we will send you a message that the udp pcbcount of your system continues to increase.
For example
for ((i=1; i<=99999; i++));do
echo "Attempt $i:"
dig google.com
done
when the dig command is used continuously,
the dig command will show the following errors when pcbcount reaches a certain number.
isc_socket_bind: address not available
Can you help us determine what the problem might be? thank you
I'm looking at implementing an iOS app that has includes a Content Filter Provider to block access to certain domains when accessed on the device.
This uses NEFilterManager, NEFilterDataProvider and NEFilterControlProvider to handle configuration and manage the network flows and block as necessary.
My question is can you deploy this in an iOS 18+ app on the App Store to devices which are unmanaged, unsupervised and don't use Screen Time APIs?
Although not 100% clear, this technote seems to say it is not possible:
https://developer.apple.com/documentation/Technotes/tn3134-network-extension-provider-deployment
Testing this on a Developer device and build works successfully without any MDM profiles installed.
A similar approach using the same APIs also works on macOS once user permissions have been given.
If it can't work on unsupervised, unmanaged iOS devices, is possible for the user to first manually install a MDM profile which includes the required 'Content Filter' details and then have it work?
If not, how would you filter iOS network traffic on an unmanaged, unsupervised device?
Is it necessary to use a VPN or DNS approach instead (which may be a lot less privacy compliant)?
I have an accessory which uses both Bluetooth and WiFi to communicate with the app. I am trying to migrate to Accessory Setup Kit.
However, the API expects both the bluetooth identifiers and WIFI SSID or SSID prefix in the ASDiscoveryDescriptor. The problem is we only have the WIFI SSID after BLE pairing.
Our current flow looks like this:
Pair via BLE
Connect via BLE
Send a BLE command to request WIFI settings (SSID and password) (Each device has a different SSID and password)
Connect to WI-FI hotspot by calling NEHotspotConfigurationManager applyConfiguration with the retrieved credentials.
Is there a way to set the Wi-Fi SSID of an ASAccessory object after the initial setup?
To use Accessory Setup Kit we would need something like this:
Call Accessory Setup Kit with bluetooth identifiers in the descriptor, finish the setup and get ASAccessory object.
Connect via BLE
Send a BLE command to request WIFI settings (SSID and password)
Set the SSID of the ASAccessory to the retrieved value.
Connect to WI-FI hotspot by calling `NEHotspotConfigurationManager joinAccessoryHotspot.
Thanks!
Topic:
App & System Services
SubTopic:
Networking
I have currently created an app which contains an upload button which when clicked upload health data using HealthKit to an AWS S3 bucket.
Now I want to implement an automatic file upload mechanism which would mean that the app is installed and opened just once - and then the upload must happen on a schedule (once daily) from the background without ever having to open the app again.
I've tried frameworks like NSURLSession and BackgroundTasks but nothing seems to work. Is this use case even possible to implement? Does iOS allow this?
The file is just a few KBs in size.
For reference, here is the Background Tasks code:
import UIKit
import BackgroundTasks
import HealthKit
class AppDelegate: NSObject, UIApplicationDelegate {
let backgroundTaskIdentifier = "com.yourapp.healthdata.upload"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Register the background task
BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in
self.handleHealthDataUpload(task: task as! BGAppRefreshTask)
}
// Schedule the first upload task
scheduleDailyUpload()
return true
}
// Schedule the background task for daily execution
func scheduleDailyUpload() {
print("[AppDelegate] Scheduling daily background task.")
let request = BGAppRefreshTaskRequest(identifier: backgroundTaskIdentifier)
request.earliestBeginDate = Date(timeIntervalSinceNow: 24*60*60)
do {
try BGTaskScheduler.shared.submit(request)
print("[AppDelegate] Daily background task scheduled.")
} catch {
print("[AppDelegate] Could not schedule daily background task: \(error.localizedDescription)")
}
}
// Handle the background task when it's triggered by the system
func handleHealthDataUpload(task: BGAppRefreshTask) {
print("[AppDelegate] Background task triggered.")
// Call your upload function with completion handler
HealthStoreManager.shared.fetchAndUploadHealthData { success in
if success {
print("[AppDelegate] Upload completed successfully.")
task.setTaskCompleted(success: true)
// Schedule the next day's upload after a successful upload
self.scheduleDailyUpload()
} else {
print("[AppDelegate] Upload failed.")
task.setTaskCompleted(success: false)
}
}
// Handle task expiration (e.g., if upload takes too long)
task.expirationHandler = {
print("[AppDelegate] Background task expired.")
task.setTaskCompleted(success: false)
}
}
}
Here's a simple program that spoofs an ARP reply for a given IP address. If I spin up two terminal sessions on the same machine.
Run this code in one window
% ./spoof en0 192.168.1.7
Listening on en0 for ARP requests to 192.168.1.7
Spoofing MAC: 00:0c:87:47:50:27
And in the second window cause the OS to issue an ARP_REQ
% ping 192.168.1.7
You will see the program respond to the ARP request. (Wireshark will see the ARP_REQ and ARP_REPLY packets) however my arp table isn't updated with the MAC for the IP address. There is no firewall active.
% arp -a|grep 192.168.1.7
(192.168.1.7) at (incomplete) on en0 ifscope [ethernet]
This is running on a MacBook pro M3 (OSX 15.4).
HOWEVER, on a MacBook pro M4 (OSX 15.2) is does Work !!!!!
Can anyone explain why its not working?
spoof.txt
Based on threads from past years, it is mentioned that a NEFilterDataProvider supports IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP and IPPROTO_IGMP.
[Q] What about IPPROTO_RAW? Is this something that would have been added recently?
Hi, I want to develop an enterprise VPN app with a custom protocol. I'm following the documentation but I have no clue how to start, is there any guide I can follow?
I am integrating per-app VPN functionality into an iOS app using Wireguard. Chrome is designated as a per-app application for this purpose. However, upon opening Chrome, the VPN icon appears in the notification bar, but there is no internet connection within the Chrome browser.
I have verified this behavior with OpenVPN, and it works correctly. While I am familiar with the MDM payload and how to implement per-app VPN, my primary concern is understanding why per-app VPN functionality is not functioning as expected with WireGuard.
An observation we made in the server-side logs is the message: "wireguard: wg0: Packet has incorrect size from peer 1"
PLATFORM AND VERSION: iOS
Development environment: Xcode 15.3, macOS 14.7.1 (23H222)
Run-time configuration: iOS 18.3.1
DESCRIPTION OF PROBLEM:
Our app uses NEHotspotConfigurationManager with joinOnce set to false to connect to an IoT device's Wi-Fi hotspot. Later, we programmatically disconnect from this hotspot. We are wondering if, after this programmatic disconnection, there is a possibility that the iPhone will automatically reconnect to the hotspot (even when our app is not running). Does it matter if the hotspot's SSID is hidden or not? This concern arises because the iPhone is already familiar with the hotspot's network configuration. Our testing indicates that this does not happen, but we want to be certain. This is a behavior we do NOT want to occur.
We set joinOnce to false because we experience connectivity issues with the IoT device when joinOnce is true (there are several discussions in forums regarding issues with setting it to true).
Thank you.
Thanks.
We have developed a DNS filter extension that works for most applications, but it does not receive all DNS queries.
In particular, if we have our extension installed and enabled, we see Safari browsing cause local DNS servers to be used instead of going through our extension.
What is the logic for how DNS servers vs. extensions are chosen to resolve DNS queries?
On my macOS 15.x device, frequently encountering the error:
Error Domain=com.apple.wifi.apple80211API.error Code=-528342014 "tmpErr"
when connecting to an EAP WiFi network using CWWiFiClient. Restarting the device temporarily resolves the issue, but it reoccurs after some time.
What could be causing this, and how can it be resolved programmatically?
I have an iPhone app which relies heavily on TCP/IP communication in the local network. Therefore, the application starts a server socket and accepts incoming connections. This worked flawlessly for a long time and we had no problems with this.
Problem
In the last days however, we observed that for some iPhones with the server role other devices cannot connect to the server of our app. The server does not accept incoming connections on the devices IP address and the client times out.
Environment
Both iPhones (the server and the client) are in the same network with 192.168.1.0 address range and 255.255.255.0 subnet mask. The server has the IP 192.168.1.11 and the client has 192.168.1.22. This is a normal home WiFi network with no special firewall rules. Both devices have mobile data disabled and the "access local network" permission is granted. The server socket is bound to all interfaces (0.0.0.0).
More technical symptoms
When the server iPhone is in this faulty state, it seems like it somehow has two ip addresses:
192.168.2.123 and 192.168.1.11
The WiFi preferences show the (correct) .1.11 ip address. The Apps however see the (wrong) .2.123 ip address. I cannot explain where the other ip address comes from and why the device thinks it has this ip address.
I've collected interface diagnosis information on a faulty iPhone and it listed the following interfaces and IPs:
en0 -> 192.168.2.123
lo0 -> 127.0.0.1
pdp_ip0 (cellular) -> 192.0.0.2
pdp_ip1 to pdp_ip6 (cellular) -> -/-
ipsec0 to ipsec6 (vpn) -> -/-
llw0 (vpn) -> -/-
awdl0 -> -/-
anpi0 -> -/-
ap1 -> -/-
XHC0 -> -/-
en1 and en2 (wired) -> -/-
utun0 to utun2 (vpn) -> -/-
The correct ip of the device is not listed anywhere in this list.
A reboot helped to temporarily fix this problem. One user reported the same issue again a few hours later after a reboot. Switching off WiFi and reconnecting does not solve the problem.
This issue occurred on several iPhones with the following specs:
iOS Version 18.1.1, 18.3.1
iPhone 13 Pro, iPhone 13 Pro Max, iPhone 15 Pro
The problem must be on the server side as the client can successfully connect to any other device in the same network.
Question(s)
Where does this second IP come from and why does the server not accept connections to either ip even though it is bound to 0.0.0.0?
Are there any iOS system settings which could lead to this problem? (privacy setting, vpn, ...)
What could be done to permanently fix this issue?
Getting cannot parse response on all downalod tasks. Example output
"BackgroundDownloadTask <E277D3D6-2FF0-4574-A617-1612ED779151>.<1>",
"LocalDownloadTask <E277D3D6-2FF0-4574-A617-1612ED779151>.<1>"
), NSLocalizedDescription=cannot parse response, _kCFStreamErrorDomainKey=4, NSErrorFailingURLStringKey=https://traffic.megaphone.fm/ESP7536701051.mp3?updated=1740573440, NSErrorFailingURLKey=https://traffic.megaphone.fm/ESP7536701051.mp3?updated=1740573440}
Can't seem to find a workaround that i can push for app to work with 18.4 beta. Can't believe that beta went to the public.
Apple multi-peer with 12 devices is unstable. Dear All,
Has anyone tried Apple multi-peer with 12 devices connected? We are building an application relying on multi-peer where 12 Ipads will be updating data and each device needs to share data between. Can anyone tell me if we can use multi-peer framework for connecting 12 devices in the multi-peer network? We are facing stability problems in the connection when we connect 12 devices in the network.
HI,
I am currently prototyping an app that compares transport protocol performances using a peer to peer connection. I have already setup TCP and UDP connections and am sending data between the clients, it works like I want it to.
Next I was trying to setup a connection using QUIC, but the NWConnection.State stays in the preparing state and I couldn't find a way to get more information from the framework or the instances about why it was not fully connecting. After searching the internet and stumbling across the forum I noticed that the missing encryption might be the issue, so I created a local root certificate*. Then I used the SecPKCS12Import function to read/extract the SecIdentity instance of the p12 file (cert + private key) stored in my bundle** and set it as a local identity with the sec_protocol_options_set_local_identity function***.
//function that creates/returns different NWParameteres
//...
let quicOptions = NWProtocolQUIC.Options()
quicOptions.alpn = ["test"]
if let identityPath = Bundle.main.path(forResource: "QUICConnect", ofType: "p12"),
let identityData = try? Data(contentsOf: URL(fileURLWithPath: identityPath)) {
if let identity = loadIdentityFromPKCS12(p12Path: identityPath, password: "insecure") { //****
sec_protocol_options_set_local_identity(quicOptions.securityProtocolOptions, sec_identity_create(identity)!)
}
}
let parameters = NWParameters(quic: quicOptions)
parameters.includePeerToPeer = true
return parameter
The documentation comments had me thinking that setting a local identity could be enough, since it consists of the private key for the "server" and the cert for the "client".
Set the local identity to be used for this protocol instance.
Unfortunately at this stage the QUIC Connection is still stuck in preparing state and since I don't know how to extract more information from the networking connection instances/framework, I am stuck.
I have seen the following other functions in Quinns answer and am confident that I could somehow figure it out with some more time put into it, but not really understanding why or how I could do it better in the future. So I am also wondering how I could have found info about this more efficiently and tackled this more strategically without needing to browse through so many forums.
sec_protocol_options_set_verify_block
sec_protocol_options_set_challenge_block
I would really appreciate any help, many thanks.
BR Matthias!
TLDR:
I want to establish a peer to peer QUIC Connection but the state is stuck in preparing. Secondary question is how I could approach a similar topic more efficiently next time, instead of browsing many forums.
* I had to create it with the openssl CLI since the keychain app created a cert, that when using the openssl CLI to get the info would throw an error unless used with the -legacy flag. The root cert, created form the keychain app also wasn't able to be imported by the SecPKCS12Import function. No clue why but it worked with a cert created from the openssl CLI. There's a chance that I messed up something else here, but these were my experiences. Info: Since QUIC is limited to TLS v1.3 I can't use PSK, afaik. Therefore the TicTacToe doesn't help me anymore.
** I know this is highly insecure, I am just using it for prototyping.
*** Forum users Info: One needs to use the sec_identity_create function to convert the SecIdentity instance to the expected parameter type.
****
func loadIdentityFromPKCS12(p12Path: String, password: String) -> SecIdentity? {
guard let p12Data = try? Data(contentsOf: URL(fileURLWithPath: p12Path)) else {
print("didnt find p12 file at path")
return nil
}
let options: NSDictionary = [kSecImportExportPassphrase as String: password, kSecImportToMemoryOnly as String: kCFBooleanTrue!]
var items: CFArray?
let status = SecPKCS12Import(p12Data as CFData, options, &items)
if status == 0, let dict = (items as? [[String: Any]])?.first {
if let identity = dict[kSecImportItemIdentity as String] {
return identity as! SecIdentity
} else {
return nil
}
} else {
return nil
}
}
PS: For TCP and UDP I am using bonjour to discover the peer and connect to the advertised ports. AFAIK I can't just use _testproto._quic to advertise a QUIC service like with tcp and udp. Therefore I am using the local domain name (it's just for prototyping and always the same device) and a hard coded port number to create the peer connection. When using a wrong name the DNS threw an error telling it could not find a peer, so the lookup itself is working I guess. The lookup should come from the cache since I already looked up when connecting to the same peer via Bonjour.
//Server
//....
listener = try NWListener(
using: transportProtocol.parameters,
on: Config.quicPort
)
//...
listener.newConnectionHandler = { [weak self] connection in
self?.connection?.cancel()
self?.connection = nil
self?.connection = C(connection) //here C is a generic that conforms to a custom connection interface, nothing to worry about :)
self?.connectionStatus.value = "Connection established"
}
listener.stateUpdateHandler = { [weak self] state in
self?.connectionStatus.value = "\(state)"
}
listener.start(queue: .global())
//Client
//...
nwConnection = NWConnection(host: "iPad.local.", port: Config.quicPort, using: transportProtocol.parameters)
//...
Hi, I've noticed a weird behavior happening on Sequoia with DF bit:
On machine where SIP is disabled, when I do /sbin/ping -D -s 1400 8.8.8.8 I do see the DF bit in wireshark
On machine where SIP is enabled, when I do /sbin/ping -D -s 1400 8.8.8.8 I do not see the DF bit in wireshark
The -D flag should set the DF bit but for some reason it doesn’t if the SIP is enabled.
Perhaps there was any change in permission/entitlements mechanism in Sequoia that can explain it ? I'm using the built-in ping command so maybe it should be signed with more entitlements ?
I am trying to commission an ESP32-H2 Matter device using the chip-tool. It's running the Light Switch sample. I can commissioning it using the iOS Home App, so I know the code on it's working okay.
I would like to understand more about the Fabric process, so I'd like to use the Home Pod powered Thread network rather than setting up an instance of Open Thread Border Router.
I have created a simple iOS app and can fetch the activeOperationalDataSet from the Preferred network using
func obtainPreferredNetworkCredentials() async -> (Void) {
let client = THClient()
let bIsPreferredAvailable = await client.isPreferredAvailable()
if bIsPreferredAvailable == true
{
var credential: THCredentials?
do {
credential = try await client.preferredCredentials()
if let dataset = credential?.activeOperationalDataSet {
print(dataset.hexDescription)
}
} catch {
print("Failed to get the credentials")
}
}
}
The hexDescription comes from this extension
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
I am decoding the Data and displaying it as a hex string. It looks something like this:
0e080000000000000000000300001935060004001fffc002089f651677026f48070708fd9f65167702000ee90914b5d1097de9bb0818dc94690c0402a0f7f8
However, when I attempt to commission the device, it fails during ThreadSetup. Googling the issue says most likely the Operational Dataset is wrong in some way.
Before I spend too much time on this, I want to make sure I'm doing the right thing in terms of getting the Operational Dataset to use with the chip-tool.
Any help is appreciated!
For years our iOS apps have experienced a networking problem, which blocks them connecting to our servers via their API endpoint domains.
How can we recover after the scenario described below?
Using 3rd party error logging solutions, which have different endpoint domains, we can record the error:
NSUnderlyingError": Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816, _NSURLErrorNWPathKey=satisfied (Path is satisfied), viable, interface: pdp_ip0[lte], ipv4, dns, expensive, uses cell}, "_NSURLErrorFailingURLSessionTaskErrorKey": LocalDataTask <DEDBFA4D-810D-4438-A6A0-95E3B9668B9E>.<308>, "_kCFStreamErrorDomainKey": 3, "_NSURLErrorRelatedURLSessionTaskErrorKey": <__NSSingleObjectArrayI 0x301f82e60>(
LocalDataTask <DEDBFA4D-810D-4438-A6A0-95E3B9668B9E>.<308>
)
"NSLocalizedDescription": An SSL error has occurred and a secure connection to the server cannot be made., "NSLocalizedRecoverySuggestion": Would you like to connect to the server anyway?
-9816 is the "server closed session with no notification" error based on comments in CoreFoundation source files. Subsequent API endpoint calls to the same domain return the same error.
The SSL error occurs most prevalently after a server outage. However, despite our best efforts, we have been unable to replicate triggering the problem for development purposes via experiments with our server.
When the error occurs the users report that:
Fully closing (i.e. not just sending to background) and reopening the app does NOT clear connectivity to our server being blocked.
Problem seems more prevalent when using mobile/cell data.
Switching from mobile/cell data to WIFI resolves the connection problem and then switching back to mobile/cell data shows the problem again. So the underlying problem is not cleared.
All other apps on the same device and mobile/cell data or WIFI connection, like Safari, have no problems connecting to the Internet.
Deleting and reinstalling, or updating (when an update is available) resolves the problem.
Or after waiting a few days the problem seems to resolve itself.
The last two point above suggest that something is persisted/cached in the app preventing it from connecting properly with subsequent network attempts.
Notes:
We have one shared instance of the URLSession in the app for its networking because we are aware of the perils of multiple URLSession instances.
We recently added conditions to call the URLSession await reset() method when detecting the SLL errors before repeating the request. It is debatable whether this reduces the problem as we still see logged cases with the subsequent requests hitting the same -9816 error.
URLSession configuration:
let config = URLSessionConfiguration.default
config.timeoutIntervalForResource = 22
config.timeoutIntervalForRequest = 20
config.requestCachePolicy = .reloadIgnoringLocalCacheData
config.urlCache = nil