Hi,
I'm very new to everything related to networking and I've been trying to make some sort of "parental control" app for macOS where if the user tries to access some website domain (e.g youtube.com) the request is denied and the user can't access the website.
Turns out I used NEFilterDataProvider and NEDNSProxyProvider to achieve that but it's not 100% bullet proof.
First problem I had is that most of the time I can't access the hostname in the NEFilterDataProvider when trying to extract it from the socketFlow.remoteEndpoint. Most of the time I get the ipv4. And the problem is : I don't know the IPV4 behind the domains, specially when they're changing frequently.
if let socketFlow = flow as? NEFilterSocketFlow {
let remoteEndpoint = socketFlow.remoteFlowEndpoint
switch remoteEndpoint {
case .hostPort(let host, _):
switch host {
case .name(let hostname, _):
log.info("🌿 Intercepted hostname: \(hostname, privacy: .public)")
case .ipv4(let ipv4):
let ipv4String = ipv4.rawValue.map { String($0) }.joined(separator: ".")
log.info("🌿 Intercepted IPV4: \(ipv4String, privacy: .public)")
So that's why I used the DNSProxyProvider. With it I can get the domains. I succeeded to drop some of the flows by not writing the datagrams when I see a domain to block, but that does not work 100% of the time and sometimes, for youtube.com for example then the website is still reachable (and sometimes it works successfully and I can't access it). I guess because the IP behind the domain has already been resolved and so it's cached somewhere and the browser does not need to send an UDP request anymore to know the IP behind the domain?
Is there a 100% bullet proof way to block traffic to specific domains? Ideally I would like to get rid of the DNSProxyProvider and use only the NEFilterDataProvider but if I can't access the hostnames then I don't see how to do it.
Is there a 100% bullet proof way to block traffic to specific domains?
Not on the path that you’re currently on.
Regarding the filter provider behaviour you’re observing, that’s expected. NE can only give you a host name if:
-
The app use a connect-by-name API, such as
URLSession
or Network framework, or -
The app went out of its way to tell the system about the name, using the
<networkext/ne_socket.h>
API.
Many apps don’t do this — especially apps uses cross-platform code bases — and thus all NE has to work with is an IP address.
And you can’t use an IP address to block traffic because, in a world of CDNs, many different host names can map to the same IP address.
Doing this with a DNS proxy is also tricky because:
-
The system DNS resolver will often choose to use encrypted DNS.
-
Not all apps use the system DNS resolver, so even if you disable the system resolver’s encrypted DNS support there’s no guarantee that your proxy will ‘see’ all DNS resolution.
One option that might work for you is to peek at the start of the flow. If the flow uses TLS then you might be able to get the host name from the SNI extension. See Server Name Indication. However, even that will start to fail once Encrypted Client Hello starts to become universal.
You might want to take a look at the new URL Filter provider. See WWDC 2025 Session 234 Filter and tunnel network traffic with NetworkExtension for an in-depth discussion. However, even that has limitations, most notably, apps not use URLSession
or Network framework must again opt in (via NEURLFilter
).
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"