Posted on November 10, 2009 at 6:04pm EST. More.

Don’t printf when you can tcpdump

I’m working on an app that talks to a web service, and in the course of debugging it’s good to know what exactly is being sent to and from the web server. I had been doing so with the tried and true method of printf() debugging (this is Cocoa, so NSLog() debugging, actually), but it was getting to be a pain:

  1. If I print all network traffic all the time, it overwhelms my console, making it useless for any other kind of output. So instead, I’m constantly inserting or removing NSLog() statements as I work. Not to mention having to reproduce a request because the right logging statements weren’t in place the first time around.
  2. NSURLConnection returns downloaded information as an NSData object, so simply passing it to NSLog() dumps a lot of useless hexadecimal code to the screen. That means I must first create an NSString from the data, print it, then release it. (I can’t use %s, the data isn’t null terminated.)
  3. NSURLConnection does a lot of behind the scenes work, like storing cookies and setting Content-Length headers. That’s nice, but that makes it hard to know exactly what’s being sent on the wire.

And that’s when it occurred to me: why not just watch what’s on the wire? tcpdump is a command line utility which monitors network traffic and prints out packets that you specify.

Here’s the incantation to monitor HTTP traffic to and from a specified host:

sudo tcpdump -l -q -A "host (Specified Host) and tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)"

To explain briefly, sudo runs the command as root, -l enables line buffering, -q hides some of the less interesting protocol information, and -A prints the content of each packet in ASCII. The filtering expression that follows selects packets to or from (Specified Host), to or from port 80, and ignoring SYN, FIN, ACK-only, and other non-data packets. I’ll confess, I don’t understand that last part completely, I copied it from the tcpdump man page.

To make the output a little easier to read, I pipe the output to a Perl script I quickly hacked together which watches for the packet header lines and outputs the ANSI escape codes to render them in bold. But I’ll leave that as an exercise to you, dear reader.

There’s no need to download anything, tcpdump is already installed on your Mac. (I don’t know if it’s part of the standard install or the Developer Tools, but what do you care?)

Now, what would be really sweet is a graphical app to do this. I found Cocoa Packet Analyzer, but it’s a little low-level for me. I don’t care about packet specifics; I’d prefer something that reconstructed the tcp streams in an easy to navigate way.