it-swarm.com.de

iPhone/iPad/OSX: Wie erhalte ich meine IP-Adresse programmgesteuert?

Ich möchte die IP-Adresse meines iPads programmgesteuert abrufen ... Wie kann ich das Netzwerk-Subsystem abfragen, um herauszufinden, was meine IPv4- (und IPv6-) Adressen sind?

Danke . PS: Kann ich IPv6 irgendwie deaktivieren?

118
Wilbur

Mit dem folgenden Code werden alle IPv4- und IPv6-Adressen auf einem iOS- oder OSX-Gerät gefunden. Die erste getIPAddress-Methode fungiert mehr oder weniger als der ältere Code in dieser Antwort: Sie können entweder die eine oder die andere Typadresse vorziehen, und sie bevorzugt immer WIFI gegenüber dem Mobilfunk (offensichtlich können Sie dies ändern).

Interessanterweise kann es ein Wörterbuch aller gefundenen Adressen zurückgeben, Adressen für not up-Schnittstellen oder Adressen, die loopback zugeordnet sind, überspringen. Der vorherige Code sowie andere Lösungen zu diesem Thema werden IPv6 nicht ordnungsgemäß decodieren (inet_ntoa kann nicht damit umgehen). Dies wurde mir von Jens Alfke in einem Apple-Forum gezeigt - die richtige Funktion ist inet_ntop (siehe Manpage und oder auf diesen inet_ntop - Artikel, der ebenfalls von Jens bereitgestellt wird).

Die Wörterbuchschlüssel haben die Form "interface" "/" "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 wurde am 16. Mai 2014 aktualisiert (von lhunath aufgefallener Fehler, siehe Kommentare). Loopback-Adressen wurden 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: Falls der Benutzer ein VPN verwendet (unabhängig von WLAN oder Mobilfunk), wäre der vorherige Code fehlgeschlagen. Jetzt funktioniert es auch mit VPN-Verbindungen. VPN-Verbindungen haben Vorrang vor WiFi und Cell, da das Gerät auf diese Weise gehandhabt wird. Dies sollte sogar für Macs funktionieren, da die VPN-Verbindung auf einem Mac auch IF utun0 verwendet, jedoch nicht getestet wurde.

EDIT3: (9/8/2016) Angesichts der Probleme von @Qiulang (siehe Kommentare) mit dem VPN-Code (den jemand anderes hinzugefügt hat), habe ich es kommentiert. Wenn jemand definitiv weiß, wie ein Benutzer-VPN festgelegt wird, geben Sie bitte einen Kommentar an.

129
David H

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;

} 
88
Raptor

Wäre es einfach, einen der IP-Adressensuchdienste aufzurufen? Ich habe http://ipof.in als Dienst eingerichtet, der die IP-Adresse des Geräts als JSON/XML oder Klartext zurückgibt. Sie finden sie hier

http://ipof.in/json

http://ipof.in/xml

http://ipof.in/txt

Wenn Sie HTTPS möchten, können Sie dieselben URLs mit dem Präfix https verwenden. Der Vorteil ist, dass Sie auch in einem WLAN die öffentliche Adresse erhalten.

18
vivekv

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

iPad: IP-Adresse programmgesteuert erhalten WIRED (nicht per Funk)

3
lundhjem

IP-Adresse mit Swift 3 abrufen:

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
}
3
BHAVIK PANCHAL

Diese Antwort wurde von @ DavidHs Antwort inspiriert. Ich habe einige Probleme behoben, inet_ntop durch getnameinfo ersetzt, was einen saubereren Ansatz ermöglicht. Beachten Sie, dass dies zu einem Wörterbuch führt, das einen Schnittstellennamen einem Array von IP-Adressen zuordnet (eine Schnittstelle kann technisch mehrere IPv4- und IPv6-Adressen haben). 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;
1
lhunath

@ DavidHs Antwort funktioniert gut, 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 verwende vpn nicht, daher habe ich keine Ahnung, warum ich eine utun0/ipv6 hatte. 

--- Aktualisierte ---

Ich debugiere dieses Problem weiter und stellte fest, dass ich auch in anderen 4G-Netzwerken eine falsche vpn-Adresse erhalten kann (ist das iOS-Bug ??),

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

So ist es utun1 NICHT utun0

Ohne herauszufinden, warum ich vpn check einfach fallen lassen muss :(

---- Update ----

Ich habe einen Fehler (28131847) an Apple gemeldet und mit "Nicht alle Utun-Schnittstellen sind für VPN. Es gibt andere Betriebssystemfunktionen, die Utun-Schnittstellen verwenden."

Als ich dann gefragt wurde, wie ich eine gültige VPN-IP-Adresse erhalten kann, war ihre Antwort ziemlich enttäuscht: "Sie können in Einstellungen -> VPN gehen und Ihre VPN-Konfiguration nachsehen, ob das VPN aktiv ist IP-Adresse auch dort zugewiesen. Wir schließen jetzt diesen Fehlerbericht. " :(

---- Aktualisierung 2016/11/04 ----

Ich habe das Problem erneut gelöst und muss die Antwort von @ DavidH weiter ändern, um das Problem 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 bekomme ich die WLAN-IP-Fe80 :: 8dd: 7d92: 4159: 170e, die gefälscht war und die Verbindung fehlgeschlagen ist.

Also habe ich den Code geändert, um zu mögen,

[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;
     }
 } ];
1
Qiulang

Die aktuelle Lösung gibt das en0-Gerät unter OS X nicht zurück. Der folgende Code verwendet System Configuration Framework, um die Schnittstellen abzurufen, und verwendet 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;
}
1
A.Badger

Tolle Lösung für Swift in Diese Datei die alle Details liefert.

In einer meiner App 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 in jeder Anweisung, die ich als optional mit ? verwendet habe, nach nil suchen muss. Daher ist es besser für mich, eine verknüpfte Datei in meiner Klasse zu verwenden. Es fällt mir leicht, jetzt 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 den String für "." analysiert, da die Interface-Klasse in meinem iPhone zwei Schnittstellen für en0-Adresse wie "fb00 ::" und die Adresse wie "101.10.1.1" zurückgibt

0
Max