Thread: Gnutella and C
View Single Post
  #1 (permalink)  
Old January 18th, 2002
James Connolly James Connolly is offline
Devotee
 
Join Date: January 18th, 2002
Posts: 22
James Connolly is flying high
Default Gnutella and C

I wanted to write a Gnutella server/client, and I wanted to learn C language, so I decided to kill two birds with one stone and write a Gnutella server/client in C. I'm not totally unfamiliar with programming, I've written PERL scripts and things like that, but am certainly not a programming guru by any means. I'm writing it on a Solaris 7 system, but Solaris, like virtually everything else, including Windows, uses BSD-like sockets, so I'm not that worried about compatibility. My program is compatible on OpenBSD so far, and I know parts of it are Windows compatible.

My first problem was connecting to a socket and handshaking. I actually used some code on RPI's web site in order to do that. I've been able to write that code in working form from memory, so I am not worried about my ability to re-code that function. I got the handshake down.

Next I had to do my initial ping. This was tougher than the handshake since it used "data". I was kind of confused about some things even with the Gnutella Protocol Specification in hand. I downloaded the Bordignon/Tolosa paper from the GDF forum file section which was helpful. I also downloaded tcpdump and watched my Solaris's compiled gnut interact with my Windows 98's Gnucleus and Limewire. This was very helpful. So I got my program to send a ping and then read in a buffer of data which I printed hexadecimally.

Now I had to grab those pongs, pings and queries out of the hexadecimal jumble. Jim Frost wrote a function that reads a set amount of bytes from the socket into a buffer, so I stuck that in.

First I wrote code that looks at the GUID, Payload descriptor, TTL, hops and payload length from each Gnutella packet. Then I wrote code which read pings. Then I wrote code which read pongs.

Then I wrote code which read queries. At first I had the code look for the 0 terminator at the end of the query to terminate it, but this caused some hassle, so I had it use the payload length to decide on the point of termination. Of course, bad or bogus fields sent to me would mess up the program, so I'll have to stick in error handling to deal with bad packets later.

One big problem I had was I could loop this translation of gnutella packets from this 1000 bytes of buffer I had accepted, but the 1000th byte would usually be the middle of a gnutella packet, and it would get hit in the middle of the translation loop. This problem took me a while to solve. What I did was loop until I had processed more than 800 bytes in the buffer of gnutella packet, and then go out and read the number of packets I had processed from the socket (usually a number a little over 800), and then I did a few memcpy()'s so that I would once again have a 1000 byte buffer to process - the <=200 byte old buffer with the >=800 new buffer appended to it. Problem solved.

It also took me a little while to realize that I should be reading this socket in as unsigned, as it was easier to interpret pong IP addresses, the payload descriptors and the like when they're unsigned.

So currently my program connects to a Gnutella server/client, receives and interprets pongs (and pings and queries), and usually after a few minutes it seems the remote servent disconnects me. So I suppose my next task will be writing code that attempts to connect to the IP addresses of some of those hosts that pong me, so that I have a persistent connection to one or more hosts.

I guess I get to decide what order to put Gnutella features in after putting in code to get a semi-decent persistent connection to the Gnutella network. I want to be a good network citizen and have a server/client that conforms to the Gnutella protocol spec. ie. if I send queryhits, my code should actually be able to send the file out properly if requested, and if I start pong'ing, I should be able to accept incoming connections, route properly and so forth. A lot of the tinkering is being done on my private LAN so as to minimize the impact on the Gnutella network.

Version 0.01 of my code is on my home page:
Gnutizen

As I said, I'm just learning C so the code surely looks like garbage. OTOH, since my C knowledge is so bad, everything I do is done fairly explicitly and simply, without a lot of pointers and structs and the like, so looking at it may be of some use.

Once you get the ball rolling on these projects, they begin to gather their own momentum, even if you're the main one pushing the ball. Writing a Gnutella server/client is a great introduction to p2p (and C language) for me, I guess I'll see where this project takes me.
Reply With Quote