iPhone получит SSID без приватной библиотеки

У меня есть коммерческое приложение, которое имеет вполне законную причину для просмотра SSID сети, к которой оно подключено: если оно подключено к сети Adhoc для стороннего аппаратного устройства, оно должно функционировать не так, как если бы оно было подключен к интернету.

Все, что я видел о получении SSID, говорит мне, что я должен использовать Apple80211, который, как я понимаю, является частной библиотекой. Я также читал, что если я использую личную библиотеку, Apple не одобрит приложение.

Я застрял между Apple и наковальней, или мне чего-то не хватает здесь?

8 ответов

Решение

Начиная с iOS 7 или 8, вы можете сделать это, используя преимущества ARC и модулей, которые будут автоматически связываться в необходимой среде:

@import SystemConfiguration.CaptiveNetwork;

/** Returns first non-empty SSID network info dictionary.
 *  @see CNCopyCurrentNetworkInfo */
- (NSDictionary *)fetchSSIDInfo
{
    NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
    NSLog(@"%s: Supported interfaces: %@", __func__, interfaceNames);

    NSDictionary *SSIDInfo;
    for (NSString *interfaceName in interfaceNames) {
        SSIDInfo = CFBridgingRelease(
            CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
        NSLog(@"%s: %@ => %@", __func__, interfaceName, SSIDInfo);

        BOOL isNotEmpty = (SSIDInfo.count > 0);
        if (isNotEmpty) {
            break;
        }
    }
    return SSIDInfo;
}

(Это модернизация образца кода, написанного для iOS 4.1+. Единственными изменениями были введение более четких имен переменных и принятие ARC и модулей.)

Пример вывода:

2011-03-04 15:32:00.669 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: Supported interfaces: (
    en0
)
2011-03-04 15:32:00.693 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: en0 => {
    BSSID = "ca:fe:ca:fe:ca:fe";
    SSID = XXXX;
    SSIDDATA = <01234567 01234567 01234567>;
}

Обратите внимание, что в симуляторе не поддерживаются if. Протестируйте на своем устройстве.

До версии 4.1 вам, возможно, повезло в словарях "Конфигурация системы". Например, используя scutil на моем Mac:

$ scutil
> show State:/Network/Interface/en1/AirPort
<dictionary> {
  Power Status : 1
  SecureIBSSEnabled : FALSE
  BSSID : <data> 0xcafecafecafe
  SSID_STR : XXXX
  SSID : <data> 0x012345670123456701234567
  Busy : FALSE
  CHANNEL : <dictionary> {
    CHANNEL : 1
    CHANNEL_FLAGS : 10
  }
}
> exit

ОБНОВЛЕНИЕ для iOS 10 и выше

CNCopySupportedInterfaces больше не считается устаревшим в iOS 10. ( Справочник по API)

Вам необходимо импортировать SystemConfiguration/CaptiveNetwork.h и добавить SystemConfiguration.framework в связанные библиотеки вашей цели (в стадии сборки).

Вот фрагмент кода в быстром (ответ RikiRiocma):

import Foundation
import SystemConfiguration.CaptiveNetwork

public class SSID {
    class func fetchSSIDInfo() -> String {
        var currentSSID = ""
        if let interfaces = CNCopySupportedInterfaces() {
            for i in 0..<CFArrayGetCount(interfaces) {
                let interfaceName: UnsafePointer<Void> = CFArrayGetValueAtIndex(interfaces, i)
                let rec = unsafeBitCast(interfaceName, AnyObject.self)
                let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)")
                if unsafeInterfaceData != nil {
                    let interfaceData = unsafeInterfaceData! as Dictionary!
                    currentSSID = interfaceData["SSID"] as! String
                }
            }
        }
        return currentSSID
    }
}

(Важно: CNCopySupportedInterfaces возвращает ноль на симуляторе.)

Для Objective-c см . Ответ Эсада здесь и ниже.

+ (NSString *)GetCurrentWifiHotSpotName {    
    NSString *wifiName = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            wifiName = info[@"SSID"];
        }
    }
    return wifiName;
}

ОБНОВЛЕНИЕ ДЛЯ iOS 9

Начиная с iOS 9 Captive Network устарела *. ( источник)

* Больше не рекомендуется в iOS 10, см. Выше.

Рекомендуется использовать NEHotspotHelper ( источник)

Вам нужно будет отправить электронное письмо по электронной почте на адрес networkextension@apple.com и запросить разрешения. ( источник)

Пример кода ( не мой код. См. Ответ Пабло А):

for(NEHotspotNetwork *hotspotNetwork in [NEHotspotHelper supportedNetworkInterfaces]) {
    NSString *ssid = hotspotNetwork.SSID;
    NSString *bssid = hotspotNetwork.BSSID;
    BOOL secure = hotspotNetwork.secure;
    BOOL autoJoined = hotspotNetwork.autoJoined;
    double signalStrength = hotspotNetwork.signalStrength;
}

