Jak uzyskać programowy adres IP w systemie iOS / macOS?

124

Chciałbym uzyskać programowo adres IP mojego iPada. Jak mogę wysłać zapytanie do podsystemu sieciowego, aby dowiedzieć się, jakie są moje adresy IPv4 (i IPv6)?

PS: Czy mogę jakoś wyłączyć IPv6?

Wilbur
źródło
14
W odniesieniu do powyższego „PS”, nie wyłączaj programowo protokołu IPv6 na czyimś urządzeniu. To po prostu niegrzeczne.
Jeremy Visser
Nie możesz wyłączyć IPv6. Jest to obowiązkowe. W rzeczywistości Twoja aplikacja na iOS musi obsługiwać IPv6.
Michael Hampton,

Odpowiedzi:

132

Poniższy kod znajduje wszystkie adresy IPv4 i IPv6 na urządzeniu z systemem iOS lub OSX. Pierwsza getIPAddressmetoda działa mniej więcej tak, jak starszy kod w tej odpowiedzi: możesz preferować jeden lub drugi typ adresu i zawsze preferuje WIFI niż komórkową (oczywiście możesz to zmienić).

Co ciekawsze, może zwrócić słownik wszystkich znalezionych adresów, pomijając adresy not upinterfejsów lub adresy powiązane z loopback. Poprzedni kod oraz inne rozwiązania na ten temat nie będą poprawnie dekodować IPv6 (inet_ntoa nie radzi sobie z nimi). Zwrócił na to uwagę Jens Alfke na forum Apple - właściwą funkcją do użycia jest inet_ntop (spójrz na stronę podręcznika systemowego lub zapoznaj się z tym artykułem na temat inet_ntop również dostarczonym przez Jensa.

Klucze słownika mają postać „interfejs” „/” „ipv4 lub ipv6”.

#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>

#define IOS_CELLULAR    @"pdp_ip0"
#define IOS_WIFI        @"en0"
//#define IOS_VPN       @"utun0"
#define IP_ADDR_IPv4    @"ipv4"
#define IP_ADDR_IPv6    @"ipv6"

- (NSString *)getIPAddress:(BOOL)preferIPv4
{
    NSArray *searchArray = preferIPv4 ?
                            @[ /*IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6,*/ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] :
                            @[ /*IOS_VPN @"/" IP_ADDR_IPv6, IOS_VPN @"/" IP_ADDR_IPv4,*/ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;

    NSDictionary *addresses = [self getIPAddresses];
    NSLog(@"addresses: %@", addresses);

    __block NSString *address;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
        {
            address = addresses[key];
            if(address) *stop = YES;
        } ];
    return address ? address : @"0.0.0.0";
}

- (NSDictionary *)getIPAddresses
{
    NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];

    // retrieve the current interfaces - returns 0 on success
    struct ifaddrs *interfaces;
    if(!getifaddrs(&interfaces)) {
        // Loop through linked list of interfaces
        struct ifaddrs *interface;
        for(interface=interfaces; interface; interface=interface->ifa_next) {
            if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
                continue; // deeply nested code harder to read
            }
            const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
            char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
            if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
                NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
                NSString *type;
                if(addr->sin_family == AF_INET) {
                    if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv4;
                    }
                } else {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
                    if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv6;
                    }
                }
                if(type) {
                    NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
                    addresses[key] = [NSString stringWithUTF8String:addrBuf];
                }
            }
        }
        // Free memory
        freeifaddrs(interfaces);
    }
    return [addresses count] ? addresses : nil;
}

EDIT1: Kod zaktualizowany 16 maja 2014 r. (Błąd wskazany przez lhunatha, patrz komentarze). Adresy zwrotne są teraz zwracane, ale możesz łatwo odkomentować test, aby je wykluczyć.

