refactor: system dns

This commit is contained in:
mrFq1 2022-11-27 11:59:38 +08:00
parent 8e88476f27
commit 9a3b404e74
9 changed files with 288 additions and 100 deletions

View File

@ -13,9 +13,10 @@
018F88F9286DD0CB004DD0F7 /* DualTitleMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */; };
01943259287D19BC008CC51A /* ClashRuleProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01943258287D19BC008CC51A /* ClashRuleProvider.swift */; };
019A239628657A7A00AE5698 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019A239528657A7A00AE5698 /* main.swift */; };
01A645D3292C769D00B37FA2 /* DNSConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A645D2292C759100B37FA2 /* DNSConfiguration.swift */; };
01B009AE2854533300B93618 /* geoip.dat.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01B009AC2854533200B93618 /* geoip.dat.gz */; };
01B009AF2854533300B93618 /* geosite.dat.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01B009AD2854533300B93618 /* geosite.dat.gz */; };
01B30C43291CE18F0081C4F7 /* MetaDNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B30C41291C98930081C4F7 /* MetaDNS.swift */; };
01BC9ABE2928EB5A00F9B177 /* MetaDNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01BC9ABD2928E5C600F9B177 /* MetaDNS.swift */; };
01C1462A28962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz in Resources */ = {isa = PBXBuildFile; fileRef = 01C1462928962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz */; };
185CBAEDFE986E6E1B836359 /* libPods-ClashX Meta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA63125BBF03DC1A291D3351 /* libPods-ClashX Meta.a */; };
4913C82321157D0200F6B87C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913C82221157D0200F6B87C /* Notification.swift */; };
@ -140,9 +141,10 @@
018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualTitleMenuItem.swift; sourceTree = "<group>"; };
01943258287D19BC008CC51A /* ClashRuleProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashRuleProvider.swift; sourceTree = "<group>"; };
019A239528657A7A00AE5698 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
01A645D2292C759100B37FA2 /* DNSConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSConfiguration.swift; sourceTree = "<group>"; };
01B009AC2854533200B93618 /* geoip.dat.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = geoip.dat.gz; sourceTree = "<group>"; };
01B009AD2854533300B93618 /* geosite.dat.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = geosite.dat.gz; sourceTree = "<group>"; };
01B30C41291C98930081C4F7 /* MetaDNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaDNS.swift; sourceTree = "<group>"; };
01BC9ABD2928E5C600F9B177 /* MetaDNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaDNS.swift; sourceTree = "<group>"; };
01C1462928962E4E00346AF3 /* com.metacubex.ClashX.ProxyConfigHelper.meta.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = com.metacubex.ClashX.ProxyConfigHelper.meta.gz; sourceTree = "<group>"; };
3F86DA2DA3CC14731BE1ABF7 /* Pods-ClashX Meta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX Meta.release.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX Meta/Pods-ClashX Meta.release.xcconfig"; sourceTree = "<group>"; };
4913C82221157D0200F6B87C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
@ -464,7 +466,6 @@
49761DA521C9490400AE13EF /* Resources */,
49CF3B3A20CD783A001EBF94 /* Support Files */,
49CF3B2020CD7463001EBF94 /* AppDelegate.swift */,
01B30C41291C98930081C4F7 /* MetaDNS.swift */,
49CF3B2620CD7465001EBF94 /* Main.storyboard */,
49CF3B2920CD7465001EBF94 /* Info.plist */,
49CF3B2A20CD7465001EBF94 /* ClashX.entitlements */,
@ -529,6 +530,8 @@
498960722340F21C00AFB7EC /* com.metacubex.ClashX.ProxyConfigHelper.entitlements */,
019A239528657A7A00AE5698 /* main.swift */,
0162E74E2864B819007218A6 /* MetaTask.swift */,
01BC9ABD2928E5C600F9B177 /* MetaDNS.swift */,
01A645D2292C759100B37FA2 /* DNSConfiguration.swift */,
F935B2F12307C802009E4D33 /* Helper-Launchd.plist */,
F935B2EA2307B6BA009E4D33 /* Helper-Info.plist */,
F935B2F22307CD32009E4D33 /* ProxyConfigHelper.h */,
@ -710,7 +713,6 @@
F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */,
495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */,
4929F67F258CE04700A435F6 /* Settings.swift in Sources */,
01B30C43291CE18F0081C4F7 /* MetaDNS.swift in Sources */,
493AEAE3221AE3420016FE98 /* AppVersionUtil.swift in Sources */,
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */,
496BDEE021196F1E00C5207F /* Logger.swift in Sources */,
@ -774,9 +776,11 @@
buildActionMask = 2147483647;
files = (
F935B2F42307CD32009E4D33 /* ProxyConfigHelper.m in Sources */,
01A645D3292C769D00B37FA2 /* DNSConfiguration.swift in Sources */,
0162E74F2864B819007218A6 /* MetaTask.swift in Sources */,
019A239628657A7A00AE5698 /* main.swift in Sources */,
491E6203258A424D00313AEF /* CommonUtils.m in Sources */,
01BC9ABE2928EB5A00F9B177 /* MetaDNS.swift in Sources */,
F935B2FA23083EE6009E4D33 /* ProxySettingTool.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -72,8 +72,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var dashboardWindowController: ClashWebViewWindowController?
let metaDNS = MetaDNS()
func applicationWillFinishLaunching(_ notification: Notification) {
signal(SIGPIPE, SIG_IGN)
// crash recorder
@ -138,8 +136,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var shouldWait = false
PrivilegedHelperManager.shared.helper()?.stopMeta()
metaDNS.updateTunState(false)
PrivilegedHelperManager.shared.helper()?.updateTun(with: false)
if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) ||
NetworkChangeNotifier.hasInterfaceProxySetToClash() {
@ -241,7 +238,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
.distinctUntilChanged()
.bind { _ in
let isTunMode = ConfigManager.shared.isTunModeVariable.value
self.metaDNS.updateTunState(isTunMode)
PrivilegedHelperManager.shared.helper()?.updateTun(with: isTunMode)
Logger.log("tun state updated,new: \(isTunMode)")
}.disposed(by: disposeBag)
Observable

View File

@ -1,89 +0,0 @@
//
// MetaDNS.swift
// ClashX
import Cocoa
class MetaDNS: NSObject {
var defaultDNS = "198.18.0.2"
var savedDNS: [String]?
func updateTunState(_ isTun: Bool) {
if isTun {
if savedDNS == nil {
let dns = getDNS()
if dns.count == 1, dns[0] == defaultDNS {
savedDNS = []
} else {
savedDNS = dns
}
}
setDNS()
} else {
if savedDNS == nil || savedDNS!.count == 0 {
setDNS([])
} else if let dns = savedDNS {
setDNS(dns)
}
}
}
func getDNS() -> [String] {
let re = runCommand("/usr/sbin/networksetup", args: [
"-getdnsservers",
"\(networkServiceName())"
])
if re.contains("There aren't any DNS Servers") {
return []
}
return re.split(separator: "\n").map(String.init)
}
func setDNS(_ dns: [String] = ["198.18.0.2"]) {
var args = [
"-setdnsservers",
"\(networkServiceName())"
]
if dns.count > 0 {
args.append(contentsOf: dns)
} else {
args.append("Empty")
}
_ = runCommand("/usr/sbin/networksetup", args: args)
}
func networkServiceName() -> String {
// https://apple.stackexchange.com/a/432170
runCommand("/bin/bash", args: ["-c", "networksetup -listnetworkserviceorder | awk -v DEV=$(/usr/sbin/scutil --nwi | awk -F': ' '/Network interfaces/ {print $2;exit;}') -F': |,' '$0~ DEV {print $2;exit;}'"])
}
func runCommand(_ path: String, args: [String]) -> String {
let proc = Process()
proc.executableURL = .init(fileURLWithPath: path)
proc.arguments = args
let pipe = Pipe()
proc.standardOutput = pipe
do {
try proc.run()
} catch let error {
Logger.log(error.localizedDescription)
return ""
}
proc.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard proc.terminationStatus == 0,
var out = String(data: data, encoding: .utf8) else {
return ""
}
if out.last == "\n" {
out.removeLast()
}
return out
}
}

View File

@ -0,0 +1,104 @@
//
// DNSConfiguration.swift
// ClashX
import Cocoa
import SystemConfiguration
// https://github.com/ivanstegic/menu-bar-dns/blob/main/Menu%20Bar%20DNS/Menu%20Bar%20DNS/DNSConfiguration.swift
class DNSConfiguration: NSObject {
static let DNSConfigurationTypeKey = Bundle.main.bundleIdentifier! + ".dns"
static let ServiceTypeWiFi = "IEEE80211"
static let ServiceTypeEthernet = "Ethernet"
static func getnterfaceTypeByServiceIDs(_ services: Array<SCNetworkService>) throws -> Dictionary<String, String> {
let allServicesIDsAndInterfaceType = try services.map { service -> (String, String) in
guard
let id = SCNetworkServiceGetServiceID(service) as String?,
let interface = SCNetworkServiceGetInterface(service),
let interfaceType = SCNetworkInterfaceGetInterfaceType(interface) as String?
else {
throw SCCopyLastError()
}
return (id, interfaceType)
}
return Dictionary(uniqueKeysWithValues: allServicesIDsAndInterfaceType)
}
static func isConnectedService(_ service : SCNetworkService) throws -> Bool {
guard
let id = SCNetworkServiceGetServiceID(service) as String?
else {
throw SCCopyLastError()
}
let dynmaicStore = SCDynamicStoreCreate(kCFAllocatorSystemDefault, "DNSSETTING" as CFString, nil, nil)
let serviceStateIPv4Key = "State:/Network/Service/\(id)/IPv4" as CFString
let value = SCDynamicStoreCopyValue(dynmaicStore, serviceStateIPv4Key) as CFPropertyList?
return value != nil
}
static func getDNSForServiceID(_ serviceID:String) -> [String] {
let serviceDNSKey = "State:/Network/Service/\(serviceID)/DNS" as CFString
let serviceSetupDNSKey = "Setup:/Network/Service/\(serviceID)/DNS" as CFString
let dynmaicStore = SCDynamicStoreCreate(kCFAllocatorSystemDefault, "DNSSETTING" as CFString, nil, nil)
var allDNSIPAddresses : Array<String> = []
let dynamicPlist = SCDynamicStoreCopyValue(dynmaicStore, serviceDNSKey)
let manualAddressPlist = SCDynamicStoreCopyValue(dynmaicStore, serviceSetupDNSKey)
if let dnsValues = manualAddressPlist?[kSCPropNetDNSServerAddresses] as? [String] {
allDNSIPAddresses += dnsValues
}
/*
if let dhcpValues = dynamicPlist?[kSCPropNetDNSServerAddresses] as? [String] {
let uniqueValues = Array(Set(dhcpValues))
for dhcpValue in uniqueValues {
let newvalue = dhcpValue.appending(" (via DHCP)")
allDNSIPAddresses.append(newvalue)
}
}
*/
return allDNSIPAddresses
}
static func getAddresses() -> (Array<String>, Array<String>) {
var ethernetDNSAddresses : Array<String> = []
var WiFiDNSAddresses : Array<String> = []
do {
let prefs = SCPreferencesCreate(
nil, DNSConfigurationTypeKey as NSString,
nil
) as SCPreferences?
let allServicesCF = SCNetworkServiceCopyAll(prefs!)
let allServices = allServicesCF as? [SCNetworkService]
let allConnectedServices = try allServices?.filter({ (service) -> Bool in
return try isConnectedService(service)
})
let serviceTypeByIDs = try getnterfaceTypeByServiceIDs(allConnectedServices!) as Dictionary<String, String>?
for (id, type) in serviceTypeByIDs! {
switch (type) {
case ServiceTypeWiFi:
WiFiDNSAddresses += getDNSForServiceID(id)
case ServiceTypeEthernet:
ethernetDNSAddresses += getDNSForServiceID(id)
default:
print("")
}
}
}
catch {
return ([], [])
}
return (ethernetDNSAddresses, WiFiDNSAddresses)
}
}

