Comment obtenir mon adresse IP par programmation sur iOS / macOS?

124

Je souhaite obtenir l'adresse IP de mon iPad par programmation. Comment puis-je interroger le sous-système réseau pour savoir quelles sont mes adresses IPv4 (et IPv6)?

PS: Puis-je désactiver IPv6 d'une manière ou d'une autre?

Wilbur
la source
14
En ce qui concerne votre «PS» ci-dessus, veuillez ne pas désactiver par programmation IPv6 sur l'appareil de quelqu'un. C'est tout simplement impoli.
Jeremy Visser
Vous ne pouvez pas désactiver IPv6. C'est obligatoire. En fait, votre application iOS doit prendre en charge IPv6.
Michael Hampton

Réponses:

132

Le code suivant trouve toutes les adresses IPv4 et IPv6 sur un appareil iOS ou OSX. La première getIPAddressméthode agit plus ou moins comme l'ancien code dans cette réponse: vous pouvez préférer l'un ou l'autre type d'adresse, et elle préfère toujours le WIFI au cellulaire (vous pouvez évidemment changer cela).

Plus intéressant encore, il peut renvoyer un dictionnaire de toutes les adresses trouvées, ignorer les adresses des not upinterfaces 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). Cela m'a été signalé par Jens Alfke sur un forum Apple - la fonction appropriée à utiliser est inet_ntop (regardez la page de manuel, et / ou référez-vous à cet 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 commentaires). Les adresses de bouclage sont maintenant renvoyées, mais il est facile pour vous de décommenter le test pour les exclure vous-même.

EDIT2: (par une personne inconnue): Amélioration supplémentaire 13 mars 2015: Dans le cas où l'utilisateur utilise un VPN (indépendamment du WiFi ou du 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, car c'est ainsi que l'appareil le gère. Cela devrait même fonctionner pour les Mac car la connexion VPN sur un Mac utilise également IF utun0 mais n'a pas été testée.

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

David H
la source
à quelle fréquence addrobtient NULL? mes utilisateurs obtiennent parfois NULL. savez-vous quelles sont les raisons possibles?
HelmiB
J'utilise uniquement AF_INETpour vérifier. pourrait être ça?
HelmiB
1
Je ne sais pas, mais il est certainement possible que vos utilisateurs soient sur un réseau IPV6.
David H
2
Cela devrait être la «bonne» réponse car elle est complète avec la 3G. Merci pour la mise à jour.
palme
1
Je ne pense pas que cela fonctionne correctement avec IPv6. La fonction inet_ntoa prend une adresse IPv4, donc dans le cas où sa_type == AF_INET6, vous prenez l'adresse IPv6 et la transtypez en IPv4 et la transformez en une chaîne (essentiellement à partir des 32 bits élevés du 128 bits adresse.)
Jens Alfke
88

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;

} 
Raptor
la source
1
Aucune interface en0 n'apparaît dans la liste des interfaces, donc renvoie simplement une erreur. Je ne sais pas si quelque chose a changé mais ne fonctionne certainement pas pour moi sur iOS 5.x
wuf810
11
Si vous regardez ci-dessous, vous trouverez un code légèrement modifié qui renvoie l'adresse de la cellule (3G) si le WIFI est désactivé.
David H
1
Le code suivant donne l'adresse IPv4 de en0 uniquement. Et l'IPv6? Quelqu'un peut-il donner un exemple de code sur la façon de le récupérer?
Oded Regev
2
Celui-ci ne trouve pas d'adresse IP lorsque l'appareil n'est pas connecté au wifi, mais au réseau
cellluar
1
Modifiez la condition if ci-dessous if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "en0"]) To if ([[NSString stringWithUTF8String: temp_addr-> ifa_name] isEqualToString [| NSString: @ "en0"] stringWithUTF8String: temp_addr-> ifa_name] isEqualToString: @ "pdp_ip0"]) pour obtenir l'IP lorsque l'appareil n'est pas connecté au WIFI ..
Raju
4

