web-dev-qa-db-fra.com

iPhone/iPad/OSX: Comment obtenir mon adresse IP par programme?

Je souhaite obtenir l'adresse IP de mon iPad par programmation . Comment interroger le sous-système de réseau pour connaître les adresses IPv4 (et IPv6)?

Merci . PS: Puis-je désactiver IPv6 en quelque sorte?

118
Wilbur

Le code suivant trouve toutes les adresses IPv4 et IPv6 sur un périphérique iOS ou OSX. La première méthode getIPAddress agit plus ou moins comme l’ancien code de cette réponse: vous pouvez préférer l’une ou l’autre adresse de type, et elle préfère toujours le WIFI au cellulaire (vous pouvez évidemment le changer).

Plus intéressant, il peut renvoyer un dictionnaire de toutes les adresses trouvées, en ignorant les adresses pour les interfaces not up ou les adresses associées à loopback. Le code précédent ainsi que d'autres solutions sur ce sujet ne décoderont pas correctement IPv6 (inet_ntoa ne peut pas les gérer). Ceci m'a été signalé par Jens Alfke sur un forum Apple - la fonction appropriée à utiliser est inet_ntop (consultez la page de manuel et consultez l’article inet_ntop également fourni par Jens.

Les clés du dictionnaire ont la forme "interface" "/" "ipv4 ou 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 mis à jour le 16 mai 2014 (bug signalé par lhunath, voir les commentaires). Les adresses en boucle sont maintenant renvoyées, mais il est facile pour vous de supprimer le commentaire pour les exclure vous-même.

EDIT2: (par une personne inconnue): Amélioration ultérieure 13 mars 2015: Si l'utilisateur utilise un VPN (sans fil ou cellulaire), le code précédent aurait échoué. Maintenant, cela fonctionne même avec des connexions VPN. Les connexions VPN ont la priorité sur WiFi et Cell parce que c'est ainsi que l'appareil les gère. Cela devrait même fonctionner pour les Mac car la connexion VPN sur un Mac utilise également IF siun0 mais n'est pas testée.

EDIT3: (08/09/2016) Compte tenu des problèmes rencontrés par @Qiulang (voir les commentaires) avec le code VPN (ajouté par une autre personne), je l'ai commenté. Si quelqu'un sait définitivement comment spécifier un utilisateur VPN, veuillez ajouter un commentaire.

129
David H

Dans votre fichier d'implémentation .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

Serait-il facile d'appeler simplement l'un des services de recherche d'adresse IP? J'ai configuré http://ipof.in en tant que service qui renvoie l'adresse IP du périphérique au format JSON/XML ou en texte brut. Vous pouvez les trouver ici

http://ipof.in/json

http://ipof.in/xml

http://ipof.in/txt

Si vous voulez HTTPS, vous pouvez utiliser les mêmes URL avec le préfixe https. L'avantage est que même si vous êtes en Wifi, vous aurez l'adresse publique.

18
vivekv

De nombreuses solutions existantes ne considèrent que les interfaces sans fil, ce qui ne fonctionnera pas pour les connexions filaires via un adaptateur Ethernet (c'est-à-dire sans Wifi ou 3G); voir cette solution plus récente qui considère également les adresses IP obtenues via des interfaces filaires.

iPad: Comment obtenir une adresse IP par programme WIRED (pas via sans fil)

3
lundhjem

Obtenir une adresse IP en utilisant 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
}
3
BHAVIK PANCHAL

La réponse de @ DavidH a inspiré cette réponse. J'ai corrigé quelques problèmes, en remplaçant inet_ntop par getnameinfo, ce qui permet une approche plus propre. Notez que cela génère un dictionnaire qui mappe un nom d'interface sur un tableau d'adresses IP (techniquement, une interface peut être associée à plusieurs adresses IPv4 et IPv6). Il ne fait pas la distinction entre IPv4 et 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;
1
lhunath

@ La réponse de DavidH fonctionne bien jusqu'à ce que je reçoive ce résultat d'un réseau cellulaire 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";
}

Je n'utilise pas vpn, donc je ne sais pas pourquoi j'ai eu utun0/ipv6. 

--- Mis à jour ---

J'ai ensuite déboguer ce problème et constaté que je pouvais obtenir une fausse adresse vpn même dans d'autres réseaux 4G (est-ce que ce bug 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"";
}

Si j'utilisais vpn, j'obtiendrais ceci:

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

Donc c'est utun1 NOT utun0

Sans comprendre pourquoi je vais juste devoir laisser tomber vpn check :(

---- mettre à jour ----

J'ai signalé un bogue (28131847) à Apple et lui ai répondu que "toutes les interfaces utun ne sont pas conçues pour le VPN. Il existe d'autres fonctionnalités du système d'exploitation qui utilisent des interfaces utun."

Mais quand j’ai demandé comment obtenir une adresse IP vpn valide, leur réponse a été plutôt déçue: "Vous pouvez aller dans Paramètres -> VPN et regarder votre configuration VPN pour voir si le VPN est actif. Dans certains cas, vous pouvez voir le l’adresse IP attribuée ici également. Nous fermons maintenant ce rapport de bogue. " :(

---- mise à jour 2016/11/04 ----

Je rencontre à nouveau le problème et je dois modifier davantage la réponse de @ DavidH pour résoudre le problème:

J'étais en réseau 4G et j'ai eu cette adresse:

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

Avec sa réponse originale, je vais obtenir l'adresse IP wifi fe80 :: 8dd: 7d92: 4159: 170e, qui était un faux et la connexion a échoué.

Alors j'ai modifié le code pour aimer,

[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

La solution actuelle ne renvoie pas le périphérique en0 sous OS X, le code suivant utilise System Configuration Framework pour obtenir les interfaces, puis utilise les fonctions C standard pour obtenir l'adresse 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;
}
1
A.Badger

Excellente solution pour Swift dans Ce fichier qui sert tous les détails.

Dans l'une de mes applications, je dois récupérer l'adresse IP wifi. J'ai utilisé les réponses ci-dessus, dans Swift 3, comme ceci:

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

Dans ce code, il se bloque parce que je dois vérifier nil dans chaque instruction que j'ai utilisé en option avec ?. Il est donc préférable pour moi d’utiliser un fichier lié donné dans ma classe. Il devient facile pour moi de vérifier maintenant comme: 

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
}

J'ai analysé la chaîne pour "." car Interface Class renvoie deux interfaces dans mon iPhone pour une adresse en0 telle que "fb00 ::" et une adresse telle que "101.10.1.1".

0
Max