Примечание: Да, они отказались от CNCopySupportedInterfaces в iOS 9 и полностью изменили свою позицию в iOS 10. Я разговаривал с сетевым инженером Apple, и это изменение произошло после того, как очень много людей подали заявки на Radars и высказались о проблеме на форумах Apple Developer.

Вот очищенная версия ARC, основанная на коде @elsurudo:

- (id)fetchSSIDInfo {
     NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
     NSLog(@"Supported interfaces: %@", ifs);
     NSDictionary *info;
     for (NSString *ifnam in ifs) {
         info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
         NSLog(@"%@ => %@", ifnam, info);
         if (info && [info count]) { break; }
     }
     return info;
}

Это работает для меня на устройстве (не симулятор). Убедитесь, что вы добавили каркас конфигурации системы.

#import <SystemConfiguration/CaptiveNetwork.h>

+ (NSString *)currentWifiSSID {
    // Does not work on the simulator.
    NSString *ssid = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            ssid = info[@"SSID"];
        }
    }
    return ssid;
}

Этот код хорошо работает для получения SSID.

#import <SystemConfiguration/CaptiveNetwork.h>

@implementation IODAppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{


CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(@"Connected at:%@",myDict);
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
NSString * BSSID = [myDictionary objectForKey:@"BSSID"];
NSLog(@"bssid is %@",BSSID);
// Override point for customization after application launch.
return YES;
}

И вот результаты:

Connected at:{
BSSID = 0;
SSID = "Eqra'aOrange";
SSIDDATA = <45717261 27614f72 616e6765>;

}

Если вы используете iOS 12, вам нужно будет сделать дополнительный шаг. Я изо всех сил пытался заставить этот код работать и, наконец, нашел его на сайте Apple: "Важно! Чтобы использовать эту функцию в iOS 12 и более поздних версиях, включите возможность доступа к информации Wi-Fi для вашего приложения в XCode. Когда вы включите эту возможность, XCode автоматически добавляет право доступа к информации Wi-Fi в ваш файл разрешений и идентификатор приложения. " https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo

Вот короткая и сладкая версия Swift.

Не забудьте связать и импортировать Framework:

import UIKit
import SystemConfiguration.CaptiveNetwork

Определите метод:

func fetchSSIDInfo() -> CFDictionary? {
    if let
        ifs = CNCopySupportedInterfaces().takeUnretainedValue() as? [String],
        ifName = ifs.first,
        info = CNCopyCurrentNetworkInfo((ifName as CFStringRef))
    {
        return info.takeUnretainedValue()
    }
    return nil
}

Вызовите метод, когда вам это нужно:

if let
    ssidInfo = fetchSSIDInfo() as? [String:AnyObject],
    ssID = ssidInfo["SSID"] as? String
{
    println("SSID: \(ssID)")
} else {
    println("SSID not found")
}

Как уже упоминалось, это работает только на вашем iDevice. Когда не по WiFi, метод вернет ноль - следовательно, необязательный.

Для iOS 13

Начиная с iOS 13 вашему приложению также требуется доступ к Core Location, чтобы использовать CNCopyCurrentNetworkInfo функция, если он не настроил текущую сеть или не имеет конфигурации VPN:

Итак, это то, что вам нужно (см. Документацию Apple):
- СвяжитеCoreLocation.frameworkбиблиотека
- Добавитьlocation-services как UIRequiredDeviceCapabilitiesКлюч / значение в Info.plist
- ДобавитьNSLocationWhenInUseUsageDescriptionКлюч / значение в Info.plist, описывающий, почему вашему приложению требуется базовое местоположение
- добавьте право "Доступ к информации WiFi" для вашего приложения.

Теперь, в качестве примера Objective-C, сначала проверьте, был ли принят доступ к местоположению, прежде чем читать информацию о сети, используя CNCopyCurrentNetworkInfo:

- (void)fetchSSIDInfo {
    NSString *ssid = NSLocalizedString(@"not_found", nil);

    if (@available(iOS 13.0, *)) {
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
            NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
        } else {
            CLLocationManager* cllocation = [[CLLocationManager alloc] init];
            if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
                [cllocation requestWhenInUseAuthorization];
                usleep(500);
                return [self fetchSSIDInfo];
            }
        }
    }

    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    id info = nil;
    for (NSString *ifnam in ifs) {
        info = (__bridge_transfer id)CNCopyCurrentNetworkInfo(
            (__bridge CFStringRef)ifnam);

        NSDictionary *infoDict = (NSDictionary *)info;
        for (NSString *key in infoDict.allKeys) {
            if ([key isEqualToString:@"SSID"]) {
                ssid = [infoDict objectForKey:key];
            }
        }
    }        
        ...
    ...
}
Другие вопросы по тегам