简体   繁体   中英

Check whether device is connected to a VPN

I am able to check whether a device is connected to the internet using:

var connected: Bool = true
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
    SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
    connected = false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt2(kSCNetworkFlagsConnectionRequired)) != 0

if isReachable && !needsConnection && connected {
    login(usernameTextField.text!, password: appPassword.text!, environment: environmentUrl)
} else {
    let alert = UIAlertView()
    alert.title = "No internet connection detected."
    alert.addButtonWithTitle("OK")
    alert.show()

    return
}

But is there any way of checking whether the device is connected to a VPN? The app that I am writing requires a VPN for access, so rather than allowing a user to attempt a login when not connected to a VPN, I would like to prompt them prior (better user experience in my opinion).

Thank you for your help.

I made it work for Swift 3/4 by using the following code:

private var isConnectedToVpn: Bool {
    if let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as? Dictionary<String, Any>,
        let scopes = settings["__SCOPED__"] as? [String:Any] {
        for (key, _) in scopes {
         if key.contains("tap") || key.contains("tun") || key.contains("ppp") || key.contains("ipsec") {
                return true
            }
        }
    }
    return false
}

I am using the code below (Swift 3 and Swift 4 compatible) to check the VPN connection on iOS devices which is working fine for me. Tested on two private VPNs and ExpressVPN, all using ipsec0 protocol.

func isVPNConnected() -> Bool {
    let cfDict = CFNetworkCopySystemProxySettings()
    let nsDict = cfDict!.takeRetainedValue() as NSDictionary
    let keys = nsDict["__SCOPED__"] as! NSDictionary

    for key: String in keys.allKeys as! [String] {
        if (key == "tap" || key == "tun" || key == "ppp" || key == "ipsec" || key == "ipsec0" || key == "utun1" || key == "utun2") {
            return true
        }
    }
    return false
}

Also here is the gist link for the code - Gist Link

ObjC:

#include <ifaddrs.h>

- (BOOL)isVPNOn
{
    BOOL flag = NO;
    NSString *version = [UIDevice currentDevice].systemVersion;
    // need two ways to judge this.
    if (version.doubleValue >= 9.0)
    {
        NSDictionary *dict = CFBridgingRelease(CFNetworkCopySystemProxySettings());
        NSArray *keys = [dict[@"__SCOPED__"] allKeys];
        for (NSString *key in keys) {
            if ([key rangeOfString:@"tap"].location != NSNotFound ||
                [key rangeOfString:@"tun"].location != NSNotFound ||
                [key rangeOfString:@"ipsec"].location != NSNotFound ||
                [key rangeOfString:@"ppp"].location != NSNotFound){
                flag = YES;
                break;
            }
        }
    }
    else
    {
        struct ifaddrs *interfaces = NULL;
        struct ifaddrs *temp_addr = NULL;
        int success = 0;

        // retrieve the current interfaces - returns 0 on success
        success = getifaddrs(&interfaces);
        if (success == 0)
        {
            // Loop through linked list of interfaces
            temp_addr = interfaces;
            while (temp_addr != NULL)
            {
                NSString *string = [NSString stringWithFormat:@"%s" , temp_addr->ifa_name];
                if ([string rangeOfString:@"tap"].location != NSNotFound ||
                    [string rangeOfString:@"tun"].location != NSNotFound ||
                    [string rangeOfString:@"ipsec"].location != NSNotFound ||
                    [string rangeOfString:@"ppp"].location != NSNotFound)
                {
                    flag = YES;
                    break;
                }
                temp_addr = temp_addr->ifa_next;
            }
        }

        // Free memory
        freeifaddrs(interfaces);
    }

    return flag;
}

ref: RealReachability/RealReachability.m - GitHub

There's a difference between checking if a VPN is installed (which is what the earlier answers detect) vs. checking whether traffic is being routed over a VPN (or other proxy). If you are only checking if a VPN is installed, it can have a negative impact on users who have 3rd party VPN apps installed that only filter some (but not all) traffic, which is common for enterprises.

Here's some sample code for checking if traffic to a particular host is being routed over a VPN/transparent proxy or not.

import Foundation
import Network

func main() {
    
    // Check if connection to host is being routed over transparent proxy (such as VPN) 
    let c = NWConnection(host: "domainname.com", port: 443, using: .tcp)
    c.stateUpdateHandler = { state in
        if (state == .ready) {
            if (c.currentPath?.usesInterfaceType(.other) == true) {
                print("Connection is over transparent proxy")
            } else {
                print("Connection is not over transparent proxy")
            }
        }
    }
    c.start(queue: .main)
    dispatchMain()
}

main()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM