VPN initial

This commit is contained in:
relikd
2020-02-25 00:08:37 +01:00
parent 017aa891ec
commit 188a130825
20 changed files with 753 additions and 873 deletions

View File

@@ -1,19 +1,24 @@
import UIKit
import NetworkExtension
let VPNConfigBundleIdentifier = "de.uni-bamberg.psi.AppCheck.VPN"
let dateFormatter = DateFormatter()
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var managerVPN: NETunnelProviderManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
if UserDefaults.standard.bool(forKey: "kill_proxy") {
UserDefaults.standard.set(false, forKey: "kill_proxy")
disableDNS()
} else {
postDNSState()
}
// if UserDefaults.standard.bool(forKey: "kill_proxy") {
// UserDefaults.standard.set(false, forKey: "kill_proxy")
// disableDNS()
// } else {
// postDNSState()
// }
if UserDefaults.standard.bool(forKey: "kill_db") {
UserDefaults.standard.set(false, forKey: "kill_db")
@@ -24,238 +29,92 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
try db.createTable(table: DNSQuery.self)
} catch {}
// loadVPN { self.startVPN() }
self.postVPNState(.invalid)
loadVPN { mgr in
self.managerVPN = mgr
self.postVPNState()
}
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
postDNSState()
// postVPNState()
}
func setProxyEnabled(_ newState: Bool) {
// DNS:
if newState != managerDNS.isEnabled {
newState ? enableDNS() : disableDNS()
guard let mgr = self.managerVPN else {
self.createNewVPN { manager in
self.managerVPN = manager
self.setProxyEnabled(newState)
}
return
}
// VPN:
// let con = self.managerVPN?.connection
// if newState != (con?.status == NEVPNStatus.connected) {
// self.updateVPN {
// self.managerVPN?.isEnabled = newState
// newState ? try? con?.startVPNTunnel() : con?.stopVPNTunnel()
// }
// }
}
// MARK: DNS
let managerDNS = NEDNSProxyManager.shared()
private func enableDNS() {
updateDNS {
self.managerDNS.localizedDescription = "GlassDNS"
let proto = NEDNSProxyProviderProtocol()
proto.providerBundleIdentifier = "de.uni-bamberg.psi.AppCheck.DNS"
self.managerDNS.providerProtocol = proto
self.managerDNS.isEnabled = true
}
}
private func disableDNS() {
updateDNS {
self.managerDNS.isEnabled = false
}
}
private func updateDNS(_ body: @escaping () -> Void) {
managerDNS.loadFromPreferences { (error) in
guard error == nil else { return }
body()
self.managerDNS.saveToPreferences { (error) in
self.postDNSState()
guard error == nil else { return }
let state = mgr.isEnabled && (mgr.connection.status == NEVPNStatus.connected)
if state != newState {
self.updateVPN({ mgr.isEnabled = true }) {
newState ? try? mgr.connection.startVPNTunnel() : mgr.connection.stopVPNTunnel()
}
}
}
private func postDNSState() {
managerDNS.loadFromPreferences {_ in
NotificationCenter.default.post(name: .init("ChangedStateGlassDNS"), object: self.managerDNS.isEnabled)
}
}
// MARK: VPN
/*var managerVPN: NETunnelProviderManager?
private func createNewVPN(_ success: @escaping (_ manager: NETunnelProviderManager) -> Void) {
let mgr = NETunnelProviderManager()
mgr.localizedDescription = "AppCheck Monitor"
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = VPNConfigBundleIdentifier
proto.serverAddress = "127.0.0.1"
mgr.protocolConfiguration = proto
mgr.isEnabled = true
mgr.saveToPreferences { error in
guard error == nil else { return }
success(mgr)
}
}
private func loadVPN(_ finally: @escaping () -> Void) {
private func loadVPN(_ finally: @escaping (_ manager: NETunnelProviderManager?) -> Void) {
NETunnelProviderManager.loadAllFromPreferences { managers, error in
if managers?.count ?? 0 > 0 {
managers?.forEach({ mgr in
if let proto = (mgr.protocolConfiguration as? NETunnelProviderProtocol) {
if proto.providerBundleIdentifier == "de.uni-bamberg.psi.AppCheck.Tunnel" {
// self.managerVPN = mgr
mgr.removeFromPreferences()
}
}
})
}
if self.managerVPN != nil {
finally()
} else {
let mgr = NETunnelProviderManager()
mgr.localizedDescription = "GlassTunnel"
let proto = NETunnelProviderProtocol()
proto.providerBundleIdentifier = "de.uni-bamberg.psi.AppCheck.Tunnel"
proto.serverAddress = "127.0.0.1"
// proto.username = "none"
// proto.proxySettings = NEProxySettings()
// proto.proxySettings?.httpEnabled = true
// proto.proxySettings?.httpsEnabled = true
// proto.authenticationMethod = .sharedSecret
// proto.sharedSecretReference = try! VPNKeychain.persistentReferenceFor(service: "GlassTunnel", account:"none", password: "none".data(using: String.Encoding.utf8)!)
mgr.protocolConfiguration = proto
mgr.isEnabled = true
self.managerVPN = mgr
mgr.saveToPreferences { (error) in
guard error == nil else {
NSLog("VPN: save error: \(String(describing: error))")
return
}
finally()
}
}
}
}
private func startVPN() {
updateVPN {
do {
try self.managerVPN?.connection.startVPNTunnel()
} catch {
print("VPN: start error: \(error.localizedDescription)")
}
}
}
private func updateVPN(_ body: @escaping () -> Void) {
self.managerVPN?.loadFromPreferences { (error) in
guard error == nil else {
guard let mgrs = managers, mgrs.count > 0 else {
finally(nil)
return
}
body()
self.managerVPN?.saveToPreferences { (error) in
guard error == nil else {
NSLog("VPN: save error: \(String(describing: error))")
return
for mgr in mgrs {
if let proto = (mgr.protocolConfiguration as? NETunnelProviderProtocol) {
if proto.providerBundleIdentifier == VPNConfigBundleIdentifier {
finally(mgr)
return
}
}
}
}
}*/
}
// MARK: VPNKeychain
/*
/// Utility routines for working with the keychain.
enum VPNKeychain {
/// Returns a persistent reference for a generic password keychain item, adding it to
/// (or updating it in) the keychain if necessary.
///
/// This delegates the work to two helper routines depending on whether the item already
/// exists in the keychain or not.
///
/// - Parameters:
/// - service: The service name for the item.
/// - account: The account for the item.
/// - password: The desired password.
/// - Returns: A persistent reference to the item.
/// - Throws: Any error returned by the Security framework.
static func persistentReferenceFor(service: String, account: String, password: Data) throws -> Data {
var copyResult: CFTypeRef? = nil
let err = SecItemCopyMatching([
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecReturnPersistentRef: true,
kSecReturnData: true
] as NSDictionary, &copyResult)
switch err {
case errSecSuccess:
return try self.persistentReferenceByUpdating(copyResult: copyResult!, service: service, account: account, password: password)
case errSecItemNotFound:
return try self.persistentReferenceByAdding(service: service, account:account, password: password)
default:
try throwOSStatus(err)
// `throwOSStatus(_:)` only returns in the `errSecSuccess` case. We know we're
// not in that case but the compiler can't figure that out, alas.
fatalError()
finally(nil)
}
}
/// Returns a persistent reference for a generic password keychain item by updating it
/// in the keychain if necessary.
///
/// - Parameters:
/// - copyResult: The result from the `SecItemCopyMatching` done by `persistentReferenceFor(service:account:password:)`.
/// - service: The service name for the item.
/// - account: The account for the item.
/// - password: The desired password.
/// - Returns: A persistent reference to the item.
/// - Throws: Any error returned by the Security framework.
private static func persistentReferenceByUpdating(copyResult: CFTypeRef, service: String, account: String, password: Data) throws -> Data {
let copyResult = copyResult as! [String:Any]
let persistentRef = copyResult[kSecValuePersistentRef as String] as! NSData as Data
let currentPassword = copyResult[kSecValueData as String] as! NSData as Data
if password != currentPassword {
let err = SecItemUpdate([
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
] as NSDictionary, [
kSecValueData: password
] as NSDictionary)
try throwOSStatus(err)
private func updateVPN(_ body: @escaping () -> Void, _ onSuccess: @escaping () -> Void) {
self.managerVPN?.loadFromPreferences { error in
guard error == nil else { return }
body()
self.managerVPN?.saveToPreferences { error in
guard error == nil else { return }
onSuccess()
}
}
return persistentRef
}
/// Returns a persistent reference for a generic password keychain item by adding it to
/// the keychain.
///
/// - Parameters:
/// - service: The service name for the item.
/// - account: The account for the item.
/// - password: The desired password.
/// - Returns: A persistent reference to the item.
/// - Throws: Any error returned by the Security framework.
private static func persistentReferenceByAdding(service: String, account: String, password: Data) throws -> Data {
var addResult: CFTypeRef? = nil
let err = SecItemAdd([
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
kSecValueData: password,
kSecReturnPersistentRef: true,
] as NSDictionary, &addResult)
try throwOSStatus(err)
return addResult! as! NSData as Data
private func postVPNState() {
guard let mgr = self.managerVPN else {
self.postVPNState(.invalid)
return
}
mgr.loadFromPreferences { _ in
self.postVPNState(mgr.connection.status)
}
}
/// Throws an error if a Security framework call has failed.
///
/// - Parameter err: The error to check.
private static func throwOSStatus(_ err: OSStatus) throws {
guard err == errSecSuccess else {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(err), userInfo: nil)
}
private func postVPNState(_ state: NEVPNStatus) {
NotificationCenter.default.post(name: .init("ChangedStateGlassVPN"), object: state)
}
}
*/

View File

@@ -7,17 +7,16 @@
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Services-->
<!--Requests-->
<scene sceneID="MN1-aZ-cZt">
<objects>
<tableViewController id="pdd-aM-sKl" customClass="TVCApps" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="kj3-8X-TyT">
<tableViewController id="pdd-aM-sKl" customClass="TVCDomains" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="kj3-8X-TyT">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<inset key="separatorInset" minX="15" minY="0.0" maxX="15" maxY="0.0"/>
<textView key="tableHeaderView" clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" editable="NO" selectable="NO" id="QWn-iX-27k">
<rect key="frame" x="0.0" y="0.0" width="320" height="501"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="317"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<accessibility key="accessibilityConfiguration">
@@ -26,11 +25,7 @@
</accessibility>
<string key="text">AppCheck helps you identify which applications communicate with third parties. It does so by logging DNS network requests. AppCheck learns only the destination addresses, not the actual data that is exchanged.
For improved readability, AppCheck shows domain names in reversed order (org.example instead of example.org)
Requests to the same domain in quick succession cannot be monitored due to DNS caching (counters may be inaccurate).
Your data belongs to you. Therefore, monitoring and analysis take place on your device only. The app does not share any data with us or any other third-party. There is one exception to this rule: To display app icons, AppCheck sends the Bundle ID (e.g., com.apple.Music) of all monitored apps to `itunes.apple.com`.
Your data belongs to you. Therefore, monitoring and analysis take place on your device only. The app does not share any data with us or any other third-party.
⒈ Tap the red button in the upper right corner to start the DNS proxy. The proxy is only accessible locally to apps on this device.
⒉ The proxy monitors DNS requests in the background.
@@ -41,35 +36,31 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="AppBundleCell" textLabel="d1D-Z8-ddD" detailTextLabel="8LO-2o-dzK" imageView="zHu-V9-cbv" rowHeight="60" style="IBUITableViewCellStyleSubtitle" id="Jcl-oS-Z3B">
<rect key="frame" x="0.0" y="529" width="320" height="60"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationWidth="10" reuseIdentifier="DomainCell" textLabel="0HB-5f-eB1" detailTextLabel="MRe-Eq-gvc" style="IBUITableViewCellStyleSubtitle" id="F8D-aK-j1W">
<rect key="frame" x="0.0" y="345" width="320" height="55.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Jcl-oS-Z3B" id="B7M-RN-vvx">
<rect key="frame" x="0.0" y="0.0" width="320" height="60"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="F8D-aK-j1W" id="FY2-xr-hqh">
<rect key="frame" x="0.0" y="0.0" width="320" height="55.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="d1D-Z8-ddD">
<rect key="frame" x="15" y="10" width="33.5" height="20.5"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0HB-5f-eB1">
<rect key="frame" x="16" y="10" width="33.5" height="20.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8LO-2o-dzK">
<rect key="frame" x="15" y="33.5" width="44" height="14.5"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="MRe-Eq-gvc">
<rect key="frame" x="16" y="31.5" width="44" height="14.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" id="zHu-V9-cbv">
<rect key="frame" x="16" y="0.0" width="60" height="60"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="WcC-nb-Vf5" kind="show" id="Z7t-Jy-aNR"/>
<segue destination="WcC-nb-Vf5" kind="show" id="EVQ-hO-JE9"/>
</connections>
</tableViewCell>
</prototypes>
@@ -78,7 +69,7 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your
<outlet property="delegate" destination="pdd-aM-sKl" id="3RN-az-SYU"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Services" id="nY5-jL-QT9">
<navigationItem key="navigationItem" title="Requests" id="nY5-jL-QT9">
<barButtonItem key="leftBarButtonItem" systemItem="trash" id="PPO-Wv-RWf">
<connections>
<action selector="clickToolbarLeft:" destination="pdd-aM-sKl" id="iFj-4n-lvg"/>
@@ -98,34 +89,34 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your
</objects>
<point key="canvasLocation" x="742.5" y="167.95774647887325"/>
</scene>
<!--Requests-->
<!--Subdomains-->
<scene sceneID="ZCV-Yx-jjW">
<objects>
<tableViewController id="WcC-nb-Vf5" customClass="TVCRequests" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController id="WcC-nb-Vf5" customClass="TVCHosts" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="nRF-dc-dC2">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="RequestCell" textLabel="Rnk-SP-UHm" detailTextLabel="ovQ-lJ-hWJ" style="IBUITableViewCellStyleValue1" id="uv0-9B-Zbb">
<rect key="frame" x="0.0" y="28" width="320" height="43.5"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="HostCell" textLabel="Rnk-SP-UHm" detailTextLabel="ovQ-lJ-hWJ" style="IBUITableViewCellStyleSubtitle" id="uv0-9B-Zbb">
<rect key="frame" x="0.0" y="28" width="320" height="55.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uv0-9B-Zbb" id="6vH-Du-gCg">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="55.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Rnk-SP-UHm">
<rect key="frame" x="16" y="12" width="33.5" height="20.5"/>
<rect key="frame" x="16" y="10" width="33.5" height="20.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ovQ-lJ-hWJ">
<rect key="frame" x="260" y="12" width="44" height="20.5"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ovQ-lJ-hWJ">
<rect key="frame" x="16" y="31.5" width="44" height="14.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="tertiaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
@@ -140,7 +131,7 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your
<outlet property="delegate" destination="WcC-nb-Vf5" id="sBd-BW-Wg6"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Requests" prompt="com.app.Example" id="TvD-8U-F05"/>
<navigationItem key="navigationItem" title="Subdomains" prompt="com.app.Example" id="TvD-8U-F05"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Gdi-Xi-JUL" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
@@ -149,13 +140,13 @@ Your data belongs to you. Therefore, monitoring and analysis take place on your
<!--Occurrences-->
<scene sceneID="ws3-sK-l8m">
<objects>
<tableViewController id="h7Z-Qr-pJ5" customClass="TVCRequestDetails" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController id="h7Z-Qr-pJ5" customClass="TVCHostDetails" customModule="AppCheck" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="4ms-FO-Fge">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="RequestDetailCell" textLabel="J2P-mU-Vad" style="IBUITableViewCellStyleDefault" id="ZCA-Dz-i92">
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="HostDetailCell" textLabel="J2P-mU-Vad" style="IBUITableViewCellStyleDefault" id="ZCA-Dz-i92">
<rect key="frame" x="0.0" y="28" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ZCA-Dz-i92" id="nxe-48-jAQ">

View File

@@ -1,139 +0,0 @@
import UIKit
class TVCApps: UITableViewController {
private let appDelegate = UIApplication.shared.delegate as! AppDelegate
private var dataSource: [AppInfoType] = []
@IBOutlet private var welcomeMessage: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.welcomeMessage.frame.size.height = 0
AppInfoType.initWorkingDir()
NotificationCenter.default.addObserver(forName: .init("ChangedStateGlassDNS"), object: nil, queue: OperationQueue.main) { [weak self] notification in
// let stateView = self.navigationItem.rightBarButtonItem?.customView as? ProxyStateView
// stateView?.status = (notification.object as! Bool ? .running : .stopped)
// let active = notification.object as! Bool
self?.changeState(notification.object as! Bool)
}
// pull-to-refresh
// tableView.refreshControl = UIRefreshControl()
// tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged)
// performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil)
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in
self?.reloadDataSource(nil)
}
// navigationItem.leftBarButtonItem?.title = "\u{2699}\u{0000FE0E}"
// navigationItem.leftBarButtonItem?.setTitleTextAttributes([NSAttributedString.Key.font : UIFont.systemFont(ofSize: 32)], for: .normal)
}
@IBAction func clickToolbarLeft(_ sender: Any) {
let alert = UIAlertController(title: "Clear results?",
message: "You are about to delete all results that have been logged in the past. Continue?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { [weak self] _ in
try? SQLiteDatabase.open(path: DB_PATH).destroyContent()
self?.reloadDataSource(nil)
}))
self.present(alert, animated: true, completion: nil)
}
@IBAction func clickToolbarRight(_ sender: Any) {
let inactive = (self.navigationItem.rightBarButtonItem?.tag == 0)
let alert = UIAlertController(title: "\(inactive ? "En" : "Dis")able Proxy?",
message: "The DNS proxy is currently \(inactive ? "dis" : "en")abled, do you want to proceed and \(inactive ? "en" : "dis")able logging?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: inactive ? "Enable" : "Disable", style: .default, handler: { [weak self] _ in
self?.changeState(inactive)
self?.appDelegate.setProxyEnabled(inactive)
}))
self.present(alert, animated: true, completion: nil)
}
func changeState(_ active: Bool) {
let stateView = self.navigationItem.rightBarButtonItem
if stateView?.tag == 0 && !active,
stateView?.tag == 1 && active {
return // don't need to change, already correct state
}
stateView?.tag = (active ? 1 : 0)
stateView?.title = (active ? "Active" : "Inactive")
stateView?.tintColor = (active ? .systemGreen : .systemRed)
// let newButton = UIBarButtonItem(barButtonSystemItem: (active ? .pause : .play), target: self, action: #selector(clickToolbarRight(_:)))
// newButton.tintColor = (active ? .systemRed : .systemGreen)
// newButton.tag = (active ? 1 : 0)
// self.navigationItem.setRightBarButton(newButton, animated: true)
}
private func updateCellAt(_ index: Int) {
DispatchQueue.main.async {
guard index >= 0 else {
self.welcomeMessage.frame.size.height = (self.dataSource.count == 0 ? self.view.frame.size.height : 0)
self.tableView.reloadData()
return
}
if let idx = self.tableView.indexPathsForVisibleRows?.first(where: { indexPath -> Bool in
indexPath.row == index
}) {
self.tableView.reloadRows(at: [idx], with: .automatic)
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let index = tableView.indexPathForSelectedRow?.row {
let info = dataSource[index]
segue.destination.navigationItem.prompt = info.name ?? info.id
(segue.destination as? TVCRequests)?.appBundleId = info.id
}
}
// MARK: - Table View Delegate
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AppBundleCell")!
let appBundle = dataSource[indexPath.row]
cell.textLabel?.text = appBundle.name ?? appBundle.id
cell.detailTextLabel?.text = appBundle.seller
cell.imageView?.image = appBundle.getImage()
cell.imageView?.layer.cornerRadius = 6.75
cell.imageView?.layer.masksToBounds = true
return cell
}
// MARK: - Data Source
@objc private func reloadDataSource(_ sender : Any?) {
DispatchQueue.global().async {
self.dataSource = self.sqliteAppList().map { AppInfoType(id: $0) } // will load from HD
self.updateCellAt(-1)
for i in self.dataSource.indices {
self.dataSource[i].updateIfNeeded { [weak self] in
self?.updateCellAt(i)
}
}
if let refreshControl = sender as? UIRefreshControl {
refreshControl.endRefreshing()
}
}
}
private func sqliteAppList() -> [String] {
// return ["unkown", "com.apple.Fitness", "com.apple.AppStore", "com.apple.store.Jolly", "com.apple.supportapp", "com.apple.TVRemote", "com.apple.Bridge", "com.apple.calculator", "com.apple.mobilecal", "com.apple.camera", "com.apple.classroom", "com.apple.clips", "com.apple.mobiletimer", "com.apple.compass", "com.apple.MobileAddressBook", "com.apple.facetime", "com.apple.appleseed.FeedbackAssistant", "com.apple.mobileme.fmf1", "com.apple.mobileme.fmip1", "com.apple.findmy", "com.apple.DocumentsApp", "com.apple.gamecenter", "com.apple.mobilegarageband", "com.apple.Health", "com.apple.Antimony", "com.apple.Home", "com.apple.iBooks", "com.apple.iCloudDriveApp", "com.apple.iMovie", "com.apple.itunesconnect.mobile", "com.apple.MobileStore", "com.apple.itunesu", "com.apple.Keynote", "com.apple.musicapps.remote", "com.apple.mobilemail", "com.apple.Maps", "com.apple.measure", "com.apple.MobileSMS", "com.apple.Music", "com.apple.musicmemos", "com.apple.news", "com.apple.mobilenotes", "com.apple.Numbers", "com.apple.Pages", "com.apple.mobilephone", "com.apple.Photo-Booth", "com.apple.mobileslideshow", "com.apple.Playgrounds", "com.apple.podcasts", "com.apple.reminders", "com.apple.Remote", "com.apple.mobilesafari", "com.apple.Preferences", "is.workflow.my.app", "com.apple.shortcuts", "com.apple.SiriViewService", "com.apple.stocks", "com.apple.tips", "com.apple.movietrailers", "com.apple.tv", "com.apple.videos", "com.apple.VoiceMemos", "com.apple.Passbook", "com.apple.weather", "com.apple.wwdc"]
// return ["com.apple.backupd", "com.apple.searchd", "com.apple.SafariBookmarksSyncAgent", "com.apple.AppStore", "com.apple.mobilemail", "com.apple.iBooks", "com.apple.icloud.searchpartyd", "com.apple.ap.adprivacyd", "com.apple.bluetoothd", "com.apple.commcentermobilehelper", "com.apple", "com.apple.coreidv.coreidvd", "com.apple.online-auth-agent", "com.apple.tipsd", "com.apple.wifid", "com.apple.captiveagent", "com.apple.pipelined", "com.apple.bird", "com.apple.amfid", "com.apple.nsurlsessiond", "com.apple.Preferences", "com.apple.sharingd", "com.apple.UserEventAgent", "com.apple.healthappd"]
guard let db = try? SQLiteDatabase.open(path: DB_PATH) else {
return []
}
return db.appList()
}
}

