I am using the NEPacketTunnelFlow.readPackets completion handler to intercept packets as they are outgoing.
A problem I am currently facing is that packets of type UDP are not always received and sometimes, not at all, and I am not really sure why. Is this an operating system caching mechanism maybe?
I am not by any means a networking expert nor do I have more than basic knowledge in the field, so maybe I can get a logical explanation here.
My current setup:
iPhone 11 with iOS 16.2, Xcode 14.2, Minimum OS: iOS 13.0
This is my code:
func readOutgoingPackets() {
tunnelProvider?.packetFlow.readPackets { [weak self] packets, protocols in
guard let self else { return }
for (i, packet) in packets.enumerated() {
self?.parsePacket(packet)
}
self?.readOutgoingPackets()
}
}
func parsePacket(_ packet: Data) {
do {
let data = BinaryData(data: packet)
let header = IPHeader(version: try data.get(0),
headerLength: try data.get(1),
typeOfService: try data.get(2),
length: try data.get(3),
id: try data.get(5),
offset: try data.get(7),
timeToLive: try data.get(8),
proto: try data.get(9),
checksum: try data.get(10),
source: in_addr(s_addr: try data.get(12)),
destination: in_addr(s_addr: try data.get(16)))
switch header.proto {
case 17:
os_log("๐ ๐ฆ UDP packet read")
case 6:
os_log("๐ ๐ฆ TCP packet read")
default: break
}
} catch {
os_log("๐ โ๏ธ Error parsing packet")
}
}
I am using an external library called BinarySwift to read the binary IP packet headers.
Any help would be appreciated. Thanks!
Post
Replies
Boosts
Views
Activity
I have a network extension setup on my app using the NEPacketTunnelProvider Network Extension.
I have set its DNS settings like so, to override all system DNS settings:
let dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "8.8.4.4"])
dnsSettings.matchDomains = [""]
tunnelNetworkSettings.dnsSettings = dnsSettings
After that, I am catching all DNS network packets (aka UDP, port 53) using
tunnelProvider.packetFlow.readPackets { ... }
When the packets are incoming, I am stripping them of their headers and sending their payload (DNS Query) to the DNS servers using
currentUDPSession.session.writeMultipleDatagrams(packetPayloadsToSend) { ... }
And receiving back the answers using the callback on NWUDPSession
currentUDPSession.session.setReadHandler { ... }
Then, on the received datagram payloads (DNS Answer) I am rebuilding the UDP headers (whether it's IPv4 or IPv6, I'm supporting both), and writing them back to the TUN interface like so:
// Examples of the data I'm passing. I'm populating it with VALID rebuilt packets beforehand
let rebuiltPackets = [Data]()
let rebuiltPacketsProtocols = [NSNumber]()
tunnelProvider.packetFlow.writePackets(rebuiltPackets, withProtocols: rebuiltPacketsProtocols)
On paper, everything looks great and sniffing those packets using Wireshark shows that everything should work. But here's the thing, It doesn't!
When trying to access any website on the iPhone I am getting:
Safari could not open the page because the server stopped responding.
Here's a screenshot of the Wireshark PCAP for trying to access twitter.com:
IP address 10.0.0.43 is my tunnel, while 172.20.100.155 is my iPhone's local network IP address. Not sure why it shows both tunnel and OS DNS packets, but someone told me it's regular iOS behavior.
Any ideas? Thanks!
I have a MessageFilter app extension embedded in my iOS app and it works great filtering out junk SMS messages.
I want to show a local notification whenever a message has moved to the junk folder and I'm trying to do it like so:
final class MessageFilterExtension: ILMessageFilterExtension {
func handle(_ queryRequest: ILMessageFilterQueryRequest,
context: ILMessageFilterExtensionContext,
completion: @escaping (ILMessageFilterQueryResponse) -> Void) {
let offlineAction = self.offlineAction(for: queryRequest)
switch offlineAction {
case .allow, .junk, .promotion, .transaction:
let response = ILMessageFilterQueryResponse()
response.action = offlineAction
scheduleLocalNotification(for: keyword) /* -> Here */
completion(response)
@unknown default:
break
}
}
func scheduleLocalNotification(for keyword: Keyword) {
let content = UNMutableNotificationContent()
content.title = "Test"
content.body = "Test2"
content.categoryIdentifier = "FILTER_SMS_BLOCKED"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(request) { [weak self] error in
guard let self else { return }
if let error {
logger.log(level: .os, icon: "๐ฅ", "Error scheduling local notification: \(error)")
}
}
}
}
But I am getting the following error:
๐ ๐ฅ Error scheduling local notification: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.usernotifications.usernotificationservice was invalidated from this process." UserInfo={NSDebugDescription=The connection to service named com.apple.usernotifications.usernotificationservice was invalidated from this process.}
I have setup the Push Notifications entitlement in the main app and in this app extension, and also requested push notifications authorization from the user on the main app.
What am I missing here?
Thanks!