Book Image

iOS and OS X Network Programming Cookbook

By : Jon Hoffman
Book Image

iOS and OS X Network Programming Cookbook

By: Jon Hoffman

Overview of this book

Table of Contents (15 chapters)
iOS and OS X Network Programming Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Retrieving network address information


Many programs will need to know the network information about the available interfaces on the device they are running on. This recipe will show you how to retrieve the network information for all the active network interfaces on your device. The information that we will be retrieving is the interface name, IP version, IP address, netmask, and default gateway.

We will start off by creating a NetworkAddressStore class that can be used to store the information for a given network interface. We will then get a list of active network interfaces and create an instance of the NetworkAddressStore class for each interface. These objects will then be stored in NSMutableArray.

This recipe will also introduce several new functions and two new structures, including the very important sockaddr family of structures. We will discuss these new functions and structures as we describe the code.

Getting ready

This recipe is compatible with both iOS and OS X. No extra frameworks or libraries are required.

How to do it…

Let's retrieve the network address information for our device as follows:

  1. To retrieve the network address information, we will use the getifaddrs() function. This function will store a reference to a linked list of ifaddrs structures. Each ifaddrs structure will represent a physical or virtual network interface. The getifaddrs() function will return 0 if it was successful, or -1 if there was a problem.

    The getifaddrs(struct ifaddrs **ifad) function is not a part of the POSIX standard, but it is a part of most BSD systems; therefore, it is on both OS X and iOS. Refer to the following code:

    struct ifaddrs *interfaces = NULL;
    int success = 0;
    success = getifaddrs(&interfaces);
  2. Once we have the linked list of ifaddrs, we will need to loop through the list and retrieve the information about each network interface as shown in the following code:

    struct ifaddrs *temp_addr = interfaces;
    for (temp_addr = interfaces; temp_addr != NULL; temp_addr =  temp_addr->ifa_next) {
    
          int ipversion;
          NSLog(@"************************");
          if(temp_addr->ifa_addr->sa_family == AF_INET) {
              NSLog(@"IPv4");
              ipversion = AF_INET;
          } else if(temp_addr->ifa_addr->sa_family == AF_INET6) {
              NSLog(@"IPv6");
              ipversion = AF_INET6;
          } else {
              NSLog(@"Unknown IP version");
              ipversion = 0;
          }

    The temp_addr ifaddrs structure is a temporary structure that will be used as we loop through the linked list. We will need to keep a pointer pointing to the first ifaddrs structure so we can properly release the structure using the freeifaddrs() function when we are done with it.

    We then create a for loop to loop through our ifaddrs linked list.

    We check the IP address version being used by checking sa_family; if it is IPv4, we set ipversion to AF_INET; if it is IPv6, we set ipversion to AF_INET6. We will use this variable later in our inet_ntop() functions.

    If the IP address version is neither IPv4 nor IPv6, we set ipversion to 0.

  3. We need to define three character arrays to hold our network address, netmask, and gateway information for the network interfaces. In the following code snippet, three character arrays are defined:

          char naddr[INET6_ADDRSTRLEN];
          char nmask[INET6_ADDRSTRLEN];
          char ngate[INET6_ADDRSTRLEN];

    We set the size of the array to INET6_ADDRSTRLEN because it is larger than INET_ADDRSTRLEN, so it will hold either IPv4 or IPv6 addresses. INET6_ADDRSTRLEN is defined as 46, and INET_ADDRSTRLEN as 16.

  4. Now we need to show the result, for which we will use the following code:

          NSLog(@"Name:  %@",[NSString stringWithUTF8String:temp_addr->ifa_name]);
          inet_ntop(ipversion,&((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr,naddr,INET_ADDRSTRLEN);
          NSLog(@"Address:  %@",[NSString stringWithUTF8String:naddr]);
          if ((struct sockaddr_in6 *)temp_addr->ifa_netmask != NULL) {
              inet_ntop(ipversion,&((struct sockaddr_in *)temp_addr->ifa_netmask)->sin_addr,nmask,INET_ADDRSTRLEN);
              NSLog(@"Netmask:  %@", [NSString stringWithUTF8String:nmask]);
          }
          if ((struct sockaddr_in6 *)temp_addr->ifa_dstaddr != NULL) {
              inet_ntop(ipversion,&((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr,ngate,INET_ADDRSTRLEN);
              NSLog(@"Gateway:  ", [NSString stringWithUTF8String:ngate]);
          }
      }
    freeifaddrs(interfaces);
    

    The ifa_name character array of the ifaddr structure contains the name of the interface; therefore, we convert ifa_name to NSString and log it.

    We then use the inet_ntop function to populate the naddr, nmask, and ngate character arrays, convert them to NSStrings, and log them.

    The data returned from the getifaddrs() function is dynamically allocated and should be released using the freeifaddrs() function when it is no longer needed to avoid any memory leaks.

How it works…

The getifaddrs() function will store a reference to a linked list of ifaddrs structures. The ifaddrs structure looks like the following:

struct ifaddrs  {  *ifa_next;    /* Pointer to next struct */
    char             *ifa_name;    /*Interface name */
    u_int             ifa_flags;   /*Interface flags */
    struct sockaddr  *ifa_addr;    /*Interface address */
    struct sockaddr  *ifa_netmask; /*Interface netmask */
    struct sockaddr  *ifa_dstaddr; /*P2P interface destination or Broadcast address */
    void             *ifa_data;    /*Address specific data */
}

We use ifa_next in our for loop because it points to the next element in our linked list. If ifa_next equals NULL, we have reached the end of our linked list.

If you look closely, you will notice that the ifaddrs structure contains three sockaddr structures. The sockaddr structure is a generic structure that pointers are cast to. The sockaddr structure looks like the following code snippet:

struct sockaddr {
  uint8_t sa_len;
  sa_family_t sa_family;
  char sa_data[14];
}

Depending on the value of sa_family, we can cast the sockaddr structure as sockaddr_in (for IPv4 addresses) or sockaddr_in6 (for IPv6 addresses) before retrieving the address information. We use sa_family to determine the IP address version of the structure. The sa_family values contain one of the following listed values:

  • AF_UNIX: Local to host (pipes)

  • AF_INET: The IPv4 address family

  • AF_INET6: The IPv6 address family

  • AF_NS: Xerox NS protocols

  • AF_CCITT: CCITT protocols, X.25

  • AF_HYLINK: NSC Hyperchannel

  • AF_ISO: ISO protocols

We use ifa_name of the ifaddrs structure to determine the name of the interface.

We used the inet_ntop function to convert the binary representation of the network address that is stored in the sockaddr structure to a character array. If you look at the ntop part of the function name, n stands for network and p stands for the presentation, so you can read the function name as the "inet network to presentation" function. There is a corresponding inet_pton function that converts an ASCII string to binary, which you can think of as inet presentation to network.

The downloadable code contains projects for both the Mac OS X and iOS devices. Sample projects use a NetworkAddressStore class to store the information returned by the getifaddrs() functions. This will make it easier to integrate this recipe with your project.