138
main/TVCDomains.swift Normal file
View File

@@ -0,0 +1,138 @@
import UIKit
import NetworkExtension
class TVCDomains: UITableViewController {
private let appDelegate = UIApplication.shared.delegate as! AppDelegate
private var dataSource: [GroupedDomain] = []
@IBOutlet private var welcomeMessage: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.welcomeMessage.frame.size.height = 0
// AppInfoType.initWorkingDir()
NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: OperationQueue.main) { [weak self] notification in
self?.changeState((notification.object as? NETunnelProviderSession)?.status ?? .invalid)
}
NotificationCenter.default.addObserver(forName: .init("ChangedStateGlassVPN"), object: nil, queue: OperationQueue.main) { [weak self] notification in
self?.changeState((notification.object as? NEVPNStatus) ?? .invalid)
}
// pull-to-refresh
tableView.refreshControl = UIRefreshControl()
tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged)
performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil)
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in
self?.reloadDataSource(nil)
}
// navigationItem.leftBarButtonItem?.title = "\u{2699}\u{0000FE0E}"
// navigationItem.leftBarButtonItem?.setTitleTextAttributes([NSAttributedString.Key.font : UIFont.systemFont(ofSize: 32)], for: .normal)
}
@IBAction func clickToolbarLeft(_ sender: Any) {
let alert = UIAlertController(title: "Clear results?",
message: "You are about to delete all results that have been logged in the past. Continue?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { [weak self] _ in
try? SQLiteDatabase.open(path: DB_PATH).destroyContent()
self?.reloadDataSource(nil)
}))
self.present(alert, animated: true, completion: nil)
}
@IBAction func clickToolbarRight(_ sender: Any) {
let active = (self.navigationItem.rightBarButtonItem?.tag == NEVPNStatus.connected.rawValue)
let alert = UIAlertController(title: "\(active ? "Dis" : "En")able Proxy?",
message: "The VPN proxy is currently \(active ? "en" : "dis")abled, do you want to proceed and \(active ? "dis" : "en")able logging?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: active ? "Disable" : "Enable", style: .default, handler: { [weak self] _ in
self?.appDelegate.setProxyEnabled(!active)
}))
self.present(alert, animated: true, completion: nil)
}
func changeState(_ newState: NEVPNStatus) {
let stateView = self.navigationItem.rightBarButtonItem
if stateView?.tag == newState.rawValue {
return // don't need to change, already correct state
}
stateView?.tag = newState.rawValue
switch newState {
case .connected:
stateView?.title = "Active"
stateView?.tintColor = .systemGreen
case .connecting, .disconnecting, .reasserting:
stateView?.title = "Updating"
stateView?.tintColor = .systemYellow
case .invalid, .disconnected:
fallthrough
@unknown default:
stateView?.title = "Inactive"
stateView?.tintColor = .systemRed
}
// let newButton = UIBarButtonItem(barButtonSystemItem: (active ? .pause : .play), target: self, action: #selector(clickToolbarRight(_:)))
// newButton.tintColor = (active ? .systemRed : .systemGreen)
// newButton.tag = (active ? 1 : 0)
// self.navigationItem.setRightBarButton(newButton, animated: true)
}
private func updateCellAt(_ index: Int) {
DispatchQueue.main.async {
guard index >= 0 else {
self.welcomeMessage.frame.size.height = (self.dataSource.count == 0 ? self.view.frame.size.height : 0)
self.tableView.reloadData()
return
}
if let idx = self.tableView.indexPathsForVisibleRows?.first(where: { indexPath -> Bool in
indexPath.row == index
}) {
self.tableView.reloadRows(at: [idx], with: .automatic)
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let index = tableView.indexPathForSelectedRow?.row {
let dom = dataSource[index].label
segue.destination.navigationItem.prompt = dom
(segue.destination as? TVCHosts)?.domain = dom
}
}
// MARK: - Table View Delegate
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DomainCell")!
let entry = dataSource[indexPath.row]
let last = Date.init(timeIntervalSince1970: Double(entry.lastModified))
cell.textLabel?.text = entry.label
cell.detailTextLabel?.text = "\(dateFormatter.string(from: last))\(entry.count)"
return cell
}
// MARK: - Data Source
@objc private func reloadDataSource(_ sender : Any?) {
self.dataSource = self.sqliteAppList()
if let refreshControl = sender as? UIRefreshControl {
DispatchQueue.main.async { refreshControl.endRefreshing() }
}
self.updateCellAt(-1)
}
private func sqliteAppList() -> [GroupedDomain] {
guard let db = try? SQLiteDatabase.open(path: DB_PATH) else {
return []
}
return db.domainList()
}
}

44
main/TVCHostDetails.swift Normal file
View File

@@ -0,0 +1,44 @@
import UIKit
class TVCHostDetails: UITableViewController {
public var domain: String?
public var host: String?
private var dataSource: [Timestamp] = []
override func viewDidLoad() {
super.viewDidLoad()
// pull-to-refresh
tableView.refreshControl = UIRefreshControl()
tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged)
performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil)
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in
self?.reloadDataSource(nil)
}
}
@objc private func reloadDataSource(_ sender : Any?) {
dataSource = []
guard let dom = domain, let db = try? SQLiteDatabase.open(path: DB_PATH) else {
return
}
dataSource = db.timesForDomain(dom, host: host)
DispatchQueue.main.async {
if let refreshControl = sender as? UIRefreshControl {
refreshControl.endRefreshing()
}
self.tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HostDetailCell")!
let date = Date.init(timeIntervalSince1970: Double(dataSource[indexPath.row]))
cell.textLabel?.text = dateFormatter.string(from: date)
return cell
}
}

80
main/TVCHosts.swift Normal file
View File

@@ -0,0 +1,80 @@
import UIKit
class TVCHosts: UITableViewController {
private var attributedDomain: NSAttributedString = NSAttributedString(string: "")
public var domain: String? {
willSet {
attributedDomain = NSAttributedString(string: ".\(newValue ?? "")",
attributes: [.foregroundColor : UIColor.darkGray])
}
}
private var dataSource: [GroupedDomain] = []
override func viewDidLoad() {
super.viewDidLoad()
// pull-to-refresh
tableView.refreshControl = UIRefreshControl()
tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged)
performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil)
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in
self?.reloadDataSource(nil)
}
}
@objc private func reloadDataSource(_ sender : Any?) {
// dataSource = [("hi", [1, 2]), ("there", [2, 4, 8, 1580472632]), ("dude", [1, 2, 3])]
// return ()
dataSource = []
guard let dom = domain, let db = try? SQLiteDatabase.open(path: DB_PATH) else {
return
}
dataSource = db.hostsForDomain(dom as NSString)
// var list: [String: [Int64]] = [:]
// db.subdomainsForDomain(appIdentifier: dom as NSString) { query in
//// let x = query.dns.split(separator: ".").reversed().joined(separator: ".")
// let x = query.host ?? ""
// if list[x] == nil {
// list[x] = []
// }
// list[x]?.append(query.ts)
// }
// dataSource = list.sorted{ $0.0 < $1.0 }
DispatchQueue.main.async {
if let refreshControl = sender as? UIRefreshControl {
refreshControl.endRefreshing()
}
self.tableView.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let index = tableView.indexPathForSelectedRow?.row {
let entry = dataSource[index]
segue.destination.navigationItem.prompt = "\(entry.label).\(domain ?? "")"
let vc = (segue.destination as? TVCHostDetails)
vc?.domain = domain
vc?.host = entry.label
}
}
// MARK: - Data Source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HostCell")!
let entry = dataSource[indexPath.row]
let last = Date.init(timeIntervalSince1970: Double(entry.lastModified))
let x = NSMutableAttributedString(string: entry.label)
x.append(attributedDomain)
cell.textLabel?.attributedText = x
// cell.textLabel?.text = "\(entry.label).\(domain ?? "")"
cell.detailTextLabel?.text = "\(dateFormatter.string(from: last))\(entry.count)"
return cell
}
}