EDIT2: (przez nieznaną osobę): Dalsze ulepszenia 13 marca 2015: W przypadku, gdy użytkownik korzysta z VPN (niezależnie od sieci Wi-Fi lub komórkowej), poprzedni kod nie powiódłby się. Teraz działa nawet z połączeniami VPN. Połączenia VPN mają pierwszeństwo przed Wi-Fi i komórką, ponieważ w ten sposób urządzenie je obsługuje. Powinno to działać nawet na komputerach Mac, ponieważ połączenie VPN na komputerze Mac również używa IF utun0, ale nie zostało przetestowane.

EDIT3: (08.09.2016) Biorąc pod uwagę problemy, których doświadczył @Qiulang (patrz komentarze) z kodem VPN (który dodał ktoś inny), skomentowałem to. Jeśli ktoś definitywnie wie, jak określić VPN użytkownika, prosimy o dołączenie komentarza.

David H.
źródło
jak często addrdostaje NULL? moi użytkownicy czasami otrzymują wartość NULL. czy wiesz, jakie są możliwe powody?
HelmiB
Używam tylko AF_INETdo sprawdzenia. może to być?
HelmiB
1
Nie wiem, ale jest pewne, że Twoi użytkownicy mogą znajdować się w sieci IPV6.
David H
2
To powinna być „właściwa” odpowiedź, ponieważ dotyczy technologii 3G. Dziękuję za aktualizację.
palme
1
Myślę, że to nie działa poprawnie z IPv6. Funkcja inet_ntoa przyjmuje adres IPv4, więc w przypadku, gdy sa_type == AF_INET6, bierzesz adres IPv6 i przesyłasz go na IPv4 i zamieniasz na łańcuch (w zasadzie z 32-bitowego wysokiego 128-bitowego adres.)
Jens Alfke
88

W pliku implementacji .m,

#import <ifaddrs.h>
#import <arpa/inet.h>



// Get IP Address
- (NSString *)getIPAddress {    
    NSString *address = @"error";
    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) {
            if(temp_addr->ifa_addr->sa_family == AF_INET) {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    // Get NSString from C String
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];               
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    // Free memory
    freeifaddrs(interfaces);
    return address;

} 
Raptor
źródło
1
Na liście interfejsów nie pojawia się żaden interfejs en0, więc po prostu zwraca błąd. Nie jestem pewien, czy coś się zmieniło, ale zdecydowanie nie działa dla mnie na iOS 5.x
wuf810
11
Jeśli spojrzysz poniżej, znajdziesz nieco zmodyfikowany kod, który zwraca adres komórki (3G), jeśli WIFI jest wyłączone.
David H
1
Poniższy kod podaje tylko adres IPv4 en0. A co z IPv6? Czy ktoś może podać przykład kodu, jak go odzyskać?
Oded Regev
2
Ten nie znajduje adresu IP, gdy urządzenie nie jest podłączone do Wi-Fi, ale do sieci komórkowej
Mapedd
1
Zmodyfikuj warunek if poniżej if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0"]) To if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0SS] || stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "pdp_ip0"]), aby uzyskać adres IP, gdy urządzenie nie jest podłączone do WIFI ..
Raju
4

Wiele istniejących rozwiązań uwzględnia tylko interfejsy bezprzewodowe, które nie będą działać w przypadku połączeń przewodowych za pośrednictwem adaptera Ethernet (tj. Bez Wi-Fi lub 3G); zobacz to nowsze rozwiązanie, które uwzględnia również adresy IP uzyskiwane za pośrednictwem interfejsów przewodowych.

iPad: Jak uzyskać adres IP programowo WIRED (nie przez sieć bezprzewodową)

lundhjem
źródło
Doskonały! Wkrótce dodam to do mojego kodu powyżej. Co to jest en1 - czy wiesz?
David H
1
Nie, ale zauważyłem, że to nie rodzina AF_INET jak inne interfejsy en
lundhjem
1
@DavidH ostatnio adres IP Ethernet pojawiał się na en1 na niektórych iPadach, więc wygląda na to, że jest on również członkiem rodziny AF_INET w niektórych sytuacjach. Powinieneś również sprawdzić ten interfejs.
lundhjem
3

