Yes, you were right. The App sandbox was blocking it. Now I am able to extract bundleId, but I don't understand why I can't connect with the host and load the website. It would help if you reviewed the code and guided me further if I were missing something.
import Foundation
import NetworkExtension
import os.log
class AppProxyProvider: NETransparentProxyProvider {
private let log = OSLog(subsystem: "com.proxy.tcp.network.extension", category: "provider")
override func startProxy(options: [String: Any]?, completionHandler: @escaping (Error?) -> Void) {
os_log(.info, log: log, "Starting Transparent Proxy %{public}@", "")
// Configure the network settings for the tunnel
let proxySettings = NETransparentProxyNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
// Include both TCP and UDP rules for complete coverage
proxySettings.includedNetworkRules = [
NENetworkRule(
remoteNetwork: nil,
remotePrefix: 0,
localNetwork: nil,
localPrefix: 0,
protocol: .TCP,
direction: .outbound
),
NENetworkRule(
remoteNetwork: nil,
remotePrefix: 0,
localNetwork: nil,
localPrefix: 0,
protocol: .UDP,
direction: .outbound
)
]
// Apply the settings
setTunnelNetworkSettings(proxySettings) { error in
if let error = error {
os_log(.error, log: self.log, "Failed to apply tunnel settings: %{public}@", error.localizedDescription)
} else {
os_log(.info, log: self.log, "Tunnel settings applied successfully %{public}@", "")
}
completionHandler(error)
}
}
override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
os_log(.info, log: log, "Stopping proxy with reason: %{public}d", reason.rawValue)
// Cancel any ongoing operations
setTunnelNetworkSettings(nil) { error in
if let error = error {
os_log(.error, log: self.log, "Error clearing tunnel settings: %{public}@", error.localizedDescription)
}
completionHandler()
}
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
guard let message = String(data: messageData, encoding: .utf8) else {
os_log(.error, log: log, "Received invalid message data %{public}@", "")
completionHandler?(nil)
return
}
os_log(.debug, log: log, "Received message: %{public}@", message)
// Handle specific message types
if message == "get_mapping" {
let responseMessage = "No active mappings"
completionHandler?(responseMessage.data(using: .utf8))
} else {
// Default response
completionHandler?(messageData)
}
}
override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
os_log(.debug, log: log, "Handling new flow of type: %{public}@", String(describing: type(of: flow)))
// Handle TCP flows
if let tcpFlow = flow as? NEAppProxyTCPFlow {
guard let remoteEndpoint = tcpFlow.remoteEndpoint as? NWHostEndpoint else {
os_log(.error, log: log, "Unable to cast remote endpoint to NWHostEndpoint %{public}@", "")
return false
}
os_log(.info, log: log, "New TCP flow to %{public}@:%{public}@", remoteEndpoint.hostname, remoteEndpoint.port)
if let token = flow.metaData.sourceAppAuditToken {
let bundleIdFromData = self.bundleIDForAuditToken(token)
//My this app bundle ID
if let bundleID = bundleIdFromData, bundleID.contains("com.promobi.TransparentProxyMacApril") {
os_log(.error, log: log, "Skipping the flow as it is not from my app %{public}@", bundleID)
return false
}
}
// Create a TCP connection manager for this flow
let connectionManager = TransparentProxyManager(flow: tcpFlow, endpoint: remoteEndpoint)
connectionManager.startExchangingData()
return true
}
// Handle UDP flows if needed
else if let udpFlow = flow as? NEAppProxyUDPFlow {
os_log(.info, log: log, "UDP flow received - not handling in this implementation %{public}@", "")
return false
}
// Unsupported flow type
os_log(.error, log: log, "Unsupported flow type: %{public}@", String(describing: type(of: flow)))
return false
}
func bundleIDForAuditToken(_ tokenData: Data) -> String? {
// Create logger
let log = OSLog(subsystem: "com.yourcompany.bundleid", category: "AuditToken")
// Get a code reference.
var codeQ: SecCode? = nil
var err = SecCodeCopyGuestWithAttributes(nil, [
kSecGuestAttributeAudit: tokenData
] as NSDictionary, [], &codeQ)
guard err == errSecSuccess else {
os_log("Failed to copy guest with attributes: %{public}d", log: log, type: .error, err)
return nil
}
let code = codeQ!
// Convert that to a static code.
var staticCodeQ: SecStaticCode? = nil
err = SecCodeCopyStaticCode(code, [], &staticCodeQ)
guard err == errSecSuccess else {
os_log("Failed to copy static code: %{public}d", log: log, type: .error, err)
return nil
}
let staticCode = staticCodeQ!
// Get code signing information about that.
var infoQ: CFDictionary? = nil
err = SecCodeCopySigningInformation(staticCode, [], &infoQ)
guard err == errSecSuccess else {
os_log("Failed to copy signing information: %{public}d", log: log, type: .error, err)
return nil
}
let info = infoQ! as! [String:Any]
// Extract the bundle ID from that.
guard
let plist = info[kSecCodeInfoPList as String] as? [String:Any],
let bundleID = plist[kCFBundleIdentifierKey as String] as? String
else {
os_log("Failed to extract bundle ID from info dictionary", log: log, type: .error)
return nil
}
os_log("Successfully extracted bundle ID: %{public}@", log: log, type: .debug, bundleID)
return bundleID
}
}