An Extensible Handshaking Protocol for the Gnutella Network Greg Bildson Christopher Rohrs Lime Wire LLC [Note: If you wish to skip the preamble, go directly to Section 3.] 1. Introduction Gnutella servents currently establish messaging connections with the following protocol: 1. The client establishes a TCP connection with the server. 2. The client sends "GNUTELLA CONNECT/0.4". Here "" refers the end-of-line character, commonly notated by "\n" in programming languages like C and Java. 3. The server responds with "GNUTELLA OK". 4. Both client and server send binary messages at will. Most clients are strict in checking these strings. For example, neither LimeWire nor Gnutella 0.56 will accept "GNUTELLA CONNECT/0.5" in step (2). The problem with this scheme is that it provides no way of versioning the Gnutella protocol. Servents cannot discover advanced capabilities in other servents. Say for example that a servent X supports a new protocol message for routing queries intelligently (QRP). X cannot tell whether a neighboring servent Y will understand its QRP messages. Lime Wire LLC had proposed using the GUIDs of initial ping requests as a form of protocol versioning. For example, the last byte of the GUID of the first message by a client would be 0 if the client spoke the "Gnutella 0.5" protocol, 1 if the client spoke the "Gnutella 0.6" protocol, etc. While this scheme is backwards- compatible, it is imperfect. First, it does not provide ways for clients to support different combinations of protocol extensions. If, for example, clients at the "0.6" level were to support QRP and pong-caching, a servent would be required to implement both. Second, this scheme places a burden on the servent implementer. For example, a servent trying to accept only connections with QRP support must put new connections on "probation" until it has received the initial ping. Clearly a simpler but more flexible scheme is needed. We propose a protocol that the uses HTTP-style headers. We introduce our scheme in two steps. First we describe a simple but flawed HTTP-style protocol. Then we describe some refinements that allow status codes to be sent in both directions. 2. Introducing HTTP Headers The following is a simple but flawed handshaking scheme using HTTP-style headers: 1. The client establishes a TCP connection with the server. 2. The client sends "GNUTELLA CONNECT/0.6". Here "" refers the carriage-return character, commonly notated by "\r" in programming languages like C and Java. 3. The client sends HTTP-style capability headers, each terminated by "", with an extra "" at the end. Example headers might be: User-Agent: LimeWire Query-Routing: 0.1 4. The server responds with "GNUTELLA/0.6 200 OK". 5. The server sends HTTP-style headers as in (3). 6. Both client and server send binary messages at will. If the server sends a response other than "200 OK" in in step (4), the client should disconnect. For example, a server requiring authentication might respond with "GNUTELLA/0.6 401 Unauthorized". Likewise, a server that is overloaded may reject connections with "GNUTELLA/0.6 503 Service Unavailable". While this scheme is an improvement over the GUID-based scheme, it has three shortcomings. First, it does not provide backwards compatibility, though this is easily fixed by making multiple connection attempts as necessary. Secondly, there is no way for the client to reject the server; status codes are only one-way. For example, a client demanding authentication from the server cannot send "401 Unauthorized". Third, the client must always send vendor-specific headers to the server, which wastes bandwidth. BearShare, for example, might wish to send signed binary data in the handshaking headers in order to eliminate its proprietary "binary query" message. If many BearShare servents are trying to connect to an overloaded LimeWire servent L, it is clearly a waste for L to read these binary headers, only to respond with "503 Service Unavailable". 3. Proposed Protocol This section presents our proposed handshaking protocol. This protocol differs from the simple one described above in that the client gets to send a response code to the server, and the client sends two sets of headers: 1. The client establishes a TCP connection with the server. 2. The client sends "GNUTELLA CONNECT/0.6". 3. The client sends all capability headers--except for vendor-specific headers--each terminated by "", with an extra "" at the end. 4. The server responds with "GNUTELLA/0.6 200 OK". 5. The server sends all its headers, in the same format as in (3). 6. The client sends "GNUTELLA/0.6 200 OK, as in (4). 7. The client sends any vendor-specific headers as needed, in the same format as (3). 6. Both client and server send binary messages at will, using the information gained in (3) and (5). Here is a sample interaction between a client and a server. Data sent from client to server is shown on the left; data sent from server to client is shown on the right. Client Server ----------------------------------------------------------- GNUTELLA CONNECT/0.6 User-Agent: BearShare Query-Routing: 0.2 GNUTELLA/0.6 200 OK User-Agent: BearShare Query-Routing: 0.1 BearShare-Data: 5ef89a GNUTELLA/0.6 200 OK BearShare-Data: a04fce [binary messags] [binary messages] A few notes about the responses: first, the client (server) should disconnect if receiving any response other than "200 OK" at step 4 (6). There is no need to define these error codes now. Second, servents should ignore higher version numbers in steps (2), (4), and (6). For example, it is perfectly legal for a future client to connect to a server and send "GNUTELLA CONNECT/0.7". The server should respond with "GNUTELLA/0.7 200 OK" if it supports the 0.7 protocol, or "GNUTELLA/0.6 200 OK" otherwise. However, note that an old-fashioned "GNUTELLA OK" is not an appropriate response. A few notes about the headers: servents should use standard HTTP headers whenever appropriate. For example, servents should use the standard "User-Agent" header rather than make up a "Servent-Vendor" header. However, it is perfectly legal to add new headers (e.g., "Query-Routing") when no appropriate HTTP header exists, as long as they follow HTTP syntax. Such headers should be approved by other developers on the GDF. One difficulty with this scheme is that it does not provide automatic backwards-compatibility with existing servents. A 0.6 client that sends "GNUTELLA CONNECT/0.6" to a 0.4 server will be disconnected. For this reason, clients are encouraged to reconnect at the 0.4 level if the 0.6 handshake failed. Furthermore, servers should respond with an old-fashioned "GNUTELLA OK" if they receive "GNUTELLA CONNECT/0.4" instead of the more modern "GNUTELLA CONNECT/0.6". Both steps will prevent 0.4 clients from being disconnected from 0.6 clients. Finally, we encourage vendors to implement this protocol in two stages. Initially, vendors should modify their servents to accept the new headers (and respond appropriately with "GNUTELLA/0.6 200 OK") on incoming connections. Only after these clients have been widely deployed should clients actually start to send "GNUTELLA CONNECT/0.6" when establishing outgoing connections. This incremental rollout will reduce the number of reconnect attempts and provide a smooth transition to the new handshaking scheme.