168 lines
5.0 KiB
Swift
168 lines
5.0 KiB
Swift
//
|
|
// 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 {
|
|
if savedDns[$0.key] == nil {
|
|
savedDns[$0.key] = []
|
|
} else {
|
|
// ignore save
|
|
}
|
|
} 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, "com.metacubex.ClashX.ProxyConfigHelper.preferences" 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] = getDNSForServiceID($0.key)
|
|
}
|
|
|
|
return re
|
|
}
|
|
|
|
func getDNSForServiceID(_ serviceID:String) -> [String] {
|
|
let serviceSetupDNSKey = "Setup:/Network/Service/\(serviceID)/DNS" as CFString
|
|
let dynmaicStore = SCDynamicStoreCreate(kCFAllocatorSystemDefault, "com.metacubex.ClashX.ProxyConfigHelper.dns" as CFString, nil, nil)
|
|
|
|
return SCDynamicStoreCopyValue(dynmaicStore, serviceSetupDNSKey)?[kSCPropNetDNSServerAddresses] as? [String] ?? []
|
|
}
|
|
|
|
|
|
@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)
|
|
|
|
let 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
|
|
}
|
|
}
|
|
}
|