Posts tagged ‘objective c’

Time to broadcast, iPhone style

Ran into a need to dynamically determine the current UDP broadcast address for the WiFi interface on the ole’ iPhone. Since NSHost appears to be a private API even w/the 3.0 software, it seems one must go lower. I wrapped it up in a neat little bundle that seems fairly usable if not verbose and full of magic (but understandable) numbers.

A few things of note. en0 is the WiFi interface. There are others. Instrument the following code w/some debug to get them all out. The ip/netmask methods return nil when the WiFi interface is not active. I would also be shocked if there were no corner cases I am ignoring…

#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <errno.h>
#include <ifaddrs.h>
#include <stdio.h>
 
static NSString *kWifiInterface = @"en0";
 
@implementation NetUtil
 
+ (NSString *)broadcastAddressForAddress:(NSString *)ipAddress withMask:(NSString *)netmask {
    NSAssert(nil != ipAddress, @"IP address cannot be nil");
    NSAssert(nil != netmask, @"Netmask cannot be nil");
    NSArray *ipChunks = [ipAddress componentsSeparatedByString:@"."];
    NSAssert([ipChunks count] == 4, @"IP does not have 4 octets!");
    NSArray *nmChunks = [netmask componentsSeparatedByString:@"."];
    NSAssert([nmChunks count] == 4, @"Netmask does not have 4 octets!");
 
    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;
    return [NSString stringWithFormat:@"%d.%d.%d.%d", (bcRaw & 0xFF000000) >> 24,
            (bcRaw & 0x00FF0000) >> 16, (bcRaw & 0x0000FF00) >> 8, bcRaw & 0x000000FF];
}
 
+ (NSString *)ipAddressForInterface:(NSString *)ifName {
    NSAssert(nil != ifName, @"Interface name cannot be nil");
 
    struct ifaddrs *addrs = NULL;
    if (getifaddrs(&addrs)) {
        NSLog(@"Failed to enumerate interfaces: %@", [NSString stringWithCString:strerror(errno)]);
        return nil;
    }
 
    /* walk the linked-list of interfaces until we find the desired one */
    NSString *addr = nil;
    struct ifaddrs *curAddr = addrs;
    while (curAddr != NULL) {
        if (AF_INET == curAddr->ifa_addr->sa_family) {
            NSString *curName = [NSString stringWithCString:curAddr->ifa_name];
            if ([ifName isEqualToString:curName]) {
                char* cstring = inet_ntoa(((struct sockaddr_in *)curAddr->ifa_addr)->sin_addr);
                addr = [NSString stringWithCString:cstring];
                break;
            }
        }
        curAddr = curAddr->ifa_next;
    }
 
    /* clean up, return what we found */
    freeifaddrs(addrs);
    return addr;
}
 
+ (NSString *)ipAddressForWifi {
    return [NetUtil ipAddressForInterface:kWifiInterface];
}
 
+ (NSString *)netmaskForInterface:(NSString *)ifName {
    NSAssert(nil != ifName, @"Interface name cannot be nil");
 
    struct ifreq ifr;
    strncpy(ifr.ifr_name, [ifName UTF8String], IFNAMSIZ-1);
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == fd) {
        NSLog(@"Failed to open socket to get netmask");
        return nil;
    }
 
    if (-1 == ioctl(fd, SIOCGIFNETMASK, &ifr)) {
        NSLog(@"Failed to read netmask: %@", [NSString stringWithCString:strerror(errno)]);
        close(fd);
        return nil;
    }
 
    close(fd);
    char *cstring = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
    return [NSString stringWithCString:cstring];
}
 
+ (NSString *)netmaskForWifi {
    return [NetUtil netmaskForInterface:kWifiInterface];
}
 
@end

NSURLConnection + startImmediately:NO == boom?

Having issues creating NSURLConnections using initWithRequest:delegate:startImmediately?

NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:url]
                                                     delegate:self
                                             startImmediately:NO];

Apparently when not using the simpler initWithRequest:delegate:, or even startImmediately:YES, the connection does not get scheduled in the current run loop. And again apparently, this causes unhappiness to occur when you eventually get around to calling start.

Simple fix, just stuff it in the current run loop before calling start and everyone gets along just fine.

[c scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[c start];

If there is something I am doing wrong or something I can do to prevent this, I’d like to know. Alas, the API is fairly brief on NSURLConnection, I don’t think I’m missing anything. This seems consistent with Cocoa, Cocoa Touch.