Last active
January 7, 2020 13:37
-
-
Save justMaku/723f14cc03ed154478a5b5efebf28035 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import SystemConfiguration | |
enum Error: Swift.Error { | |
case pppInterfaceNotFound | |
case failedToWriteToConfigurationStore | |
} | |
let dynamicStore = SCDynamicStoreCreate(nil, "setVPNDNS" as CFString, nil, nil) | |
func cast<T>(_ input: CFArray?) -> Array<T> { | |
guard | |
let asAnyObject = input as? [AnyObject], | |
let asTypedObject = asAnyObject as? [T] | |
else { | |
return [] | |
} | |
return asTypedObject | |
} | |
func query<T>(_ filter: String) -> [T] { | |
return cast((SCDynamicStoreCopyKeyList(dynamicStore, filter as CFString))) | |
} | |
func get<T>(_ path: String) -> T { | |
return SCDynamicStoreCopyValue(dynamicStore, path as CFString) as! T | |
} | |
func set(path: String, value: NSDictionary) -> Bool { | |
return SCDynamicStoreSetValue(dynamicStore, path as CFString, value) | |
} | |
func getServiceUUID(for desiredInterfaceName: String) throws -> String { | |
let serviceKeys: [NSString] = query("State:/Network/Service/(.+)/PPP") | |
for serviceKey in serviceKeys { | |
let dictionary: [String: AnyObject] = get(serviceKey as String) | |
let interfaceName = dictionary[kSCPropInterfaceName as String] as? String | |
if (interfaceName == desiredInterfaceName) { | |
return serviceKey | |
.replacingOccurrences(of: "State:/Network/Service/", with: "") | |
.replacingOccurrences(of: "/PPP", with: "") // Quite a naive approach, might explode. | |
} | |
} | |
throw Error.pppInterfaceNotFound | |
} | |
func overridePrimary(for serviceUIID: String) { | |
let path = "State:/Network/Service/\(serviceUIID)/IPv4" | |
var current: [CFString: AnyObject] = get(path) | |
current[kSCPropNetOverridePrimary] = 1 as NSNumber | |
current[kSCPropInterfaceName] = "en0" as CFString // TODO: Hardcoded for my setup. | |
current[kSCPropNetIPv4Router] = "192.168.1.1" as CFString // TODO: Hardcoded for my setup. | |
set(path: path, value: current as NSDictionary) | |
} | |
func injectDNSServers(for serviceUUID: String, dnsServers: [String]) { | |
let path = "State:/Network/Service/\(serviceUUID)/DNS" | |
let dictionary = [ | |
kSCPropNetDNSServerAddresses: dnsServers, | |
kSCPropNetDNSSupplementalMatchOrders: [100000 as NSNumber], | |
kSCPropNetDNSSupplementalMatchDomains: [] | |
] as [CFString : Any] | |
set(path: path, value: dictionary as NSDictionary) | |
} | |
func makePrimaryService(uuid: String) { | |
let globalIPv4Path = "State:/Network/Global/IPv4" | |
var current: [CFString: AnyObject] = get(globalIPv4Path) | |
current[kSCDynamicStorePropNetPrimaryService] = uuid as CFString | |
set(path: globalIPv4Path, value: current as NSDictionary) | |
} | |
let interface = "ppp0" | |
let dnsServers = [ | |
"10.11.3.241", // VPN DNS | |
"10.11.3.242", // VPN DNS | |
"83.169.184.33", | |
"83.169.184.97" | |
] | |
let uuid = try getServiceUUID(for: interface) | |
injectDNSServers(for: uuid, dnsServers: dnsServers) | |
overridePrimary(for: uuid) | |
makePrimaryService(uuid: uuid) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment