I've developed a network content filter extension for macOS.
When overriding the handleNewFlow method, I want to examine the hostname for the given flow. I can do this for browsers like Safari, Firefox, and DuckDuckGo using flow.url?.host (WebKit flows) or (flow as? NEFilterSocketFlow)?.remoteHostname (Firefox flows).
However, for Google Chrome, these properties return nil, and I only get an outgoing IP address using socketFlow.remoteEndpoint as? NWHostEndpoint. How can I retrieve the outgoing domain for flows from Google Chrome?
I've tried resolving the IP to a domain name, but in most cases, I'm unable to retrieve the domain name using the following functions I found on forum posts:
func reverseDNS(ip: String) -> String {
var results: UnsafeMutablePointer<addrinfo>? = nil
defer {
if let results = results {
freeaddrinfo(results)
}
}
let error = getaddrinfo(ip, nil, nil, &results)
if (error != 0) {
NSLog("Unable to reverse ip: \(ip)")
return ip
}
for addrinfo in sequence(first: results, next: { $0?.pointee.ai_next }) {
guard let pointee = addrinfo?.pointee else {
NSLog("Unable to reverse ip: \(ip)")
return ip
}
let hname = UnsafeMutablePointer<Int8>.allocate(capacity: Int(NI_MAXHOST))
defer {
hname.deallocate()
}
let error = getnameinfo(pointee.ai_addr, pointee.ai_addrlen, hname, socklen_t(NI_MAXHOST), nil, 0, 0)
if (error != 0) {
continue
}
return String(cString: hname)
}
return ip
}
func resolveIP(_ ipAddress: String) -> String? {
var hints = addrinfo(
ai_flags: AI_NUMERICHOST,
ai_family: AF_UNSPEC,
ai_socktype: SOCK_STREAM,
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil
)
var res: UnsafeMutablePointer<addrinfo>? = nil
let status = getaddrinfo(ipAddress, nil, &hints, &res)
guard status == 0, let result = res else {
print("Error: \(String(cString: gai_strerror(status)))")
return nil
}
var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if let addr = result.pointee.ai_addr {
let addrLen = socklen_t(result.pointee.ai_addrlen)
if getnameinfo(addr, addrLen, &hostBuffer, socklen_t(hostBuffer.count), nil, 0, 0) == 0 {
freeaddrinfo(res)
return String(cString: hostBuffer)
}
}
freeaddrinfo(res)
return nil
}
I know that Little Snitch can block and display domain name requests using a content filter, even in Google Chrome, so I'm certain it's possible. However, I'm unsure how to accomplish this. Can anyone assist me in resolving IP addresses to hostnames for most IP addresses, or in obtaining the hostnames directly from the flow on macOS?
Post
Replies
Boosts
Views
Activity
I'm attempting to reload a Safari Content Blocker from within a sandboxed command-line tool configured as a LaunchAgent. However, when I use SFContentBlockerManager to reload the content blocker, I encounter the error SFErrorDomain Code=1: Unavailable error.
Is it possible to reload a content blocker from a LaunchAgent? If so, how can it be done?
//
// main.swift
// BlockerUpdater
//
// Created by Sebastian Livoni on 30/06/2024.
//
import Foundation
import SafariServices
// Function to reload content blocker asynchronously
func reloadContentBlocker() async {
NSLog("Hello, World!")
do {
try await SFContentBlockerManager.reloadContentBlocker(withIdentifier: "me.livoni.blocker.dns")
NSLog("Reload complete")
} catch {
NSLog("Failed to reload content blocker: \(error.localizedDescription)")
}
}
// Main entry point for async code
@main
struct BlockerUpdater {
static func main() async {
await reloadContentBlocker()
}
}
When following this guide https://developer.apple.com/documentation/xcode/embedding-a-helper-tool-in-a-sandboxed-app I cannot run the embedded binary.
I'm getting this error: "zsh: trace trap"
I would like to be able to use the embedded binary for NativeMessaging with for example Chrome but I can't figure out how to allow it to be executable even with sandboxing enabled.
How are Strongbox able to do this with their afproxy executable?