Uzyskaj adres IP za pomocą Swift 3:

func getIPAddress() -> String {
    var address: String = "error"

    var interfaces: ifaddrs? = nil

    var temp_addr: ifaddrs? = nil
    var success: Int = 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 != nil {
            if temp_addr?.ifa_addr?.sa_family == AF_INET {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if (String(utf8String: temp_addr?.ifa_name) == "en0") {
                    // Get NSString from C String
                    address = String(utf8String: inet_ntoa((temp_addr?.ifa_addr as? sockaddr_in)?.sin_addr))
                }
            }
            temp_addr = temp_addr?.ifa_next
        }
    }
        // Free memory
    freeifaddrs(interfaces)
    return address
}
BHAVIK PANCHAL
źródło
1

Bieżące rozwiązanie nie zwraca urządzenia en0 w systemie OS X, poniższy kod używa struktury konfiguracji systemu do pobrania interfejsów, a następnie używa standardowych funkcji języka C w celu uzyskania adresu IP.

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define IFT_ETHER 0x6

#include <SystemConfiguration/SCDynamicStore.h>

+(void)getInterfaces
{
    SCDynamicStoreRef storeRef = SCDynamicStoreCreate(NULL, (CFStringRef)@"FindCurrentInterfaceIpMac", NULL, NULL);
    CFPropertyListRef global = SCDynamicStoreCopyValue (storeRef,CFSTR("State:/Network/Interface"));
    id primaryInterface = [(__bridge NSDictionary *)global valueForKey:@"Interfaces"];

    for (NSString* item in primaryInterface)
    {
        if(get_iface_address([item UTF8String]))
        {
            NSString *ip = [NSString stringWithUTF8String:get_iface_address([item UTF8String])];
            NSLog(@"interface: %@ - %@",item,ip);
        } else
            NSLog(@"interface: %@",item);
    }
}

