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?