De nombreuses solutions existantes ne prennent en compte que les interfaces sans fil, qui ne fonctionneront pas pour les connexions filaires via un adaptateur Ethernet (c'est-à-dire pas de Wifi ou 3G); voir cette solution plus récente qui prend également en compte les adresses IP obtenues via des interfaces filaires.

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

lundhjem
la source
Excellent! Je vais bientôt l'ajouter à mon code ci-dessus. Qu'est-ce que en1 - le savez-vous?
David H le
1
Je ne le fais pas, mais j'ai remarqué que ce n'est pas la famille AF_INET comme les autres interfaces en
lundhjem
1
@DavidH récemment, l'adresse IP Ethernet est apparue sur en1 sur certains iPad, il semble donc qu'elle soit également membre de la famille AF_INET dans certains scénarios. Vous devriez également vérifier cette interface.
lundhjem
3

Obtenez 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
}
BHAVIK PANCHAL
la source
1

La solution actuelle ne renvoie pas le périphérique en0 sur OS X, le code suivant utilise le 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;
}
A. Badger
la source
Malheureusement, SCDynamicStoreCreate n'est pas disponible sur iOS, en l'état: SCDynamicStoreCopyValue
Alex Zavatone
1

Cette réponse a été inspirée par la réponse de @ DavidH. J'ai corrigé quelques problèmes, remplacé inet_ntoppar getnameinfoce qui permet une approche plus propre. Notez que cela produit un dictionnaire qui mappe un nom d'interface à un tableau d'adresses IP (une interface peut avoir plusieurs IPv4 et IPv6 associés, techniquement). 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;
lhunath
la source
Je n'ai jamais vu strf- est-ce une fonction d'assistance que vous avez dans votre code?
David H
Ouais, désolé, il y a quelques aides dans le code ci-dessus. erret strf. Vous pouvez simplement remplacer strfpar -stringWithCString:encoding:. errest un NSLogwrapper qui produit également le fichier et la ligne. github.com/Lyndir/Pearl/blob/master/Pearl/…
lhunath
1

La réponse de @ DavidH fonctionne bien jusqu'à ce que j'obtienne 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 n'ai aucune idée de pourquoi j'ai eu un utun0 / ipv6.

--- Actualisé ---

J'ai encore débogué ce problème et j'ai constaté que je pouvais obtenir une fausse adresse VPN même dans d'autres réseaux 4G (est-ce un bogue 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'ai utilisé vpn, j'obtiendrai 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 PAS utun0

Sans comprendre pourquoi je devrai simplement abandonner le vpn check :(

---- mettre à jour ----

J'ai signalé un bogue (28131847) à Apple et j'ai répondu: "Toutes les interfaces utun ne sont pas destinées au 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 adresse IP assignée là aussi. Nous fermons maintenant ce rapport de bogue. " :(

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

J'ai de nouveau touché le problème et je dois modifier davantage la réponse de @ DavidH pour le résoudre:

J'étais en réseau 4G et j'ai obtenu 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, j'obtiendrai l'IP wifi fe80 :: 8dd: 7d92: 4159: 170e, qui était faux et la connexion a échoué.

J'ai donc 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;
     }
 } ];
Qiulang
la source
J'ai posté sur des forums internes à propos du problème VPN, l'ingénieur Apple a proposé de m'aider. J'ai besoin que vous me contactiez hors ligne dhoerl at mac dot com
David H
1

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 du 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 plante parce que je dois vérifier nildans chaque instruction que j'ai utilisée 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 "."car la classe d'interface renvoie deux interfaces dans mon iPhone pour une en0adresse comme "fb00 ::" et une adresse comme "101.10.1.1"

Max
la source
1
Un lien vers une solution potentielle est toujours le bienvenu, mais veuillez ajouter du contexte autour du lien afin que vos collègues utilisateurs aient une idée de ce que c'est et pourquoi il est là. Citez toujours la partie la plus pertinente d'un lien important, au cas où le site cible serait inaccessible ou serait définitivement hors ligne. Tenez compte du fait qu'être à peine plus qu'un lien vers un site externe est une raison possible pour savoir pourquoi et comment certaines réponses sont-elles supprimées? .
Paul Roub
1
Ok sera mis à jour en conséquence car il est utile pour moi pour une meilleure compréhension
Max
1

J'ai créé un fichier simple pour obtenir l'adresse IP. J'ai basé cette solution sur les réponses de @ lundhjem, @ DavidH et @ Ihunath. Il considère les connexions filaires. Cependant, je n'ai pas inclus de VPN dans cette solution.

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
rue des cages
la source
0

dans iOS 13.4.1 ne fonctionne pas pour moi. J'utilise cette solution.

+ (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
la source