View File

@@ -1,24 +0,0 @@
import UIKit
class TVCRequestDetails: UITableViewController {
public var dataSource: [Int64] = []
private let dateFormatter = DateFormatter()
override func viewDidLoad() {
super.viewDidLoad()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RequestDetailCell")!
let intVal = dataSource[indexPath.row]
let date = Date.init(timeIntervalSince1970: Double(intVal))
cell.textLabel?.text = dateFormatter.string(from: date)
return cell
}
}

View File

@@ -1,64 +0,0 @@
import UIKit
class TVCRequests: UITableViewController {
public var appBundleId: String? = nil
private var dataSource: [(String, [Int64])] = []
override func viewDidLoad() {
super.viewDidLoad()
// pull-to-refresh
tableView.refreshControl = UIRefreshControl()
tableView.refreshControl?.addTarget(self, action: #selector(reloadDataSource(_:)), for: .valueChanged)
performSelector(inBackground: #selector(reloadDataSource(_:)), with: nil)
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] _ in
self?.reloadDataSource(nil)
}
}
@objc private func reloadDataSource(_ sender : Any?) {
// dataSource = [("hi", [1, 2]), ("there", [2, 4, 8, 1580472632]), ("dude", [1, 2, 3])]
// return ()
dataSource = []
guard let bundleId = appBundleId, let db = try? SQLiteDatabase.open(path: DB_PATH) else {
return
}
var list: [String: [Int64]] = [:]
db.dnsQueriesForApp(appIdentifier: bundleId as NSString) { query in
let x = query.dns.split(separator: ".").reversed().joined(separator: ".")
if list[x] == nil {
list[x] = []
}
list[x]?.append(query.ts)
}
dataSource = list.sorted{ $0.0 < $1.0 }
DispatchQueue.main.async {
self.tableView.reloadData()
if let refreshControl = sender as? UIRefreshControl {
refreshControl.endRefreshing()
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let index = tableView.indexPathForSelectedRow?.row {
let info = dataSource[index]
segue.destination.navigationItem.prompt = info.0
(segue.destination as? TVCRequestDetails)?.dataSource = info.1
}
}
// MARK: - Data Source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RequestCell")!
let info = dataSource[indexPath.row]
cell.textLabel?.text = info.0
cell.detailTextLabel?.text = "\(info.1.count)"
return cell
}
}

View File

@@ -4,7 +4,7 @@
<dict>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>dns-proxy</string>
<string>packet-tunnel-provider</string>
</array>
<key>com.apple.developer.networking.vpn.api</key>
<array>