« October 2008 | Main | December 2008 »

November 15, 2008

touchengine -- iPhone Google App Engine communication

My friend Noah Gift, an awesome python programmer who wrote the very popular book Python for Unix and Linux System Administration and I are working on a new open source framework that aims to facilitate communication between the iPhone SDK and Google App engine. In the spirit of the open source mantra, "release early, release often," we've made it available here on Google Code and is MIT licensed. We're also working on a couple of apps that use the framework; we plan to eat our own dogfood so-to-speak.

Current features

  • Includes a slightly modified version of the python plist library to allow syndication of data from Google App Engine to the iPhone via xml plists.
  • Includes a generically useful caching plist downloader library for the iPhone SDK that keeps the user in sync with Google App Engine data and allows offline access to that data.

Example Code

  • isonnet, a Google App Engine application that syndicates Shakespeare's Sonnets in plist form for consumption by the iPhone app.
  • Sonnet, a viewer application for the iPhone, which we're going to release soon for free on the app store, that connects to our Google App Engine site to download, cache, and display Shakespeare's Sonnets.

Future Features / Informal Roadmap

  • Authentication with Google App Engine with the user's Google ID
  • Two-way communication and data sync between app engine and the iPhone SDK
  • Integration and automatic plist syndication of underlying Google App Engine Data Storage and objects
  • Support for Application skinning through plist syndication
  • Support for storage of the iPhone user's application preferences on Google App Engine.
Look for our article on IBM DeveloperWorks coming soon.

If you have any questions or comments for us, please feel free to contact me at jonathan ((at)) thisdomainyou'reonrightnow ((dot com)).

November 14, 2008

Reverse DNS on the iPhone

Reverse DNS via the usual OS X means doesn't seem to work on the iPhone. It looks like this is a known bug/limitation. The new apple developer forums (login required) have a thread that's dedicated to the problem. (rdar://problem/5929766 is mentioned there). Apple seems to have used the eraser on some other of their DNS code on the iPhone. Saurik has an interesting hack to work - around a perhaps related change in DNS behavior.

You can actually get reverse DNS lookup on the iPhone to work using res_query (which is in libresolv, so make sure you link against libresolv.dylib) to query DNS and dns_parse_packet, which you'll find in dns_utils.h, to do the work of parsing out the DNS server reply works rather well. I could not get the recommended tools in dns.h to work, but I did discover that res_query returns the same raw reply from the DNS Server that dns_parse_packet expects from the utilities in dns.h. The code:
#define T_PTR		12		/* domain name pointer */
#define C_IN		1		/* the arpa internet */
static const int kBufLen = 1500;

//takes a string like 1.1.168.192.in-addr.arpa and turns it into the hostname
char *_reverseDns(const char* arpaPoint)
{
    int len = -1;
    char buffer[kBufLen];
    res_init();
    
    int queryType = T_PTR; /* domain name pointer */ 
    int arpanet = C_IN; /* the arpa internet = 1*/
    len = res_query(arpaPoint, arpanet, queryType, (u_char *)buffer, kBufLen);
    char *resStr;
    dns_reply_t *reply;
    if (len > 0)
    {
        reply = dns_parse_packet(buffer, len);
        resStr = ((*(reply->answer))->data).PTR->name;
    }
    else
    {
        if (errno)
            fprintf(stderr, "Error, res_query() error value: %d\n", errno);
        if (61 == errno) 
            resStr = "Could not connect to DNS";
        else
            resStr = "DNS entry Not Found";
    }    
    return resStr;
}

//makes 192.168.1.1 into 1.1.168.192.in-addr.arpa
NSString *makeDNSLookupString(NSString *inString)
{
    NSString *eachString = @"";
    NSScanner *scanner = [NSScanner scannerWithString:inString];
    NSUInteger scanLocation = 0;
    NSUInteger stringLen = [inString length];
    NSString *outStr = @"";
    while([scanner scanUpToString:@"." intoString:&eachString])
    {
        eachString = [eachString stringByAppendingString:@"."];
        outStr = [eachString stringByAppendingString:outStr];
        scanLocation = [scanner scanLocation];
        if (scanLocation < stringLen)
            [scanner setScanLocation:scanLocation + 1]; // hop the . character
    }
    outStr = [outStr stringByAppendingString:@"in-addr.arpa"];
    return outStr;
}

NSString *reverseDNS(NSString *ipAddress)
{
    NSString *arpaString = makeDNSLookupString(ipAddress); 
    //NSLog(@"arpaString = %@", arpaString);
    NSString *returnString = [NSString stringWithCString:_reverseDns([arpaString cStringUsingEncoding:NSASCIIStringEncoding]) encoding:NSASCIIStringEncoding];
    return returnString;
}
You can find a simple example app that uses this here.

revDNS.jpg

It also uses a nice trick from The iPhone Developer's Cookbook. It's MIT licensed, so feel free to use it in your own app. Consider this a workaround until apple comes up with something simpler. This works for me installed on the iphone itself looking up addresses on the internet and on my local network (it loses the .local, but is otherwise aok). It doesn't validate the ip address string you give it or check to see if DNS is even available, so you'll want to wrap it in some reachability and validation calls, etc (which I did not do in the demo app, but it fails fairly gracefully when you give it garbage input).

Running reverseDNS(@"192.168.1.139") (which is the IP address of my imac) on the iPhone connected via wi-fi to my local network returns @"FreakinIMac" (which is the host name of my imac). It even returns the DD-WRT hostname of my router. It returns @"DNS entry Not Found if it can't find an entry or errs out and it will log errno if set by res_query to the console. You'll probably want to wrap some error handling code around this if you plan to use it in a real app.

Other useful stuff: You can find the source for libresolv here, which was quite helpful. The source to dns.c and dns_util.c proved useful as well. A post on an apple mailing list also pointed me in a direction. Having the source to the libraries you use is a great help. Thanks to Apple for making these available; keep 'em coming!

Note: If you want to log the whole parsed reply from the DNS server, use dns_print_reply (also in dns_utils) with DNS_PRINT_ANSWER as an arg. There is probably a lot more you can do with this method of DNS lookup. I'm guessing you can send pretty much any kind of DNS query and parse the result, just watch out for (and apply his genius patch, if necessary) that bug that Saurik found.

Note: Watching network traffic with a packet sniffer like Wireshark is quite informative.