Sneaky Pete redux
--#--
Our first step is to rewrite Sneaky Pete, the
Java HTTP client, in C.
paul.c will need to do two special things: Formulate a request
header and POST string, and open an Internet socket on a server.
Because this is C, both will take a moderate amount of code to
perform.
We want to start with opening a socket. Creating the socket is the
simplest part. A call to the socket function needs three
parameters: an int constant representing the Internet member of
address families (AF_INET), another int constant representing a
bi-directional stream (SOCK_STREAM), and a protocol identifier,
which is always zero for stream sockets on Internet addresses.
#include
int sock;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
die("Can't open socket\n");
You'll need to define a Perl-style die function somewhere:
void die(char *s) {
printf("%s\n", s);
exit(0);
}
Next, you open a connection on a specific port to the target
host. But before you do, you need to set up an Internet
socket address structure defined in the header file netinet/in.h.
And to do that, you need:
long ipAddress;
struct hostent* hostInfo;
struct sockaddr_in sockInfo;
int sock;
// Fill sock address structure
memset(&sockInfo, 0, sizeof(sockInfo));
sockInfo.sin_family = AF_INET;
sockInfo.sin_port = htons(port);
ipAddress = inet_addr(host);
if (ipAddress < 0) {
hostInfo = gethostbyname(host);
ipAddress = *(long *)*hostInfo->h_addr_list;
}
sockInfo.sin_addr.s_addr = ipAddress;
The Internet socket address structure (struct sockaddr_in) needs
three values: the Internet address cast as a single 32-bit
number, the port and the AF_INET constant. (The port number
is first passed through the macro htons defined
in netinet/in.h. htons, for host-to-network-short, swaps
the value's byte order from the host (Little Endian on Intel machines)
to network byte order (Big Endian)). It's important to
fill the struct w/ NULLs first, though, because there is
more space in it that must be zeroes if not used.
The function inet_addr() will cast a dotted quad Internet
address to a 32-bit integer. But if the host string is
a fully-qualified domain (i.e., www.darkspell.com), inet_addr()
will return -1. In that case you need to call gethostbyname.
This involves a tricky type cast. gethostbyname stuffs the
address into a four-char array in a hostent record (defined in
netdb.h). The hostent record itself is accessed via a pointer.
So, to obtain the Internet address as a 32-bit number, you need to
cast the pointer to the four-char array to a pointer to a long,
and dereference the long.
Then, just assign it to the sin_addr.s_addr member of the
sockaddr_in struct and we're ready to go.