static char * get_iface_address (char *interface)
{
    int sock;
    uint32_t ip;
    struct ifreq ifr;
    char *val;

    if (!interface)
        return NULL;

    /* determine UDN according to MAC address */
    sock = socket (AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
    {
        perror ("socket");
        return NULL;
    }

    strcpy (ifr.ifr_name, interface);
    ifr.ifr_addr.sa_family = AF_INET;

    if (ioctl (sock, SIOCGIFADDR, &ifr) < 0)
    {
        perror ("ioctl");
        close (sock);
        return NULL;
    }

    val = (char *) malloc (16 * sizeof (char));
    ip = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
    ip = ntohl (ip);
    sprintf (val, "%d.%d.%d.%d",
             (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);

    close (sock);

    return val;
}
A.Badger
źródło
Niestety, SCDynamicStoreCreate jest niedostępne na iOS, tak jak: SCDynamicStoreCopyValue
Alex Zavatone
1

Ta odpowiedź została zainspirowana odpowiedzią @ DavidH. Poprawiłem kilka kwestii, otrzymuje inet_ntopz getnameinfoco umożliwia podejście czystsze. Zauważ, że daje to słownik, który mapuje nazwę interfejsu na tablicę adresów IP (interfejs może mieć wiele powiązanych z nim adresów IPv4 i IPv6). Nie rozróżnia IPv4 i IPv6:

  // Get all our interface addresses.
  struct ifaddrs *ifAddresses;
  if (getifaddrs( &ifAddresses ) != 0) {
    NSLog( @"Couldn't get interface addresses: %d", errno );
    return nil;
  }

  int error;
  char host[MAX( INET_ADDRSTRLEN, INET6_ADDRSTRLEN )];
  _ipAddressesByInterface = [NSMutableDictionary dictionaryWithCapacity:8];

  for (struct ifaddrs *ifAddress = ifAddresses; ifAddress; ifAddress = ifAddress->ifa_next) {
    if (!(ifAddress->ifa_flags & IFF_UP) || (ifAddress->ifa_flags & IFF_LOOPBACK))
      // Ignore interfaces that aren't up and loopback interfaces.
      continue;

    if (ifAddress->ifa_addr->sa_family != AF_INET && ifAddress->ifa_addr->sa_family != AF_INET6)
      // Ignore non-internet addresses.
      continue;

    if ((error = getnameinfo( ifAddress->ifa_addr, ifAddress->ifa_addr->sa_len, host, sizeof( host ), NULL, 0, NI_NUMERICHOST )) != noErr) {
      // Couldn't to format host name for this address.
      NSLog( @"Couldn't resolve host name for address: %s", gai_strerror( error ) );
      continue;
    }

    NSString *ifName = [NSString stringWithCString:ifAddress->ifa_name encoding: NSUTF8StringEncoding];
    NSMutableArray *ifIpAddresses = _ipAddressesByInterface[ifName];
    if (!ifIpAddresses)
      ifIpAddresses = _ipAddressesByInterface[ifName] = [NSMutableArray arrayWithCapacity:2];
    [ifIpAddresses addObject:[NSString stringWithCString:host encoding: NSUTF8StringEncoding]];
  }

  freeifaddrs( ifAddresses );
  return _ipAddressesByInterface;
lhunath
źródło
Nigdy nie widziałem strf- czy jest to funkcja pomocnicza, którą masz w swoim kodzie?
David H
Tak, przepraszam, w powyższym kodzie jest kilku pomocników. erri strf. Można po prostu zastąpić strfz -stringWithCString:encoding:. errjest NSLogopakowaniem, które wypisuje również plik i wiersz. github.com/Lyndir/Pearl/blob/master/Pearl/…
lhunath
1

@ DavidH Odpowiedź działa dobrze, dopóki nie otrzymałem tego wyniku z jakiejś sieci komórkowej 4G:

{
    "lo0/ipv4" = "127.0.0.1";
    "lo0/ipv6" = "fe80::1";
    "pdp_ip0/ipv4" = "10.132.76.168";
    "utun0/ipv6" = "fe80::72c3:e25e:da85:b730";
}

Nie używam VPN, więc nie mam pojęcia, dlaczego mam utun0 / ipv6.

--- Zaktualizowano ---

Dalej debugowałem ten problem i stwierdziłem, że mogę uzyskać fałszywy adres VPN nawet w innych sieciach 4G (czy to błąd iOS?),

{
    ""awdl0/ipv6"" = ""fe80::c018:9fff:feb2:988"";
    ""en0/ipv6"" = ""fe80::181a:2e43:f91b:db2b"";
    ""lo0/ipv4"" = ""127.0.0.1"";
    ""lo0/ipv6"" = ""fe80::1"";
    ""pdp_ip0/ipv4"" = ""10.48.10.210"";
    ""utun0/ipv4"" = ""192.168.99.2"";
}

Gdybym użył VPN, otrzymam to:

{
    "lo0/ipv4" = "127.0.0.1";
    "lo0/ipv6" = "fe80::1";
    "pdp_ip0/ipv4" = "10.49.187.23";
    "utun0/ipv6" = "fe80::5748:5b5d:2bf0:658d";
    "utun1/ipv4" = "192.168.99.2"; //the real one
}

Więc to jest utun1 NIE utun0

Bez zastanawiania się, dlaczego będę musiał po prostu zrezygnować z czeku VPN :(

---- aktualizacja ----

Zgłosiłem błąd (28131847) do Apple i odpowiedziałem: „Nie wszystkie interfejsy utun są przeznaczone dla sieci VPN. Istnieją inne funkcje systemu operacyjnego, które używają interfejsów utun”.

Ale kiedy zapytałem, jak uzyskać prawidłowy adres IP VPN, ich odpowiedź była raczej rozczarowana: „Możesz przejść do Ustawienia -> VPN i spojrzeć na konfigurację VPN, aby sprawdzić, czy VPN jest aktywny. W niektórych przypadkach możesz zobaczyć przypisany adres IP również tam. Zamykamy ten raport o błędzie. " :(

---- aktualizacja 2016/11/04 ----

Znów napotykam problem i muszę dalej zmodyfikować odpowiedź @ DavidH, aby to naprawić:

Byłem w sieci 4G i dostałem ten adres:

addresses: {
    "awdl0/ipv6" = "fe80::98fd:e6ff:fea9:3afd";
    "en0/ipv6" = "fe80::8dd:7d92:4159:170e";
    "lo0/ipv4" = "127.0.0.1";
    "lo0/ipv6" = "fe80::1";
    "pdp_ip0/ipv4" = "10.37.212.102";
    "utun0/ipv6" = "fe80::279c:ea56:a2ef:d128";
}

Z jego oryginalną odpowiedzią otrzymam wifi IP fe80 :: 8dd: 7d92: 4159: 170e, które było fałszywe i połączenie nie powiodło się.

Więc zmodyfikowałem kod, by polubić,

[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
 {
     if ((internetReach.isReachableViaWiFi && [key hasPrefix:IOS_WIFI]) ||
         (internetReach.isReachableViaWWAN && [key hasPrefix:IOS_CELLULAR])) {
         address = addresses[key];
         if(address) *stop = YES;
     }
 } ];
Qiulang
źródło
Napisałem na wewnętrznych forach o problemie z VPN, inżynier Apple zaoferował pomoc. Chcę, żebyś skontaktował się ze mną offline dhoerl pod adresem mac dot com
David H,
1

Świetne rozwiązanie dla szybkiego w tym pliku, który zawiera wszystkie szczegóły.

W jednej z moich aplikacji muszę pobrać adres IP Wi-Fi. Użyłem odpowiedzi powyżej, w swift 3 w ten sposób:

let WIFI_IF = "en0"
let UNKNOWN_IP_ADDRESS = ""
var addresses: [AnyHashable: Any] = ["wireless": UNKNOWN_IP_ADDRESS, "wired": UNKNOWN_IP_ADDRESS, "cell": UNKNOWN_IP_ADDRESS]
var interfaces: UnsafeMutablePointer<ifaddrs>? = nil
var temp_addr: UnsafeMutablePointer<ifaddrs>? = nil
var success: Int = 0
success = Int(getifaddrs(&interfaces))
if success == 0 {
   temp_addr = interfaces
   while temp_addr != nil {
      if temp_addr?.pointee.ifa_addr == nil {
           continue
      }
      if temp_addr?.pointee.ifa_addr.pointee.sa_family == UInt8(AF_INET) {
         if (String(utf8String: (temp_addr?.pointee.ifa_name)!) == WIFI_IF) {
             addresses["wireless"] = String(utf8String: inet_ntoa(((temp_addr?.pointee.ifa_addr as? sockaddr_in)?.sin_addr)!))
         }
      }
      temp_addr = temp_addr?.pointee.ifa_next
   }
}

W tym kodzie ulega awarii, ponieważ muszę sprawdzić nilw każdej instrukcji, której użyłem jako opcjonalne ?. Więc lepiej jest dla mnie użyć podanego połączonego pliku w mojej klasie. Łatwo mi teraz sprawdzić:

class func getWifiIPAddress() -> String {
    var wifiIp = ""

    let WIFI_IF = "en0"
    let allInterface = Interface.allInterfaces()

    for interf in allInterface {
        if interf.name == WIFI_IF {
            if let address = interf.address {

                if address.contains(".") {
                wifiIp = address
                break
                }
            }
        }
    }

    return wifiIp
}

Przeanalizowałem ciąg znaków, "."ponieważ klasa interfejsu zwraca dwa interfejsy w moim iPhonie dla en0adresu takiego jak „fb00 ::” i adresu takiego jak „101.10.1.1”

Maks
źródło
1
Link do potencjalnego rozwiązania jest zawsze mile widziany, ale dodaj kontekst do niego, aby inni użytkownicy mieli pojęcie, co to jest i dlaczego się tam znajduje. Zawsze cytuj najbardziej odpowiednią część ważnego linku, na wypadek gdyby strona docelowa była nieosiągalna lub została trwale wyłączona. Weź pod uwagę, że fakt, że niewiele więcej niż link do strony zewnętrznej jest tylko linkiem do zewnętrznej witryny, jest możliwym powodem, dlaczego i jak są usuwane niektóre odpowiedzi? .
Paul Roub
1
Ok zaktualizuje się odpowiednio, ponieważ jest to dla mnie pomocne dla lepszego zrozumienia
Max
1

Stworzyłem prosty plik do uzyskania adresu IP. Oparłem to rozwiązanie na odpowiedziach @ lundhjem, @ DavidH i @ Ihunath. Uwzględnia połączenia przewodowe. Jednak nie uwzględniłem VPN w tym rozwiązaniu.

PCNetwork.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface PCNetwork : NSObject
+ (NSString *)getIPAddress; // Prefers IPv4
+ (NSString *)getIPAddress:(BOOL)preferIPv4;
+ (NSDictionary *)getIPAddresses;
@end

NS_ASSUME_NONNULL_END

PCNetwork. M

#import "PCNetwork.h"
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>

#define IP_UNKNOWN          @"0.0.0.0"
#define IP_ADDR_IPv4        @"ipv4"
#define IP_ADDR_IPv6        @"ipv6"

@implementation PCNetwork
#pragma mark - IP
+ (NSString *)getIPAddress {
    return [self getIPAddress:YES];
}

+ (NSString *)getIPAddress:(BOOL)preferIPv4 {
    NSArray *searchArray = [self getAllIFSearchArray:preferIPv4];
    NSDictionary *addresses = [self getIPAddresses];
    DLog(@"addresses: %@", addresses);

    __block NSString *address = nil;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
         address = addresses[key];
         if(address) *stop = YES;
     }];
    return address ?: IP_UNKNOWN;
}

+ (NSDictionary *)getIPAddresses {
    NSMutableDictionary *addresses = [NSMutableDictionary dictionary];
    struct ifaddrs *interfaces;
    BOOL success = !getifaddrs(&interfaces); // Retrieve the current interfaces : returns 0 on success
    if (success) {
        struct ifaddrs *temp_interface;
        for (temp_interface = interfaces; temp_interface; temp_interface = temp_interface->ifa_next) { // Loop through linked list of interfaces
            if (!(temp_interface->ifa_flags & IFF_UP) || (temp_interface->ifa_flags & IFF_LOOPBACK)) { // Ignore interfaces that aren't up and loopback interfaces.
                continue;
            }

            if (!temp_interface->ifa_addr) {
                continue;
            }

            const struct sockaddr_in *temp_addr = (const struct sockaddr_in*)temp_interface->ifa_addr;
            if (temp_addr->sin_family == AF_INET || temp_addr->sin_family == AF_INET6) {
                char addrBuf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
                NSString *name = [NSString stringWithUTF8String:temp_interface->ifa_name];
                NSString *type = nil;
                if (temp_addr->sin_family == AF_INET) {
                    if (inet_ntop(AF_INET, &temp_addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv4;
                    }
                } else {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)temp_interface->ifa_addr; // AF_INET6
                    if (inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv6;
                    }
                }

                if (type) {
                    NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
                    addresses[key] = [NSString stringWithUTF8String:addrBuf];
                }
            }
        }
        freeifaddrs(interfaces); // Free memory
    }
    return addresses.count ? addresses.copy : nil;
}

#pragma mark - Inter Frame Spacing
+ (NSArray *)getAllIFSearchArray:(BOOL)preferIPv4 {
    NSArray *KNOWN_WIFI_IFS = @[@"en0"];
    NSArray *KNOWN_WIRED_IFS = @[@"en1",@"en2",@"en3",@"en4"];
    NSArray *KNOWN_CELL_IFS = @[@"pdp_ip0",@"pdp_ip1",@"pdp_ip2",@"pdp_ip3"];

    NSMutableArray *searchArray = [NSMutableArray array];

    // Add wifi
    [searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_WIFI_IFS preferIPv4:preferIPv4]];

    // Add cell
    [searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_CELL_IFS preferIPv4:preferIPv4]];

    // Add wired
    [searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_WIRED_IFS preferIPv4:preferIPv4]];

    return searchArray.copy;
}