View File

@ -9,9 +9,9 @@
<key>CFBundleName</key>
<string>com.metacubex.ClashX.ProxyConfigHelper</string>
<key>CFBundleShortVersionString</key>
<string>1.5</string>
<string>1.6</string>
<key>CFBundleVersion</key>
<string>6</string>
<string>7</string>
<key>SMAuthorizedClients</key>
<array>
<string>anchor apple generic and identifier &quot;com.metacubex.ClashX.ProxyConfigHelper&quot; and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = MEWHFZ92DY)</string>

View File

@ -0,0 +1,155 @@
//
// MetaDNS.swift
// ClashX
import Cocoa
import SystemConfiguration
// https://github.com/zhuhaow/Specht2/blob/main/app/me.zhuhaow.Specht2.proxy-helper/ProxyHelper.swift
class MetaDNS: NSObject {
var savedDns = [String: [String]]()
let defaultDNS = "198.18.0.2"
let authRef: AuthorizationRef
override init() {
var auth: AuthorizationRef?
let authFlags: AuthorizationFlags = [.extendRights, .interactionAllowed, .preAuthorize]
let authErr = AuthorizationCreate(nil, nil, authFlags, &auth)
if authErr != noErr {
NSLog("Error: Failed to create administration authorization due to error \(authErr).")
}
if auth == nil {
NSLog("Error: No authorization has been granted to modify network configuration.")
}
authRef = auth!
super.init()
}
deinit {
AuthorizationFree(authRef, AuthorizationFlags())
}
@objc func updateDns() {
let dns = getAllDns()
dns.forEach {
if $0.value.count == 1,
$0.value[0] == defaultDNS {
savedDns[$0.key] = []
} else {
savedDns[$0.key] = $0.value
}
}
let dnsDic = dns.reduce(into: [:]) {
$0[$1.key] = [defaultDNS]
}
updateDNSConfigure(dnsDic)
}
@objc func revertDns() {
updateDNSConfigure(savedDns)
savedDns.removeAll()
}
func getAllDns() -> [String: [String]] {
var re = [String: [String]]()
guard let prefs = SCPreferencesCreate(nil, "ClashX" as CFString, nil),
let values = SCPreferencesGetValue(prefs, kSCPrefNetworkServices) as? [String: AnyObject] else {
return re
}
values.reduce(into: [:]) {
$0[$1.key] = $1.value.value(forKeyPath: "Interface.Hardware") as? String
}.filter {
["AirPort", "Wi-Fi", "Ethernet"].contains($0.value)
}.forEach {
re[$0.key] = DNSConfiguration.getDNSForServiceID($0.key)
}
return re
}
@objc func flushDnsCache() {
CommonUtils.runCommand("/usr/bin/killall", args: ["-HUP", "mDNSResponder"])
print("flushDnsCache")
}
private func updateDNSConfigure(_ dnsDic: [String: [String]]) {
guard let prefRef = SCPreferencesCreateWithAuthorization(
nil,
"com.metacubex.ClashX.ProxyConfigHelper.config" as CFString,
nil,
authRef) else {
NSLog("Error: Failed to obtain preference ref.")
return
}
guard SCPreferencesLock(prefRef, true) else {
NSLog("Error: Failed to obtain lock to preference.")
return
}
defer {
SCPreferencesUnlock(prefRef)
}
guard let networks = SCNetworkSetCopyCurrent(prefRef),
let services = SCNetworkSetCopyServices(networks) as? [SCNetworkService] else {
NSLog("Error: Failed to load network services.")
return
}
let type = kSCNetworkProtocolTypeDNS
services.forEach { service in
guard let id = SCNetworkServiceGetServiceID(service) as? String,
let dns = dnsDic[id] else {
return
}
guard let protoc = SCNetworkServiceCopyProtocol(service, type) else {
NSLog("Error: Failed to obtain \(type) settings for \(SCNetworkServiceGetName(service)!)")
return
}
let config = SCNetworkProtocolGetConfiguration(protoc)
var dic = (config as NSDictionary?)?.mutableCopy() as? NSMutableDictionary ?? NSMutableDictionary()
dic["ServerAddresses"] = dns
guard SCNetworkProtocolSetConfiguration(protoc, dic as CFDictionary) else {
NSLog("Error: Failed to set \(type) settings for \(SCNetworkServiceGetName(service)!)")
return
}
NSLog("Set \(type) settings for \(SCNetworkServiceGetName(service)!)")
}
guard SCPreferencesCommitChanges(prefRef) else {
NSLog("Error: Failed to commit preference change")
return
}
guard SCPreferencesApplyChanges(prefRef) else {
NSLog("Error: Failed to apply preference change")
return
}
}
}

