Wie erhalte ich meine IP-Adresse programmgesteuert unter iOS / macOS?

124

Ich möchte die IP-Adresse meines iPad programmgesteuert erhalten. Wie kann ich das Netzwerksubsystem abfragen, um herauszufinden, wie meine IPv4- (und IPv6-) Adressen lauten?

PS: Kann ich IPv6 irgendwie deaktivieren?

Wilbur
quelle
14
Deaktivieren Sie IPv6 auf dem Gerät einer anderen Person nicht programmgesteuert. Es ist einfach unhöflich.
Jeremy Visser
Sie können IPv6 nicht deaktivieren. Es zwingend erforderlich ist. Tatsächlich muss Ihre iOS-App IPv6 unterstützen.
Michael Hampton

Antworten:

132

Der folgende Code findet alle IPv4- und IPv6-Adressen auf einem iOS- oder OSX-Gerät. Die erste getIPAddressMethode fungiert mehr oder weniger als der ältere Code in dieser Antwort: Sie können entweder die eine oder die andere Typadresse bevorzugen, und sie bevorzugt immer WIFI gegenüber Mobilfunk (offensichtlich können Sie dies ändern).

Interessanterweise kann ein Wörterbuch aller gefundenen Adressen zurückgegeben werden, wobei Adressen für not upSchnittstellen oder damit verbundene Adressen übersprungen werden loopback. Der vorherige Code sowie andere Lösungen zu diesem Thema dekodieren IPv6 nicht ordnungsgemäß (inet_ntoa kann sie nicht verarbeiten). Dies wurde mir von Jens Alfke in einem Apple-Forum mitgeteilt - die richtige Funktion ist inet_ntop (siehe Manpage, oder lesen Sie diesen Artikel von inet_ntop, der ebenfalls von Jens bereitgestellt wird.

Die Wörterbuchschlüssel haben die Form "Schnittstelle" / "IPv4 oder 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: Code aktualisiert am 16. Mai 2014 (Fehler von lhunath, siehe Kommentare). Loopback-Adressen werden jetzt zurückgegeben, aber Sie können den Test leicht auskommentieren, um sie selbst auszuschließen.

EDIT2: (von einer unbekannten Person): Weiter verbessert 13. März 2015: Wenn der Benutzer ein VPN verwendet (unabhängig von WLAN oder Mobilfunk), wäre der vorherige Code fehlgeschlagen. Jetzt funktioniert es sogar mit VPN-Verbindungen. VPN-Verbindungen haben Vorrang vor WLAN und Mobilfunk, da das Gerät so damit umgeht. Dies sollte sogar für Macs funktionieren, da die VPN-Verbindung auf einem Mac ebenfalls IF utun0 verwendet, jedoch nicht getestet wurde.

EDIT3: (08.09.2016) Angesichts der Probleme von @Qiulang (siehe Kommentare) mit dem VPN-Code (den jemand anderes hinzugefügt hat) habe ich ihn auskommentiert. Wenn jemand definitiv weiß, wie man ein Benutzer-VPN angibt, meldet sich bitte mit einem Kommentar.

David H.
quelle
Wie oft addrwird NULL? Meine Benutzer erhalten gelegentlich NULL. Weißt du was mögliche Gründe sind?
HelmiB
Ich benutze nur AF_INET, um zu überprüfen. könnte das sein?
HelmiB
1
Ich weiß es nicht, aber es ist sicher möglich, dass sich Ihre Benutzer in einem IPV6-Netzwerk befinden.
David H
2
Dies sollte die "richtige" Antwort sein, da es mit 3G komplett ist. Danke für das Update.
Palme
1
Ich denke nicht, dass dies mit IPv6 richtig funktioniert. Die Funktion inet_ntoa verwendet eine IPv4-Adresse. In dem Fall, in dem sa_type == AF_INET6 ist, nehmen Sie die IPv6-Adresse und geben sie in IPv4 um und wandeln sie in eine Zeichenfolge um (im Grunde genommen aus den hohen 32 Bit des 128-Bit) Adresse.)
Jens Alfke
88

