How to change/add DNS server for connected WiFi in iOS programmatically?

后端 未结 1 1521
生来不讨喜
生来不讨喜 2021-01-13 17:34

I want to change/add DNS server for connected WiFi of my iPhone device in iOS programmatically?

The Google Public DNS IP addresses (IPv4) are as follows: 8.8.8.8 8.8

1条回答
  •  孤街浪徒
    2021-01-13 17:58

    Look at the Network Extensions classes. With the NETunnelProviderManager class you can set the onDemandRules with a NEEvaluateConnectionRule. The NEEvaluateConnectionRule constructor can take a list of wildcard with top-level-domains (i.e. ["*.com", "*.net", "*.org", "*.io"]) as the domains, and use NEEvaluateConnectionRuleAction.connectIfNeeded as the action. Set the onDemandRules of the NEEvaluateConnectionRule you create with all tlds as the domains. Then create a NEOnDemandRuleEvaluateConnection and set its connectionRules to the NEEvaluateConnectionRule created with all of the top-level-domains, and set its interfaceTypeMatch to NEOnDemandRuleInterfaceType.any. Set the NETunnelProviderManager.onDemandRules with a NEOnDemandRuleEvaluateConnection created this way. If you create a NETunnelProviderManagerand load it and save it as described above, you can then turn it on and off by using the NETunnelProviderManager.isEnabled and NETunnelProviderManager.isOnDemandEnabled properties.

    Here is an example class that does exactly that.

    import Foundation
    import NetworkExtension
    
    public class VPNConnect {
        private static let vpnDescription = "DNS OnDemand to GoogleDNS"
        private static let vpnServerDescription = "OnDemand DNS to GoogleDNS"
    
        public var manager:NETunnelProviderManager = NETunnelProviderManager()
        public var dnsEndpoint1:String = "8.8.8.8"
        public var dnsEndpoint2:String = "8.8.4.4"
    
        public var connected:Bool {
            get {
                return self.manager.isOnDemandEnabled
            }
            set {
                if newValue != self.connected {
                    update(
                        body: {
                            self.manager.isEnabled = newValue
                            self.manager.isOnDemandEnabled = newValue
    
                        },
                        complete: {
                            if newValue {
                                do {
                                    try (self.manager.connection as? NETunnelProviderSession)?.startVPNTunnel(options: nil)
                                } catch let err as NSError {
                                    NSLog("\(err.localizedDescription)")
                                }
                            } else {
                                (self.manager.connection as? NETunnelProviderSession)?.stopVPNTunnel()
                            }
                        }
                    )
                }
            }
        }
    
        public init() {
            refreshManager()
        }
    
        public func refreshManager() -> Void {
            NETunnelProviderManager.loadAllFromPreferences(completionHandler: { (managers, error) in
                if nil == error {
                    if let managers = managers {
                        for manager in managers {
                            if manager.localizedDescription == VPNConnect.vpnDescription {
                                self.manager = manager
                                return
                            }
                        }
                    }
                }
                self.setPreferences()
            })
        }
    
        private func update(body: @escaping ()->Void, complete: @escaping ()->Void) {
            manager.loadFromPreferences { error in
                if (error != nil) {
                    NSLog("Load error: \(String(describing: error?.localizedDescription))")
                    return
                }            
                body()
                self.manager.saveToPreferences { (error) in
                    if nil != error {
                        NSLog("vpn_connect: save error \(error!)")
                    } else {
                        complete()
                    }
                }
            }
        }
    
        private func setPreferences() {
            self.manager.localizedDescription = VPNConnect.vpnDescription        
            let proto = NETunnelProviderProtocol()
            proto.providerBundleIdentifier = "com.popmedic.vpntunnel.provider"
            proto.serverAddress = VPNConnect.vpnServerDescription
            self.manager.protocolConfiguration = proto
            // TLDList is a struct I created in its own swift file that has an array of all top level domains
            let evaluationRule = NEEvaluateConnectionRule(matchDomains: TLDList.tlds, 
                                                             andAction: NEEvaluateConnectionRuleAction.connectIfNeeded)
            evaluationRule.useDNSServers = [self.dnsEndpoint1, self.dnsEndpoint2]
            let onDemandRule = NEOnDemandRuleEvaluateConnection()
            onDemandRule.connectionRules = [evaluationRule]
            onDemandRule.interfaceTypeMatch = NEOnDemandRuleInterfaceType.any
            self.manager.onDemandRules = [onDemandRule]
        }
    }
    

    Please note that you will have to turn the Network Extensions capabilities on and there will be a dialog presented telling the user that you are turning on a VPN connection, but you will not have the [VPN] icon in the status bar when the connection is turned on because we are not setting a vpn, just using the on demand rules.

    Here is the TLDList class I used.

    Hate Google as much as I do, maybe use this for the DNS you set... Quad9

    0 讨论(0)
提交回复
热议问题