View File

@ -26,6 +26,7 @@ ProxyConfigRemoteProcessProtocol
@property (nonatomic, assign) BOOL shouldQuit;
@property (nonatomic, strong) MetaTask *metaTask;
@property (nonatomic, strong) MetaDNS *metaDNS;
@end
@ -38,6 +39,7 @@ ProxyConfigRemoteProcessProtocol
self.listener = [[NSXPCListener alloc] initWithMachServiceName:@"com.metacubex.ClashX.ProxyConfigHelper"];
self.listener.delegate = self;
self.metaTask = [MetaTask new];
self.metaDNS = [MetaDNS new];
}
return self;
}
@ -158,4 +160,15 @@ ProxyConfigRemoteProcessProtocol
[self.metaTask getUsedPorts:reply];
}
- (void)updateTunWith:(BOOL)state {
dispatch_async(dispatch_get_main_queue(), ^{
if (state) {
[self.metaDNS updateDns];
} else {
[self.metaDNS revertDns];
}
[self.metaDNS flushDnsCache];
});
}
@end

View File

@ -27,6 +27,8 @@ typedef void(^dictReplyBlock)(NSDictionary *);
- (void)getUsedPorts:(stringReplyBlock)reply;
- (void)updateTunWith:(BOOL)state;
- (void)stopMeta;
- (void)getVersion:(stringReplyBlock)reply;

View File

@ -6,3 +6,4 @@
#import "ProxyConfigHelper.h"
#import "ProxyConfigRemoteProcessProtocol.h"
#import "CommonUtils.h"
#import "ProxySettingTool.h"