Explanation of Kragen's second C .signature puzzle

Last updated 1999-07-12.

Here's the program:

char a[99]="  KJ",d[999][16];main(){int s=socket(2,1,0),n=0,z,l,i;*(short*)a=2;
if(!bind(s,a,16))for(;;){z=16;if((l=recvfrom(s,a,99,0,d[n],&z))>0){for(i=0;i<n;
i++){z=(memcmp(d[i],d[n],8))?z:0;while(sendto(s,a,l,0,d[i],16)<0);}z?n++:0;}}}

Here's a line-by-line (or expression-by-expression) dissection of the program. (You should read how to run the program first.)

char a[99]=" KJ",

Defines a global 99-character string, whose first four chars are space, space, K, and J, and which otherwise consists of zeroes. This is used as a struct sockaddr_in at first.

d[999][16];

Defines an array of 999 16-char arrays, the addresses of the clients, as struct sockaddr_ins.

main() {

Note no command-line arguments.

int s = socket(2,1,0),

This is intended to be socket(PF_INET, SOCK_DGRAM, 0). It works on SPARC Solaris 2.6; I know at least the second argument is wrong for Linux.

n=0,

The number of currently-known clients.

z,l,i;

z holds the size of a struct sockaddr_in most of the time, but also gets used as a boolean variable; l holds the length of an incoming message; and i is just a loop variable that loops over the clients.

*(short*)a=2;

This sets the sin_family of the struct sockaddr_in in a to be AF_INET, in host byte order. (If I didn't care about byte order, I'd just initialize the string to \0\2 or \2\0.)

if(!bind(s,a,16))

If the port is in use, or if the socket() call failed, bind returns -1, but otherwise returns 0. So this really means "if bind() succeeds". Note that we haven't changed the port ('KJ', 'K' * 256 + 'J', or (64+10)*256 + 11, or 18955; chosen because those are my initials) or IP address (all zeroes, we assume, since it's global, and therefore INADDR_ANY).

The rest of the program is contingent upon bind() succeeding.

for(;;){

This is the message loop; it runs until someone kills the server.

z=16;

recvfrom() wants to know how big a space you're passing it to give you the address in, and it wants to use the same space to tell you how big the address actually was. So here we put 16, the size of a struct sockaddr_in, into a variable to make it happy.

if((l=recvfrom(s,a,99,0,d[n],&z))>0) {

Here we receive a message of up to 99 bytes on the (bound but unconnected) UDP socket we create earlier, with no flags (0), and we put the address it's from in the next available slot in the clients table.

If we get an error (which should only happen on Linux), then we just go to the next iteration of the message loop.

for(i=0;i<n;i++) {

Iterate from 0 to the number of clients, minus one. This is a loop with two purposes: to see if we already know about this client and to send copies of the message to all known clients. This has the somewhat negative effect that the client who sent the message will get a copy only if it is already known.

z = (memcmp(d[i],d[n],8)) ? z : 0;

This is a fancy way of saying, "If d[i] and d[n] match, set z to zero, otherwise leave it set to what it is set to." d[n] is the client who sent the current packet. Remember, z should be set to 16 from recvfrom() when we enter this loop. So at the end of this loop, if we found a match (i.e. this client is already in the client table) z will be 0; otherwise it will be 16.

while(sendto(s,a,l,0,d[i],16)<0);

sendto() returns -1 on error; on Linux we should get an error if the socket just got an ICMP error such as a Port Unreachable, which is what we'll get a lot once clients start dying. When it reports this error, it fails to send the packet. So here we repeat it until it succeeds.

It is merely sending the contents of a (l bytes of them -- however many bytes we received) through the socket s to the client d[i].

   }
   z ? n++ : 0;
}

The end of the loop over the clients; if we found no copies of the sender of this packet in the client table, we increment n, and now it has an entry in the client table -- what was previously d[n].

Then there's the end of the if() block for recvfrom().

}}

And the end of the message loop, and the end of main().


Kragen's second C .signature puzzle | Kragen's home page