In Ihrer Implementierungsdatei .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;

} 
Raubvogel
quelle
1
In der Schnittstellenliste wird keine en0-Schnittstelle angezeigt, daher wird nur ein Fehler zurückgegeben. Ich
bin
11
Wenn Sie unten nachsehen, finden Sie einen leicht geänderten Code, der die Zellenadresse (3G) zurückgibt, wenn WIFI deaktiviert ist.
David H
1
Der folgende Code gibt nur die IPv4-Adresse von en0 an. Was ist mit IPv6? Kann jemand ein Codebeispiel geben, wie man es abruft?
Oded Regev
2
Dieser findet keine IP-Adresse, wenn das Gerät nicht mit WLAN verbunden ist, sondern mit dem Cellluar-Netzwerk
Mapedd
1
Ändern Sie die folgende if-Bedingung if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0"]) To if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "enS" || stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "pdp_ip0"]), um die IP zu erhalten, wenn das Gerät nicht mit WIFI verbunden ist.
Raju
4

Viele vorhandene Lösungen berücksichtigen nur drahtlose Schnittstellen, die für kabelgebundene Verbindungen über einen Ethernet-Adapter (dh kein WLAN oder 3G) nicht funktionieren. Sehen Sie sich diese neuere Lösung an, die IP-Adressen berücksichtigt, die auch über kabelgebundene Schnittstellen erhalten werden.

iPad: So erhalten Sie die IP-Adresse programmgesteuert VERDRAHTET (nicht über WLAN)

lundhjem
quelle
Ausgezeichnet! Ich werde dies bald zu meinem obigen Code hinzufügen. Was ist en1 - weißt du?
David H
1
Ich weiß nicht, aber ich habe bemerkt, dass es nicht AF_INET-Familie wie die anderen en-Schnittstellen ist
lundhjem
1
@DavidH Vor kurzem wurde die Ethernet-IP auf bestimmten iPads auf en1 angezeigt, sodass sie in bestimmten Szenarien anscheinend auch zur AF_INET-Familie gehört. Sie sollten auch diese Schnittstelle überprüfen.
Lundhjem
3

Holen Sie sich die IP-Adresse mit 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
quelle
1

Die aktuelle Lösung gibt das en0-Gerät unter OS X nicht zurück. Der folgende Code verwendet das System Configuration Framework, um die Schnittstellen abzurufen, und verwendet dann Standard-C-Funktionen, um die IP-Adresse abzurufen.

#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
quelle
Leider ist SCDynamicStoreCreate unter iOS nicht verfügbar, ebenso wie: SCDynamicStoreCopyValue
Alex Zavatone
1

Diese Antwort wurde von der Antwort von @ DavidH inspiriert. Ich reparierte einige Probleme, ersetzt inet_ntopmit getnameinfodenen ermöglicht eine sauberere Ansatz. Beachten Sie, dass dies ein Wörterbuch ergibt, das einen Schnittstellennamen einem Array von IP-Adressen zuordnet (einer Schnittstelle können technisch gesehen mehrere IPv4- und IPv6-Adressen zugeordnet sein). Es wird nicht zwischen IPv4 und IPv6 unterschieden:

  // 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
quelle
Ich habe noch nie gesehen strf- ist das eine Hilfsfunktion, die Sie in Ihrem Code haben?
David H
Ja, tut mir leid, der obige Code enthält einige Helfer. errund strf. Sie können einfach ersetzen strfmit -stringWithCString:encoding:. errist ein NSLogWrapper, der auch die Datei und die Zeile ausgibt. github.com/Lyndir/Pearl/blob/master/Pearl/…
lhunath
1

Die Antwort von @ DavidH funktioniert einwandfrei, bis ich dieses Ergebnis von einem 4G-Mobilfunknetz erhalten habe:

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

Ich benutze kein VPN, daher habe ich keine Ahnung, warum ich ein utun0 / ipv6 hatte.

