1

Hi all, I am looking for a better solution for checking / calculating if an IP-address is in the same subnet like your iPhone.

The background: I am looking up devices by the Zeroconf / Bonjour and this devices can have multiple IP-addresses. So I want to check which of them can be reached by my device. I Thought the easiest way is to take the extern IP and connect it with my subnet-mask, but I havenot found any way to do it directly (need both as int), so I made a workaround.

The code:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <net/if.h>

-(void)netServiceDidResolveAddress:(NSNetService *)service {
  NSLog(@" # # # Bonjour.netServiceDidResolveAddress # # # ");
  self.isConnected = YES;
  self.connectedService = service;

  NSLog(@"service.hostName   : %@", service.hostName);
  NSLog(@"service.name       : %@", service.name);
  NSLog(@"service.description: %@", service.description);

  // # # #

  // get MAC-ADDR from TXTRecordData
  NSData *txtRecord = service.TXTRecordData;

  unsigned char aBuffer[[txtRecord length]];
  [txtRecord getBytes:aBuffer length:[txtRecord length]];
  NSString *macAddr = [NSString stringWithCString:(const char *)aBuffer encoding:NSStringEncodingConversionAllowLossy];
  if(macAddr && macAddr.length >28)
  {
      macAddr = [macAddr substringFromIndex:12];
      macAddr = [macAddr substringToIndex:17];
  }

  NSLog(@"service.macAddr: %@", macAddr);
  // # # #

  NSString           *name = nil;
  NSData             *address = nil;
  struct sockaddr_in *socketAddress = nil;
  NSString           *ipString = nil;
  int       port;
  int   i = 0;

  for (i = 0; i < [[service addresses] count]; i++)
  {
      NSString *ip;
      name = [service name];
      address = [[service addresses] objectAtIndex:i];
      socketAddress = (struct sockaddr_in *)[address bytes];
      ip = [NSString stringWithFormat: @"%s", inet_ntoa (socketAddress->sin_addr)];

      if([ip isEqualToString:@"0.0.0.0"])
          continue;

      NSLog(@"- - - ");
      if([self isIpInSubnet:ip])
      {
          ipString = [ip copy];
          port = service.port;
          NSLog(@"set IP");
      }
      NSLog(@"%@", ipString);
      NSLog(@"%d", port);
 }
....
}

-

- (BOOL)isIpInSubnet:(NSString *)targetIP{
// http://www.cocoabuilder.com/archive/cocoa/127230-finding-subnet.html
// http://veys.com/

struct ifaddrs *ifap, *ifp;
char    ip[NI_MAXHOST], nm[NI_MAXHOST];
BOOL isInMask = NO;

if(getifaddrs(&ifap) < 0)
    perror("getifaddrs");

for(ifp = ifap; ifp; ifp = ifp->ifa_next) {
    /* Ignore everything but IPv4 */
    if(ifp->ifa_addr->sa_family != AF_INET )
        continue;

    /* Ignore interfaces marked down. */
    if(!(ifp->ifa_flags & IFF_UP))
        continue;

    /* Ignore interfaces without netmask. */
    if(!ifp->ifa_netmask)
        continue;

    if(getnameinfo(ifp->ifa_addr, ifp->ifa_addr->sa_len, ip, sizeof(ip),
                   NULL, 0, NI_NUMERICHOST) != 0)
        perror("getnameinfo");

    if(getnameinfo(ifp->ifa_netmask, ifp->ifa_netmask->sa_len, nm,
                   sizeof(nm), NULL, 0, NI_NUMERICHOST) != 0)
        perror("getnameinfo");

    /* Ignore interfaces for localhost. */
    if([[NSString stringWithFormat:@"%s", ip] isEqualToString:@"127.0.0.1"])
        continue;

    // # # # # # # # # # # # #

    NSArray *ipChunks = [targetIP componentsSeparatedByString:@"."]; // chunks for extern IP
    NSArray *nmChunks = [[NSString stringWithFormat:@"%s",nm] componentsSeparatedByString:@"."];

    NSUInteger ipRaw = 0;
    NSUInteger nmRaw = 0;
    NSUInteger shift = 24;
    for (NSUInteger i = 0; i < 4; ++i, shift -= 8) {
        ipRaw |= [[ipChunks objectAtIndex:i] intValue] << shift;
        nmRaw |= [[nmChunks objectAtIndex:i] intValue] << shift;
    }

    NSUInteger bcRaw = nmRaw & ipRaw; // lowest interface addr.
    NSString *nw1 = [NSString stringWithFormat:@"%d.%d.%d.%d", (bcRaw & 0xFF000000) >> 24,
                     (bcRaw & 0x00FF0000) >> 16, (bcRaw & 0x0000FF00) >> 8, bcRaw & 0x000000FF];

    ipRaw = nmRaw = 0;
    shift = 24;

    ipChunks = [[NSString stringWithFormat:@"%s",ip] componentsSeparatedByString:@"."]; // chunks for local IP

    for (NSUInteger i = 0; i < 4; ++i, shift -= 8) {
        ipRaw |= [[ipChunks objectAtIndex:i] intValue] << shift;
        nmRaw |= [[nmChunks objectAtIndex:i] intValue] << shift;
    }

    bcRaw = nmRaw & ipRaw;
    NSString *nw2 = [NSString stringWithFormat:@"%d.%d.%d.%d", (bcRaw & 0xFF000000) >> 24,
                     (bcRaw & 0x00FF0000) >> 16, (bcRaw & 0x0000FF00) >> 8, bcRaw & 0x000000FF];

    isInMask = [nw1 isEqualToString:nw2];
    NSLog(@"intern: %@ - extern: %@", nw2, nw1);
    // # # # # # # # # # # # #

    NSLog(@"ip %@ is in range with %s in mask %s ? %@", targetIP, ip, nm, isInMask ? @"YES" : @"NO");
}
// Free memory 
freeifaddrs(ifap);

return isInMask;
}

if someone has a hint to make it easier or some kind better, he is always welcome to post his solutions :)

danielbeard
  • 9,120
  • 3
  • 44
  • 58
geo
  • 1,781
  • 1
  • 18
  • 30
  • *poke and push* not even one possible change how to do it better? ^^ – geo Jun 22 '12 at 09:44
  • sa_len isn't correctly set on netmasks returned by getifaddrs. This applies to MacOS (Darwin) and eg FreeBSD. For completeness, note also that other systems with sa_len, including AIX, also return a short sa_len for SIOCGIFNETMASK, for example. – Nicholas Wilson Nov 06 '12 at 11:50

0 Answers0