General:
Forums subtopic: App & System Services > Networking
TN3151 Choosing the right networking API
Networking Overview document — Despite the fact that this is in the archive, this is still really useful.
TLS for App Developers forums post
Choosing a Network Debugging Tool documentation
WWDC 2019 Session 712 Advances in Networking, Part 1 — This explains the concept of constrained networking, which is Apple’s preferred solution to questions like How do I check whether I’m on Wi-Fi?
TN3135 Low-level networking on watchOS
TN3179 Understanding local network privacy
Adapt to changing network conditions tech talk
Understanding Also-Ran Connections forums post
Extra-ordinary Networking forums post
Foundation networking:
Forums tags: Foundation, CFNetwork
URL Loading System documentation — NSURLSession, or URLSession in Swift, is the recommended API for HTTP[S] on Apple platforms.
Moving to Fewer, Larger Transfers forums post
Testing Background Session Code forums post
Network framework:
Forums tag: Network
Network framework documentation — Network framework is the recommended API for TCP, UDP, and QUIC on Apple platforms.
Building a custom peer-to-peer protocol sample code (aka TicTacToe)
Implementing netcat with Network Framework sample code (aka nwcat)
Configuring a Wi-Fi accessory to join a network sample code
Moving from Multipeer Connectivity to Network Framework forums post
NWEndpoint History and Advice forums post
Network Extension (including Wi-Fi on iOS):
See Network Extension Resources
Wi-Fi Fundamentals
TN3111 iOS Wi-Fi API overview
Wi-Fi Aware framework documentation
Wi-Fi on macOS:
Forums tag: Core WLAN
Core WLAN framework documentation
Wi-Fi Fundamentals
Secure networking:
Forums tags: Security
Apple Platform Security support document
Preventing Insecure Network Connections documentation — This is all about App Transport Security (ATS).
WWDC 2017 Session 701 Your Apps and Evolving Network Security Standards [1] — This is generally interesting, but the section starting at 17:40 is, AFAIK, the best information from Apple about how certificate revocation works on modern systems.
Available trusted root certificates for Apple operating systems support article
Requirements for trusted certificates in iOS 13 and macOS 10.15 support article
About upcoming limits on trusted certificates support article
Apple’s Certificate Transparency policy support article
What’s new for enterprise in iOS 18 support article — This discusses new key usage requirements.
Technote 2232 HTTPS Server Trust Evaluation
Technote 2326 Creating Certificates for TLS Testing
QA1948 HTTPS and Test Servers
Miscellaneous:
More network-related forums tags: 5G, QUIC, Bonjour
On FTP forums post
Using the Multicast Networking Additional Capability forums post
Investigating Network Latency Problems forums post
WirelessInsights framework documentation
iOS Network Signal Strength forums post
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] This video is no longer available from Apple, but the URL should help you locate other sources of this info.
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
Transport Layer Security (TLS) is the most important security protocol on the Internet today. Most notably, TLS puts the S into HTTPS, adding security to the otherwise insecure HTTP protocol.
IMPORTANT TLS is the successor to the Secure Sockets Layer (SSL) protocol. SSL is no longer considered secure and it’s now rarely used in practice, although many folks still say SSL when they mean TLS.
TLS is a complex protocol. Much of that complexity is hidden from app developers but there are places where it’s important to understand specific details of the protocol in order to meet your requirements. This post explains the fundamentals of TLS, concentrating on the issues that most often confuse app developers.
Note The focus of this is TLS-PKI, where PKI stands for public key infrastructure. This is the standard TLS as deployed on the wider Internet. There’s another flavour of TLS, TLS-PSK, where PSK stands for pre-shared key. This has a variety of uses, but an Apple platforms we most commonly see it with local traffic, for example, to talk to a Wi-Fi based accessory. For more on how to use TLS, both TLS-PKI and TLS-PSK, in a local context, see TLS For Accessory Developers.
Server Certificates
For standard TLS to work the server must have a digital identity, that is, the combination of a certificate and the private key matching the public key embedded in that certificate. TLS Crypto Magic™ ensures that:
The client gets a copy of the server’s certificate.
The client knows that the server holds the private key matching the public key in that certificate.
In a typical TLS handshake the server passes the client a list of certificates, where item 0 is the server’s certificate (the leaf certificate), item N is (optionally) the certificate of the certificate authority that ultimately issued that certificate (the root certificate), and items 1 through N-1 are any intermediate certificates required to build a cryptographic chain of trust from 0 to N.
Note The cryptographic chain of trust is established by means of digital signatures. Certificate X in the chain is issued by certificate X+1. The owner of certificate X+1 uses their private key to digitally sign certificate X. The client verifies this signature using the public key embedded in certificate X+1. Eventually this chain terminates in a trusted anchor, that is, a certificate that the client trusts by default. Typically this anchor is a self-signed root certificate from a certificate authority.
Note Item N is optional for reasons I’ll explain below. Also, the list of intermediate certificates may be empty (in the case where the root certificate directly issued the leaf certificate) but that’s uncommon for servers in the real world.
Once the client gets the server’s certificate, it evaluates trust on that certificate to confirm that it’s talking to the right server. There are three levels of trust evaluation here:
Basic X.509 trust evaluation checks that there’s a cryptographic chain of trust from the leaf through the intermediates to a trusted root certificate. The client has a set of trusted root certificates built in (these are from well-known certificate authorities, or CAs), and a site admin can add more via a configuration profile.
This step also checks that none of the certificates have expired, and various other more technical criteria (like the Basic Constraints extension).
Note This explains why the server does not have to include the root certificate in the list of certificates it passes to the client; the client has to have the root certificate installed if trust evaluation is to succeed.
In addition, TLS trust evaluation (per RFC 2818) checks that the DNS name that you connected to matches the DNS name in the certificate. Specifically, the DNS name must be listed in the Subject Alternative Name extension.
Note The Subject Alternative Name extension can also contain IP addresses, although that’s a much less well-trodden path. Also, historically it was common to accept DNS names in the Common Name element of the Subject but that is no longer the case on Apple platforms.
App Transport Security (ATS) adds its own security checks.
Basic X.509 and TLS trust evaluation are done for all TLS connections. ATS is only done on TLS connections made by URLSession and things layered on top URLSession (like WKWebView). In many situations you can override trust evaluation; for details, see Technote 2232 HTTPS Server Trust Evaluation). Such overrides can either tighten or loosen security. For example:
You might tighten security by checking that the server certificate was issued by a specific CA. That way, if someone manages to convince a poorly-managed CA to issue them a certificate for your server, you can detect that and fail.
You might loosen security by adding your own CA’s root certificate as a trusted anchor.
IMPORTANT If you rely on loosened security you have to disable ATS. If you leave ATS enabled, it requires that the default server trust evaluation succeeds regardless of any customisations you do.
Mutual TLS
The previous section discusses server trust evaluation, which is required for all standard TLS connections. That process describes how the client decides whether to trust the server. Mutual TLS (mTLS) is the opposite of that, that is, it’s the process by which the server decides whether to trust the client.
Note mTLS is commonly called client certificate authentication. I avoid that term because of the ongoing industry-wide confusion between certificates and digital identities. While it’s true that, in mTLS, the server authenticates the client certificate, to set this up on the client you need a digital identity, not a certificate.
mTLS authentication is optional. The server must request a certificate from the client and the client may choose to supply one or not (although if the server requests a certificate and the client doesn’t supply one it’s likely that the server will then fail the connection).
At the TLS protocol level this works much like it does with the server certificate. For the client to provide this certificate it must apply a digital identity, known as the client identity, to the connection. TLS Crypto Magic™ assures the server that, if it gets a certificate from the client, the client holds the private key associated with that certificate.
Where things diverge is in trust evaluation. Trust evaluation of the client certificate is done on the server, and the server uses its own rules to decided whether to trust a specific client certificate. For example:
Some servers do basic X.509 trust evaluation and then check that the chain of trust leads to one specific root certificate; that is, a client is trusted if it holds a digital identity whose certificate was issued by a specific CA.
Some servers just check the certificate against a list of known trusted client certificates.
When the client sends its certificate to the server it actually sends a list of certificates, much as I’ve described above for the server’s certificates. In many cases the client only needs to send item 0, that is, its leaf certificate. That’s because:
The server already has the intermediate certificates required to build a chain of trust from that leaf to its root.
There’s no point sending the root, as I discussed above in the context of server trust evaluation.
However, there are no hard and fast rules here; the server does its client trust evaluation using its own internal logic, and it’s possible that this logic might require the client to present intermediates, or indeed present the root certificate even though it’s typically redundant. If you have problems with this, you’ll have to ask the folks running the server to explain its requirements.
Note If you need to send additional certificates to the server, pass them to the certificates parameter of the method you use to create your URLCredential (typically init(identity:certificates:persistence:)).
One thing that bears repeating is that trust evaluation of the client certificate is done on the server, not the client. The client doesn’t care whether the client certificate is trusted or not. Rather, it simply passes that certificate the server and it’s up to the server to make that decision.
When a server requests a certificate from the client, it may supply a list of acceptable certificate authorities [1]. Safari uses this to filter the list of client identities it presents to the user. If you are building an HTTPS server and find that Safari doesn’t show the expected client identity, make sure you have this configured correctly. If you’re building an iOS app and want to implement a filter like Safari’s, get this list using:
The distinguishedNames property, if you’re using URLSession
The sec_protocol_metadata_access_distinguished_names routine, if you’re using Network framework
[1] See the certificate_authorities field in Section 7.4.4 of RFC 5246, and equivalent features in other TLS versions.
Self-Signed Certificates
Self-signed certificates are an ongoing source of problems with TLS. There’s only one unequivocally correct place to use a self-signed certificate: the trusted anchor provided by a certificate authority.
One place where a self-signed certificate might make sense is in a local environment, that is, securing a connection between peers without any centralised infrastructure. However, depending on the specific circumstances there may be a better option. TLS For Accessory Developers discusses this topic in detail.
Finally, it’s common for folks to use self-signed certificates for testing. I’m not a fan of that approach. Rather, I recommend the approach described in QA1948 HTTPS and Test Servers. For advice on how to set that up using just your Mac, see TN2326 Creating Certificates for TLS Testing.
TLS Standards
RFC 6101 The Secure Sockets Layer (SSL) Protocol Version 3.0 (historic)
RFC 2246 The TLS Protocol Version 1.0
RFC 4346 The Transport Layer Security (TLS) Protocol Version 1.1
RFC 5246 The Transport Layer Security (TLS) Protocol Version 1.2
RFC 8446 The Transport Layer Security (TLS) Protocol Version 1.3
RFC 4347 Datagram Transport Layer Security
RFC 6347 Datagram Transport Layer Security Version 1.2
RFC 9147 The Datagram Transport Layer Security (DTLS) Protocol Version 1.3
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Revision History:
2025-11-21 Clearly defined the terms TLS-PKI and TLS-PSK.
2024-03-19 Adopted the term mutual TLS in preference to client certificate authentication throughout, because the latter feeds into the ongoing certificate versus digital identity confusion. Defined the term client identity. Added the Self-Signed Certificates section. Made other minor editorial changes.
2023-02-28 Added an explanation mTLS acceptable certificate authorities.
2022-12-02 Added links to the DTLS RFCs.
2022-08-24 Added links to the TLS RFCs. Made other minor editorial changes.
2022-06-03 Added a link to TLS For Accessory Developers.
2021-02-26 Fixed the formatting. Clarified that ATS only applies to URLSession. Minor editorial changes.
2020-04-17 Updated the discussion of Subject Alternative Name to account for changes in the 2019 OS releases. Minor editorial updates.
2018-10-29 Minor editorial updates.
2016-11-11 First posted.
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
Please consider this trivial C code which deals with BSD sockets. This will illustrate an issue with sendto() which seems to be impacted by the recent "Local Network" restrictions on 15.3.1 macos.
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "sys/socket.h"
#include <string.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <net/if.h>
// prints out the sockaddr_in6
void print_addr(const char *msg_prefix, struct sockaddr_in6 sa6) {
char addr_text[INET6_ADDRSTRLEN] = {0};
printf("%s%s:%d, addr family=%u\n",
msg_prefix,
inet_ntop(AF_INET6, &sa6.sin6_addr, (char *) &addr_text, INET6_ADDRSTRLEN),
sa6.sin6_port,
sa6.sin6_family);
}
// creates a datagram socket
int create_dgram_socket() {
const int fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (fd < 0) {
perror("Socket creation failed");
return -1;
}
return fd;
}
// returns a string representing the current local time
char *current_time() {
time_t seconds_since_epoch;
time(&seconds_since_epoch);
char *res = ctime(&seconds_since_epoch);
const size_t len = strlen(res);
// strip off the newline character that's at the end of the ctime() output
res[len - 1] = '\0';
return res;
}
// Creates a datagram socket and then sends a messages (through sendto()) to a valid
// multicast address. This it does two times, to the exact same destination address from
// the exact same socket.
//
// Between the first and the second attempt to sendto(), there is
// a sleep of 1 second.
//
// The first time, the sendto() succeeds and claims to have sent the expected number of bytes.
// However system logs (generated through "log collect") seem to indicate that the message isn't
// actually sent (there's a "cfil_service_inject_queue:4466 CFIL: sosend() failed 65" in the logs).
//
// The second time the sendto() returns a EHOSTUNREACH ("No route to host") error.
//
// If the sleep between these two sendto() attempts is removed then both the attempts "succeed".
// However, the system logs still suggest that the message isn't actually sent.
int main() {
printf("current process id:%ld parent process id: %ld\n", (long) getpid(), (long) getppid());
// valid multicast address as specified in
// https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
const char *ip6_addr_str = "ff01::1";
struct in6_addr ip6_addr;
int rv = inet_pton(AF_INET6, ip6_addr_str, &ip6_addr);
if (rv != 1) {
fprintf(stderr, "failed to parse ipv6 addr %s\n", ip6_addr_str);
exit(EXIT_FAILURE);
}
// create a AF_INET6 SOCK_DGRAM socket
const int sock_fd = create_dgram_socket();
if (sock_fd < 0) {
exit(EXIT_FAILURE);
}
printf("created a socket, descriptor=%d\n", sock_fd);
const int dest_port = 12345; // arbitrary port
struct sockaddr_in6 dest_sock_addr;
memset((char *) &dest_sock_addr, 0, sizeof(struct sockaddr_in6));
dest_sock_addr.sin6_addr = ip6_addr; // the target multicast address
dest_sock_addr.sin6_port = htons(dest_port);
dest_sock_addr.sin6_family = AF_INET6;
print_addr("test will attempt to sendto() to destination host:port -> ", dest_sock_addr);
const char *msg = "hello";
const size_t msg_len = strlen(msg) + 1;
for (int i = 1; i <= 2; i++) {
if (i != 1) {
// if not the first attempt, then sleep a while before attempting to sendto() again
int num_sleep_seconds = 1;
printf("sleeping for %d second(s) before calling sendto()\n", num_sleep_seconds);
sleep(num_sleep_seconds);
}
printf("%s attempt %d to sendto() %lu bytes\n", current_time(), i, msg_len);
const size_t num_sent = sendto(sock_fd, msg, msg_len, 0, (struct sockaddr *) &dest_sock_addr,
sizeof(dest_sock_addr));
if (num_sent == -1) {
fprintf(stderr, "%s ", current_time());
perror("sendto() failed");
close(sock_fd);
exit(EXIT_FAILURE);
}
printf("%s attempt %d of sendto() succeeded, sent %lu bytes\n", current_time(), i, num_sent);
}
return 0;
}
What this program does is, it uses the sendto() system call to send a message over a datagram socket to a (valid) multicast address. It does this twice, from the same socket to the same target address. There is a sleep() of 1 second between these two sendto() attempts.
Copy that code into noroutetohost.c and compile:
clang noroutetohost.c
Then run:
./a.out
This generates the following output:
current process id:58597 parent process id: 21614
created a socket, descriptor=3
test will attempt to sendto() to destination host:port ->ff01::1:14640, addr family=30
Fri Mar 14 20:34:09 2025 attempt 1 to sendto() 6 bytes
Fri Mar 14 20:34:09 2025 attempt 1 of sendto() succeeded, sent 6 bytes
sleeping for 1 second(s) before calling sendto()
Fri Mar 14 20:34:10 2025 attempt 2 to sendto() 6 bytes
Fri Mar 14 20:34:10 2025 sendto() failed: No route to host
Notice how the first call to sendto() "succeeds", even the return value (that represents the number of bytes sent) matches the number of bytes that were supposed to be sent. Then notice how the second attempt fails with a EHOSTUNREACH ("No route to host") error. Looking through the system logs, it appears that the first attempt itself has failed:
2025-03-14 20:34:09.474797 default kernel cfil_hash_entry_log:6082 <CFIL: Error: sosend_reinject() failed>: [58597 a.out] <UDP(17) out so 891be95f3a70c605 22558774573152560 22558774573152560 age 0> lport 0 fport 12345 laddr :: faddr ff01::1 hash 1003930
2025-03-14 20:34:09.474806 default kernel cfil_service_inject_queue:4466 CFIL: sosend() failed 65
(notice the time on that log messages, they match the first attempt from the program's output log)
So even though the first attempt failed, it never got reported back to the application. Then after sleeping for (an arbitrary amount of) 1 second, the second call fails with the EHOSTUNREACH. The system logs don't show any error (at least not the one similar to that previous one) for the second call.
If I remove that sleep() between those two attempts, then both the sendto() calls "succeed" (and return the expected value for the number of bytes sent). However, the system logs show that the first call (and very likely even the second) has failed with the exact same log message from the kernel like before.
If I'm not wrong then this appears to be some kind of a bug in the "local network" restrictions. Should this be reported? I can share the captured logs but I would prefer to do it privately for this one.
Another interesting thing in all this is that there's absolutely no notification to the end user (I ran this program from the Terminal) about any of the "Local Network" restrictions.
General:
Forums subtopic: App & System Services > Networking
DevForums tag: Network Extension
Network Extension framework documentation
Routing your VPN network traffic article
Filtering traffic by URL sample code
Filtering Network Traffic sample code
TN3120 Expected use cases for Network Extension packet tunnel providers technote
TN3134 Network Extension provider deployment technote
TN3165 Packet Filter is not API technote
Network Extension and VPN Glossary forums post
Debugging a Network Extension Provider forums post
Exporting a Developer ID Network Extension forums post
Network Extension vs ad hoc techniques on macOS forums post
Network Extension Provider Packaging forums post
NWEndpoint History and Advice forums post
Extra-ordinary Networking forums post
Wi-Fi management:
Wi-Fi Fundamentals forums post
TN3111 iOS Wi-Fi API overview technote
How to modernize your captive network developer news post
iOS Network Signal Strength forums post
See also Networking Resources.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
IMPORTANT The resume rate limiter is now covered by the official documentation. See Use background sessions efficiently within Downloading files in the background. So, the following is here purely for historical perspective.
NSURLSession’s background session support on iOS includes a resume rate limiter. This limiter exists to prevent apps from abusing the background session support in order to run continuously in the background. It works as follows:
nsurlsessiond (the daemon that does all the background session work) maintains a delay value for your app.
It doubles that delay every time it resumes (or relaunches) your app.
It resets that delay to 0 when the user brings your app to the front.
It also resets the delay to 0 if the delay period elapses without it having resumed your app.
When your app creates a new task while it is in the background, the task does not start until that delay has expired.
To understand the impact of this, consider what happens when you download 10 resources. If you pass them to the background session all at once, you see something like this:
Your app creates tasks 1 through 10 in the background session.
nsurlsessiond starts working on the first few tasks.
As tasks complete, nsurlsessiond starts working on subsequent ones.
Eventually all the tasks complete and nsurlsessiond resumes your app.
Now consider what happens if you only schedule one task at a time:
Your app creates task 1.
nsurlsessiond starts working on it.
When it completes, nsurlsessiond resumes your app.
Your app creates task 2.
nsurlsessiond delays the start of task 2 a little bit.
nsurlsessiond starts working on task 2.
When it completes, nsurlsessiond resumes your app.
Your app creates task 3.
nsurlsessiond delays the start of task 3 by double the previous amount.
nsurlsessiond starts working on task 3.
When it completes, nsurlsessiond resumes your app.
Steps 8 through 11 repeat, and each time the delay doubles. Eventually the delay gets so large that it looks like your app has stopped making progress.
If you have a lot of tasks to run then you can mitigate this problem by starting tasks in batches. That is, rather than start just one task in step 1, you would start 100. This only helps up to a point. If you have thousands of tasks to run, you will eventually start seeing serious delays. In that case it’s much better to change your design to use fewer, larger transfers.
Note All of the above applies to iOS 8 and later. Things worked differently in iOS 7. There’s a post on DevForums that explains the older approach.
Finally, keep in mind that there may be other reasons for your task not starting. Specifically, if the task is flagged as discretionary (because you set the discretionary flag when creating the task’s session or because the task was started while your app was in the background), the task may be delayed for other reasons (low power, lack of Wi-Fi, and so on).
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
(r. 22323366)
I want to know the reason for last Wi-Fi disconnection. It would be helpful to that I can get the reason-code received from the access point.
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?
The Wi‑Fi Alliance’s Wi‑Fi Aware data communication uses IPv6.
However, in Chapter 53 “Wi‑Fi Aware” of the Accessory Design Guidelines for Apple Devices, Release R26, it is stated that “The Neighbor Discovery Protocol (NDP) for IPv6 address resolution is not supported.”
This has caused confusion among developers: Does Apple’s Wi‑Fi Aware data communication actually use IPv6?
What is the impact of “The Neighbor Discovery Protocol (NDP) for IPv6 address resolution is not supported” in Apple’s implementation?
I developed a Content Filter using the Network Extension, and when deployed to a batch of hosts (50 +), the installation worked for most of them, but there were six exceptions: five of them were macOS 10.15 and one of them was macOS 12.5.
The phenomenon of these 6 hosts is: in the System Settings->Network, two content filters with the same name appear. When one of the content filters with the same name is clicked, shows "Please use 'X Agent Extension' to control this content filter configuration" ('X Agent Extension' is the program I developed, this content filter can be deleted by clicking the minus sign in the lower left corner). Click on another content filter with the same name, shows 'Please use 'null' to control this content filter configuration', (but this content filter can't be removed by clicking the minus sign in the bottom left corner).
These systems are clean, use CLI 'systemextensionsctl list', and have only one systemextension in the output (this systemextension is my content filter). Online reference "https://forums.macrumors.com/threads/how-to-delete-custom-dns-profile-from-network-preference.2293322/" this paper, by closing the SIP, and delete file '/Library/Preferences/com.apple.networkextension.plist', then restart the system can remove the abnormal content filters with the same name. After restarting the system and reinstalling my content filter, the two content filters with the same name disappear (only the Content Filter I reinstalled) and the exception scenario cannot be repeated.
I would like to know, why do I have two content filters with the same name, how can I avoid this phenomenon, is there a way to remove the wrong content filter without closing SIP.
I would like to test running some Thread Networking code on my MacOS machine:
import ThreadNetwork
let client = THClient()
let bIsPreferredAvailable = await client.isPreferredAvailable()
but I get some errors when trying to create an instance of the THClient class:
Client: -[THClient connectToXPCService]_block_invoke - CTCS XPC Client is interrupted.
Client: -[THClient getConnectionEntitlementValidity]_block_invoke - clientProxyWithErrorHandler Error: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.ThreadNetwork.xpc" UserInfo={NSDebugDescription=connection to service named com.apple.ThreadNetwork.xpc}
Client: -[THClient init] - XPC Client Init Failed
Invalidating XPC connection.
Client: -[THClient getConnectionEntitlementValidity]_block_invoke - clientProxyWithErrorHandler Error: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.ThreadNetwork.xpc" UserInfo={NSDebugDescription=connection to service named com.apple.ThreadNetwork.xpc}
How can I get the code to run?
In order to create a Message Filter Extension it is necessary to set up Shared Web Credentials.
I'd like to form an understanding of what role SWC plays when the OS is making request to the associated network service (when the extension has called deferQueryRequestToNetwork()) and how this differs from when an app directly uses Shared Web Credentials itself.
When an app is making direct use of SWC, it makes a request to obtain the user's credentials from the web site.
However in the case of a Message Filter Extension, there aren't any individual user credentials, so what is happening behind the scenes when the OS makes a server request on behalf of a Message Filtering Extension?
A more general question - the documentation for Shared Web Credentials says "Associated domains establish a secure association between domains and your app.".
Thank you
Topic:
App & System Services
SubTopic:
Networking
Tags:
iOS
SMS and Call Reporting
Authentication Services
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?
I’m developing a iOS VPN app, and I need to execute a task in the main app even when it’s in the background or killed state. I know the Network Extension continues running during those times. Is there a way for the extension to immediately notify the app or trigger a task on the app side?
I am currently developing a custom-protocol VPN application for iOS using PacketTunnelProvider. I have also integrated an HTTP proxy service, which is launched via a dylib.
The overall flow is as follows:
App -> VPN TUN -> Local HTTP Proxy -> External Network
I have a question:
I am capturing all traffic, and normally, requests sent out by the HTTP proxy are also captured again by the VPN. However, when I send requests using createUdpSession in my code, they are not being captured by the virtual interface (TUN).
What could be the reason for this?
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
let tunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "192.168.18.0")
tunnelNetworkSettings.mtu=1400
let ipv4Settings = NEIPv4Settings(addresses: ["192.169.10.10"], subnetMasks: ["255.255.255.0"])
ipv4Settings.includedRoutes=[NEIPv4Route.default()]
ipv4Settings.excludedRoutes = [NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0"),
NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
NEIPv4Route(destinationAddress:"127.0.0.0", subnetMask: "255.0.0.0"),
]
tunnelNetworkSettings.ipv4Settings = ipv4Settings
// Configure proxy settings
let proxySettings = NEProxySettings()
proxySettings.httpEnabled = true
proxySettings.httpServer = NEProxyServer(address: "127.0.0.1", port: 7890)
proxySettings.httpsEnabled = true
proxySettings.httpsServer = NEProxyServer(address: "127.0.0.1", port: 7890)
proxySettings.excludeSimpleHostnames = true
proxySettings.exceptionList=["localhost","127.0.0.1"]
tunnelNetworkSettings.proxySettings = proxySettings
setTunnelNetworkSettings(tunnelNetworkSettings) { [weak self] error in
if error != nil {
completionHandler(error)
return
}
completionHandler(nil)
let stack = TUNInterface(packetFlow: self!.packetFlow)
RawScoketFactory.TunnelProvider=self
stack.register(stack: UDPDirectStack())
stack.register(stack: TCPDirectStack())
stack.start()
}
}
NWUdpSession.swift
//
// NWUDPSocket.swift
// supervpn
//
// Created by TobbyQuinn on 2025/2/3.
//
import Foundation
import NetworkExtension
import CocoaLumberjack
public protocol NWUDPSocketDelegate: AnyObject{
func didReceive(data:Data,from:NWUDPSocket)
func didCancel(socket:NWUDPSocket)
}
public class NWUDPSocket:NSObject{
private let session:NWUDPSession
private let timeout:Int
private var pendingWriteData: [Data] = []
private var writing = false
private let queue:DispatchQueue=QueueFactory.getQueue()
public weak var delegate:NWUDPSocketDelegate?
public init?(host:String,port:UInt16,timeout:Int=Opt.UDPSocketActiveTimeout){
guard let udpSession = RawScoketFactory.TunnelProvider?.createUDPSession(to: NWHostEndpoint(hostname: host, port: "\(port)"), from: nil) else{
return nil
}
session = udpSession
self.timeout=timeout
super.init()
session.addObserver(self, forKeyPath: #keyPath(NWUDPSession.state),options: [.new], context: nil)
session.setReadHandler({ dataArray, error in
self.queueCall{
guard error == nil, let dataArray = dataArray else {
print("Error when reading from remote server or connection reset")
return
}
for data in dataArray{
self.delegate?.didReceive(data: data, from: self)
}
}
}, maxDatagrams: 32)
}
/**
Send data to remote.
- parameter data: The data to send.
*/
public func write(data: Data) {
pendingWriteData.append(data)
checkWrite()
}
public func disconnect() {
session.cancel()
}
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard keyPath == "state" else {
return
}
switch session.state {
case .cancelled:
queueCall {
self.delegate?.didCancel(socket: self)
}
case .ready:
checkWrite()
default:
break
}
}
private func checkWrite() {
guard session.state == .ready else {
return
}
guard !writing else {
return
}
guard pendingWriteData.count > 0 else {
return
}
writing = true
session.writeMultipleDatagrams(self.pendingWriteData) {_ in
self.queueCall {
self.writing = false
self.checkWrite()
}
}
self.pendingWriteData.removeAll(keepingCapacity: true)
}
private func queueCall(block:@escaping ()->Void){
queue.async {
block()
}
}
deinit{
session.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.state))
}
}
Hi everyone,
I'm building a health-focused iOS and watchOS app that uses WatchConnectivity to sync real-time heart rate and core body temperature data from iPhone to Apple Watch. While the HealthKit integration works correctly on the iPhone side, I'm facing persistent issues with WatchConnectivity — the data either doesn't arrive on the Watch, or session(_:didReceiveMessage:) never gets triggered.
Here's the setup:
On iPhone: Using WCSession.default.sendMessage(_:replyHandler:errorHandler:) to send real-time values every few seconds.
On Apple Watch: Implemented WCSessionDelegate, and session(_:didReceiveMessage:) is supposed to update the UI.
Both apps have WCSession.isSupported() checks, activate the session, and assign delegates correctly.
The session state shows isPaired = true and isWatchAppInstalled = true.
Bluetooth and Wi-Fi are on, both devices are unlocked and nearby.
Despite all this, the Watch never receives messages in real-time. Sometimes, data comes through in bulk much later or not at all.
I've double-checked Info.plist configurations and made sure background modes include "Uses Bluetooth LE accessories" and "Background fetch" where appropriate.
I would really appreciate guidance on:
Best practices for reliable, low-latency message delivery with WatchConnectivity.
Debugging steps or sample code to validate message transmission and reception.
Any pitfalls related to UI updates from the delegate method.
Happy to share further details. Thanks in advance!
Topic:
App & System Services
SubTopic:
Networking
Tags:
Watch Connectivity
Health and Fitness
watchOS
Apple Watch
I develop a Network Extension with NEFilterDataProvider and want to understand how to stop or disable it on exit of the base app without deactivating NE from OS and leave ability to start it again without requiring a password from the user.
It starts normally, but when I try to disable it:
NEFilterManager.sharedManager.enabled = NO;
[NEFilterManager.sharedManager saveToPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
// never called
}];
the completion handler has never called.
But stopFilterWithReason inside the NE code called by the framework where I only replay with required completionHandler();. Then NE process keeps alive.
I also tried to call remove, which should disable NE:
[NEFilterManager.sharedManager removeFromPreferencesWithCompletionHandler:^(NSError * _Nullable error) {
// never called
}];
with same result - I freeze forever on waiting completion handler.
So what is the correct way to disable NE without explicit deactivation it by [OSSystemExtensionRequest deactivationRequestForExtension:...]?
Hi, when I perform an overlay installation via a PKG on macOS for an application containing the NEFilterDataProvider functionality, there is a chance that the entire system network becomes unreachable. Disabling the corresponding Content Filter in "System Settings > Network > Filters" immediately restores network connectivity. This issue does not occur every time, with a frequency of approximately 1 in 20 installation attempts.
The following details may help identify the problem:
The Filter.app containing the NEFilterDataProvider resides within the main app's Resources directory, e.g., /Applications/Main.app/Contents/Resources/Filter.app
Main.app is installed via a PKG; the issue typically occurs during an overlay installation of Main.app.
The NEFilterDataProvider operates as a System Extension.
The func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {} returns .allow.
Wireshark packet captures show TCP packets but no UDP packets; TCP handshakes cannot complete.
Disabling the corresponding content filter in "System Settings > Network > Filters" restores the network; re-enabling it breaks connectivity again.
After waiting for a period, approximately 30-60 minutes, network connectivity can recover automatically.
What causes this and how can it be fixed? Any workarounds?
Hi there,
can some one help how to debug this crashes? where I can start to find root causes of this crashes. I've got lot of these NSInvalidArgumentException crashes in myapp last version
I have no idea how to reproduce these issues since it doesn't point to any specific code on myapp, so I don't know how to start
Fatal Exception: NSInvalidArgumentException
-[NWConcrete_nw_protocol_options copyWithZone:]: unrecognized selector sent to instance 0x283391d60
Fatal Exception: NSInvalidArgumentException
-[NSConcreteHashTable lengthOfBytesUsingEncoding:]: unrecognized selector sent to instance 0x281d4cbe0
Fatal Exception: NSInvalidArgumentException
-[_NSXPCConnectionExportedObjectTable lengthOfBytesUsingEncoding:]: unrecognized selector sent to instance 0x2829d11d0
Fatal Exception: NSInvalidArgumentException
-[OS_dispatch_group lengthOfBytesUsingEncoding:]: unrecognized selector sent to instance 0x281a11900
Fatal Exception: NSInvalidArgumentException
-[__NSCFData getBytes:maxLength:usedLength:encoding:options:range:remainingRange:]: unrecognized selector sent to instance 0x28210e440
Fatal Exception: NSInvalidArgumentException
-[_NSCoreTypesetterLayoutCache copyWithZone:]: unrecognized selector sent to instance 0x283bbc730
Thanks
com.kitabisa.ios_issue_dd3c71c96cddb5bb99874640746439d6_crash_session_de9bb41c2b7e43fa9ccfc42e0f649aa3_DNE_0_v2_stacktrace.txt
Hi, after upgrading to MacOS Sequoia, my connection to my local IP address does not work. The issue is with the PF (MacOS advanced firewall), as I confirmed that my local application works disabling it temporarily.
Does anyone know how can I do to solve this problem? As APP developer, this is a big problem for me.
Thanks in advance.
I use eapolcfg in Apple's open source eap8021x repository to connect to the enterprise network.
1.https://github.com/gfleury/eap8021x-debug
https://opensource.apple.com/source/eap8021x/eap8021x-304.100.1/
Our enterprise network authentication is PEAP. So far, I have created a profile using the following commands and have done the access.
./eapolcfg createProfile --authType PEAP --SSID myssid --securityType WPA2 --userDefinedName MyProfile
./eapolcfg setPasswordItem --password mypassword --name myname --SSID myssid
./eapolcfg startAuthentication --interface en0 --SSID myssid
After I performed this series of operations, I passed
BOOL success = [self.interface associateToEnterpriseNetwork:network identity:nil username:username password:password error:&error];
Connection will pop up the following pop-up window, sometimes associateToEnterpriseNetwork will fail. I don't know what went wrong, is it that I missed some steps through the eapolcfg [tool?]
This function also reports the following error:Error Domain=com.apple.coreWLAN.EAPOL.error Code=1
"(null)"
Please answer my questions. Thank you very much