--- Aktualisiert ---

Ich habe dieses Problem weiter behoben und festgestellt, dass ich auch in anderen 4G-Netzwerken eine gefälschte VPN-Adresse erhalten kann (ist das ein iOS-Fehler?).

{
    ""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"";
}

Wenn ich VPN verwendet habe, bekomme ich Folgendes:

{
    "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
}

Es ist also utun1 NICHT utun0

Ohne herauszufinden, warum ich nur den VPN-Check fallen lassen muss :(

---- Update ----

Ich habe einen Fehler (28131847) bei Apple ausgelöst und geantwortet: "Nicht alle Utun-Schnittstellen sind für VPN. Es gibt andere Betriebssystemfunktionen, die Utun-Schnittstellen verwenden."

Als ich dann fragte, wie man eine gültige VPN-IP-Adresse erhält, war ihre Antwort ziemlich enttäuscht: "Sie können unter Einstellungen -> VPN Ihre VPN-Konfiguration überprüfen, um festzustellen, ob das VPN aktiv ist. In einigen Fällen können Sie die sehen Dort wurde auch die IP-Adresse zugewiesen. Wir schließen jetzt diesen Fehlerbericht. " :((

---- Update 2016/11/04 ----

Ich habe das Problem erneut festgestellt und muss die Antwort von @ DavidH weiter ändern, um es zu beheben:

Ich war im 4G-Netzwerk und habe folgende Adresse erhalten:

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";
}

Mit seiner ursprünglichen Antwort erhalte ich die WLAN-IP fe80 :: 8dd: 7d92: 4159: 170e, die gefälscht war und die Verbindung fehlgeschlagen ist.

Also habe ich den Code so geändert, dass er gefällt.

[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
quelle
Ich habe in internen Foren Informationen zum VPN-Problem veröffentlicht, die Apple Engineer angeboten hat, um zu helfen. Sie müssen mich offline kontaktieren dhoerl bei mac dot com
David H
1

Tolle Lösung für Swift in Diese Datei, die alle Details enthält.

In einer meiner Apps muss ich die WLAN-IP-Adresse abrufen. Ich habe die obigen Antworten in Swift 3 wie folgt verwendet:

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
   }
}

In diesem Code stürzt es ab, weil ich nilin jeder Anweisung, mit der ich optional gearbeitet habe, nachsehen muss ?. Daher ist es für mich besser, eine bestimmte verknüpfte Datei in meiner Klasse zu verwenden. Es fällt mir jetzt leicht, Folgendes zu überprüfen:

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
}

Ich habe eine Zeichenfolge analysiert, "."da die Schnittstellenklasse zwei Schnittstellen in meinem iPhone für en0Adressen wie "fb00 ::" und Adressen wie "101.10.1.1" zurückgibt.

Max
quelle
1
Ein Link zu einer möglichen Lösung ist immer willkommen. Fügen Sie jedoch einen Kontext um den Link hinzu, damit Ihre Mitbenutzer eine Vorstellung davon haben, was es ist und warum es dort ist. Zitieren Sie immer den relevantesten Teil eines wichtigen Links, falls die Zielwebsite nicht erreichbar ist oder dauerhaft offline geht. Berücksichtigen Sie, dass kaum mehr als ein Link zu einer externen Website ein möglicher Grund dafür ist, warum und wie einige Antworten gelöscht werden. .
Paul Roub
1
Ok wird entsprechend aktualisiert, da es für mich zum besseren Verständnis hilfreich ist
Max
1

Ich habe eine einfache Datei erstellt, um die IP-Adresse zu erhalten. Ich habe diese Lösung auf die Antworten von @ lundhjem, @ DavidH und @ Ihunath gestützt. Es werden Kabelverbindungen berücksichtigt. Ich habe VPN jedoch nicht in diese Lösung aufgenommen.

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
quelle
0

in iOS 13.4.1 funktioniert bei mir nicht. Ich benutze dieses Update.

+ (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
quelle