+ (NSArray *)getIFSearchArrayWith:(NSArray *)iFList preferIPv4:(BOOL)preferIPv4 {
    NSMutableArray *searchArray = [NSMutableArray array];
    for (NSString *iFType in iFList) {
        if (preferIPv4) {
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv4]];
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv6]];
        } else {
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv6]];
            [searchArray addObject:[NSString stringWithFormat:@"%@/%@", iFType, IP_ADDR_IPv4]];
        }
    }
    return searchArray.copy;
}

@end
cessmestreet
źródło
0

w iOS 13.4.1 nie działa dla mnie. używam tego naprawić to.

+ (NSString *)getIPAddress{
    NSArray *searchArray =
    @[ IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_4_3G @"/" IP_ADDR_IPv4, IOS_4_3G @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6];

    __block NSDictionary *addresses = [self getIPAddressArray];

    __block NSString *address;
    [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
    {
         address = addresses[key];
         if ([key rangeOfString:@"ipv6"].length > 0  && ![[NSString stringWithFormat:@"%@",addresses[key]] hasPrefix:@"(null)"] ) {
             if ( ![addresses[key] hasPrefix:@"fe80"]) {
                 //     isIpv6 = YES;
                 *stop = YES;
              }
         }else{
             if([self isValidatIP:address]) {
                 *stop = YES;
             }
         }
     } ];
    return address ? address : @"error";
}
+ (NSString *)getIPType{
    NSString *ipAddress = [self getIPAddress];
    if ([self isValidatIP:ipAddress]) {
        return @"04";//ipv4
    }else{
        return @"06";//ipv6
    }
}
+ (NSDictionary *)getIPAddressArray{
    NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
    // retrieve the current interfaces - returns 0 on success
    struct ifaddrs *interfaces;
    if(!getifaddrs(&interfaces)) {
        // Loop through linked list of interfaces
        struct ifaddrs *interface;
        for(interface=interfaces; interface; interface=interface->ifa_next) {
            if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
                continue; // deeply nested code harder to read
            }
            const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
            char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
            if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
                NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
                NSString *type;
                if(addr->sin_family == AF_INET) {
                    if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv4;
                    }
                } else {
                    const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
                    if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
                        type = IP_ADDR_IPv6;
                    }
                }
                if(type) {
                    NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
                    addresses[key] = [NSString stringWithUTF8String:addrBuf];
                }
            }
        }
        // Free memory
        freeifaddrs(interfaces);
    }
    return [addresses count] ? addresses : nil;
}
+ (BOOL)isValidatIP:(NSString *)ipAddress {
    if (ipAddress.length == 0) {
        return NO;
    }
    NSString *urlRegEx = @"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";

    NSError *error;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:urlRegEx options:0 error:&error];

    if (regex != nil) {
        NSTextCheckingResult *firstMatch=[regex firstMatchInString:ipAddress options:0 range:NSMakeRange(0, [ipAddress length])];

        if (firstMatch) {
            NSRange resultRange = [firstMatch rangeAtIndex:0];
            NSString *result=[ipAddress substringWithRange:resultRange];
            //输出结果
            NSLog(@"%@",result);
            return YES;
        }
    }
    return NO;
}
yaoning
źródło