The FreenetClientProtocol
?
(FCP) is designed to abstract the basics of
Freenet so that client developers
do not have to track the main
Freenet protocol. FCP should be the bare bones of
Freenet - metadata handling is not included in FCP though an extension to FCP
may come about at a later date to avoid writing metadata handling libraries in
many languages.
This protocol is never meant to go across a network - only via the loopback.
Nodes should not accept FCP connections from hosts other than localhost by
default.
By
default FCP is port 8481, but any client that uses FCP should leave
this configurable, because this may be changed in the node's configuration file
or by some future FCP revision.
FCP follows the FNP setup for session and presentation.
In the following, numbers are always hex-encoded and fields in square-brackets
are optional.
FCP allows one transaction per connection, after which the connection is torn
down. At the beginning of each connection, the client must send these 4 bytes:
00 00 00 02
These are the 2-byte session identifier and the 2-byte presentation identifier.
In the future, different identifiers may be used to allow alternate syntaxes or
encrypted FCP connections from remote hosts, for example.
After sending the session and presentation identifiers, the client sends a
message to initiate the transaction, then waits for one or more messages from
the node until the transaction is complete. Messages are a series of lines
terminated by LF or CRLF, in this form:
Header
{Field1=Value1}
.
.
{FieldN=ValueN}
EndMessage?
This is the complete set of client to node messages, with the possible node to
client responses (only the headers are listed).
-
ClientGet -
URIError -
Restarted -
DataNotFound -
RouteNotFound -
DataFound
-
ClientPut -
URIError -
Restarted -
RouteNotFound -
KeyCollision -
Pending -
Success
Additionally, the node may respond to any client message with a
FormatError, meaning the command was not understood, and the node
may responsd at any time with a
Failed, indicating a fault in the
node itself:
(Node -> Client)
FormatError?
{Reason=}
EndMessage?
(Node -> Client)
Failed
{Reason=}
EndMessage?
Failed and
FormatError will not be discussed in the
remainder of this document. Clients should be prepared to handle a
Failed at any time, and a
FormatError as teh response
to any client message. Either of these messages terminates the transaction and
the connection.
This is totally optional for the client. Note that this counts as a transation
and thus the connection is torn down afterwards.
(Client -> Node)
ClientHello?
EndMessage?
In response the node sends the following message:
(Node -> Client)
NodeHello?
Protocol=
Node=
{HighestSeenBuild=}
EndMessage?
The optional HighestSeenBuild
? will only be present if a build higher than the
node's current build is seen in the datastore. Client implementors are
advised, in this circumstance, to notify the user that they should upgrade to
the latest build of
Freenet. The user should have the ability to turn off this
warning.
(Client -> Node)
ClientGet?
URI=
HopsToLive=
EndMessage?
The client is now in the
waiting state. The node may return one of
the following messages:
-
URIError: Invalid Freenet URL. The transaction is
terminated.
-
Restarted: The client should continue waiting.
-
DataNotFound: The transaction is terminated due to not being
able to find data.
-
RouteNotFound: The transaction is terminated due to not being
able to find a route.
Otherwise a
DataFound message is returned:
(Node -> Client)
DataFound?
DataLength=
{MetadataLength=
EndMessage?
Note - If metadata is present in the data being delivered, then
DataLength
will be the total length, including metadata. The length of the pure data is
DataLength - MetadataLength.
Also, the metadata is sent first, then the data.
After a
DataFound message the data itself is sent in chunks:
(Node -> Client)
DataChunk?
Length=
Data
<@Length bytes of data>
At any time when the full payload of data has not been sent a Restarted message
may be sent.
(Note by David McNab? - no need to check for 'Restarted'
messages within a data chunk - just check the first line each time you're
expecting a new DataChunk.). This means that the data failed to verify and
the transfer will be restarted. The client should return to the
waiting
state, and if a
DataFound is then received, the data transfer will
start over from
the beginning. Otherwise, when the final
DataChunk is received,
the transaction is complete and the connection dies.
Note - you cannot assume that the transition from metadata to data will occur
cleanly on DataChunk boundaries. It is possible for the last bytes of metadata
and the first bytes of data to be received within a DataChunk.
In your client programs, you'll need to buffer these incoming datachunks in a separate layer of your key retrieval code.
.
(Client->Node)
ClientPut?
HopsToLive=
URI=
DataLength=
{MetadataLength=}
Data
<@DataLength number of bytes>
If the client is inserting a CHK, the URI may be abbreviated as just
CHK@. In this case, the node will calculate the CHK. The node
must get all of the trailing field before it can start the insert into
Freenet.
The node may reply with one of the following messages:
-
URIError: Invalid Freenet URL. The transaction is
terminated.
-
Restarted: The client should continue waiting.
-
RouteNotFound: The transaction is terminated due to not being
able to find a route.
-
KeyCollision: The transaction is terminated due to a document
with the same key already existing in Freenet. This message contains a URI
field with the Freenet URI of the document.
-
SizeError: The transaction is terminated due to the data
being too large for the key type; all non-CHK keys have a limit of 32 kB of
data.
During an insertion, multiple
Pending messages may be returned.
These messages signal that the data is being successfully inserted, but
insertion is not complete, and the node has not received a
StoreData message yet:
(Node -> Client)
Pending
URI=
{PublicKey=}
{PrivateKey=}
EndMessage?
When the node receives a
StoreData message (and thus insertion is
complete), a
Success message is returned with the
Freenet URI of
the new document and possibly a private/public keypair, if the inserted
document was an SVK. See the section on key generation about this.
(Node -> Client)
Success
URI=
{PublicKey=}
{PrivateKey=}
EndMessage?
These messages allow a client to generate keys. This does not affect
Freenet
at all - the calculations are carried out at the node.
Key generation requests are done via a
GenerateKey message.
Either a CHK or an SVK keypair can be generated:
(Client -> Node)
GenerateCHK
DataLength=
{MetadataLength=}
Data
<@DataLength number of bytes>
The node calculates the CHK as it would do if inserting, but instead returns
it. This completes the transaction:
(Node -> Client)
Success
URI=
EndMessage?
The format for generating SVKs is very similar but generates a pair of keys
(public and private) which are independent of any data. This is generally used
for setting up SSKs:
(Client -> Node)
GenerateSVKPair
EndMessage?
The node generates a key pair and returns:
(Node -> Client)
Success
PublicKey=
PrivateKey=
EndMessage?
The
PublicKey and
PrivateKey are returned as
Freenet-base64 encoded strings. These can be used to construct URIs for
requesting or inserting SSKs:
(insert) freenet:SSK@/
(request) freenet:SSK@/
Unix shell script to send a command to FCP, for testing purposes (you must install netcat):
(echo -e -n '\0\0\0\2'; echo "ClientHello"; echo "EndMessage") | nc localhost 8481
I (TravisBemann) originally
transcribed this document from a converted to HTML version of an SGML document
by Adam Langley which originally documented FCP, along with a few changes to
document the introduction of the Pending message, and changing
wording and formatting in some circumstances. Thus I will credit Adam Langley
with the majority of the content in this.
-- TravisBemann?
I removed a rather off-topic debate about authorship of this document.
-- IanClarke? - 07 Aug 2002