freenet logo

Il progetto Freenet

freenet symbol

--- ricablare Internet ---

navigatore
donazioni
grazie a...
Ospitato da:
SourceForge
&
Dominio donato da:
Edward Alfert
Mousetracker.com

• Questo documento è disponibile in una versione più aggiornata in inglese qui.

Inglese

Utilizzo del client Java API

Versione 1.1

la traduzione e' solo parziale; eventuali completamenti sono bene accetti!

0 Introduzione

La libreria di riferimento per l' implementazione dei client Freenet è costituita da un nucleo flessibile e potente attorno il quale progettare il client. Condivide la maggior parte del codice (network, crypto, threads) con il nodo Freenet ('Fred'). Flessibile significa che un paio di operazioni in più che una semplice chiamata di sistema per eseguire un Insert od un Request, ma anche che è universalmente integrabile, da una interfaccia grafica a una integrazione su un proxy o un remailer.

0.1 Cenni legali

Questo documento, eccetto dove espressamente indicato, è stato scritto da Oskar Sandberg, ed è sotto GNU General Public License.

0.2 Correzioni, domande, discussioni, ecc...

Per queste potete contattarmi a

perl -e 'print(pack("h*","d6469383d2f6371604e6164616e2b64786e23756"));'
o discuterne nella development mailing list.

Operazioni preliminari

1.1 Il Core

Per poter funzionare, il client necessita di un Core per poter gestire i servizi di base (questa è la parte condivisa con il server Freenet). I core di Freenet possono essere molto flessibili e progettati per supportare differenti protocolli di trasporto e presentazione, ma la maggior parte dei client avrà bisogno solamente di un core base per supportare il protocollo Freenet standard su tcp/ip. Per maggior efficienza, i core dovrebbero essere condivisi tra tutte le richieste effettuate, si raccomanda di lanciarne uno e continuarlo ad utilizzare fino alla fine della sessione.

Ci sono due modalità Freenet.client.ClientUtil.getTCPCore() che generano un Core. Il primo utilizza solamente un integer del valore della porta tcp che il Core utilizzerà come parametro per i messaggi in arrivo (se questo ha valore 0 questo verrà selezionato casualmente). Il secondo metodo permette anche di specificare un set di parametri e un file di log. I Client di solito funzionano anche senza l' inserimento di parametri di logging o di settaggio, in questo caso va bene il primo metodo, e potete passare subito alla sezione 1.2..

1.1.1 Parametri

Il core Freenet accetta un set di parametri, sotto forma di Freenet.Params object. Un params object può essere creato da un costrutto vuoto, usando una stringa (che ci si aspetta sia nella forma "-[=] "), un file (in formato java, "=", EOL delimited, e con # per i commenti), o un qualsiasi numero o entrambi. Quando l' oggetto è creato, si possono settare i valori dei parametri utilizzando il metodo:

    setParam(String name, String value);

Ecco i parametri che sono rilevanti per il client (così come si trovano nel sample.freenetrc file). Se non si capisce il loro significato, non c' è ragione per cambiarli:

# Il numero di millisecondi tra gli scatti del timer
tickerTime=500
# Il tempo previsto e la deviazione standard, in millisecondi, che impiega
# un nodo Freenet per far passare un messaggio. Questi sono utilizzati per calcolare
# quanto il nodo deve attendere prima di considerare perso un messaggio.
# Questi sono indicati principalmente per motivi di debugging  - cambiarli NON
# renderà il tempo di risposta più veloce.
hopTimeExpected=12000
hopTimeDeviation=12000
# Quanto si deve aspettare per un' autenticazione prima di rinunciare(in millisecondi)
authTimeout=30000
# Quanto si deve aspettare per connettersi a un host prima di rinunc(in millisecondi)
connectTimeout=2000
# Quanto si deve aspettare prima di chiudere una connessione inattiva (se l' indirizzo
# di reply è conosciuto).
connectionTimeout=300000
# Lasciarla impostata come "yes" a meno che non stia facendo un debugging di timeout.
doHandshake=yes
# Quanto si deve attendere per l' handshake (in millisecondi)
handshakeTimeout=30000
# Quanto attendere prima che un handshake venga chiusa (in millisecondi)
handshakeLife=10000000
# Si vuole usare il thread-management?  Se questo numero è definito e diverso da zero,
# questo specifica quante connessioni possono essere attive contemporaneamente.
maximumConnectionThreads=50
# Una lista di numeri coppie di classi delle key per key plugin 
#keyTypes=0101 : Freenet.keys.KHK

Ai parametri che non sono specificati verrà attribuito il valore di default . Nessuno di questi è indispensabile per il funzionamento del client.

1.1.2 Logging

Il Core Freenet registerà il suo stato interno in un oggetto che implementi la Freenet.support.Logger interface. Questa importante procedura è:

    /**
     * Log a message
     * @param source   L' oggetto sorgente, in cui questo messaggio è stato generato
     * @param message Un chiaro e dettagliato messaggio che descrive l' evento
     * @param priority La priorità del messaggio, tra Logger.ERROR,
     *                 Logger.NORMAL, Logger.MINOR, o Logger.DEBUGGING.
     **/
    public abstract void log(Object source, String message, int priority);

In generale, i client avranno solo bisogno di tener traccia dei log interni del Core per cercare dei bug e realizzarne un utile resoconto (e di bug ce ne saranno...). I livelli di priorità sono i seguenti:

    /** Questo messaggio indica un errore che impedisce il corretto funzionamento**/
    public static int ERROR = 3;
    /** Un evento normale **/
    public static int NORMAL = 2;
    /** Un evento minore che normalmente non è di interesse **/
    public static int MINOR = 1;
    /** Un evento che potrà interessare durante il debugging **/
    public static int DEBUGGING = 0;

Un buon client dovrebbe mantenere da qualche parte un log di tutti i messaggi, inclusi quelli di debugging, per esserci d' aiuto quando cerchiamo dei bug, ma non è necessario. Anche gli ERROR level log possono essere ignorati, dato che quando il client riscontra dei problemi genera una lista degli eventi (vedi sez. 5) in relazione al verificarsi degli errori.

1.2 Indirizzi

Per progettare un client, si ha anche la ncessità di fornigli l' indirizzo del nodo Freenet cui potersi connettere (come avrete probabilmente sentito, è fortemente raccomandato che questo sia un nodo locale sulla macchina dell' utente). Gli oggetti Freenet.Address sono address wrappers indipendenti dal protocollo, ma se abbiamo a che fare solo col TCP/IP, si può utilizzare il metodo Freenet.client.ClientUtil.getAddress per creare tale oggetto. Gli argomenti sono sia un java.net.InetAddress e un numero di porta, una stringa con il nome dell' host ed il numero della porta, o una stringa delimitata da due punti.

1.3 Librerie del Client

Quando si hanno Core e Address, si può costruire una libreria del Client con:

    public Client(ClientCore core, Address target);

Si può usare lo stesso oggetto Freenet.client.Client per richieste diverse, ma si dovrà crearne uno nuovo se ci si vuole connettere ad un altro nodo (conservando il Core però).

2 Le Chiavi

Le Chiavi sono la struttura portante di Freenet. Per inserire o richiedere un qualsiasi dato, è necessaria una chiave (key). Le chiavi possono avere diverse forme e dimensioni. Questa sezione tratta solo della creazione di chiavi per i client, per documentazione sui vari tipi di key e su come funzionano, vedi il documento "Bestiario delle Chiavi".

2.1 URI di Freenet

Le URI di Freenet sono così definite:

freenet:[KeyType@]KeyValue[,DecryptionKey]

per CHK, SVK, e KSK, benchè le KSK non abbiano una DecryptionKey, e non richiedano di indicare il keytype perchè sono il tipo di default. Per le SSK la sintassi è leggermente diversa.

freenet:SSK@DocumentName,SubspaceKey

C' è una Freenet.client.FreenetURI class che può essere usata per i descritti Freenet URIs. Può essere costruita dal valore della stringa, e può anche restituire una stringa in questo formato partendo dalla key. I metodi più importanti sono:

    getKeyType()

che indicherà che tipo di key l' URI descrive, e

    toString() 

per fornire in uscita le key create dal client

2.1.1 Freenet Base64

Per le key che contengono dati in forma binaria (tutte eccetto le KSK) Freenet usa una versione modificata della Base64, modificata in modo da non contenere alcun carattere che abbia un significato particolare nelle URI, ed eliminando il requisito che la lunghezza sia un multiplo di 4 caratteri. L' alfabeto in base64 utilizzato è:

  private static char[] base64Alphabet = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', '~', '-'};

dato che le librerie del client effettueranno la codifica e la decodifica in maniera trasparente, molto probabilmente non ce se ne dovrà preoccupare. In caso, c' è una classe che realizza la conversione da e verso questo formato in Freenet.client.Base64.

2.2 CHKs - Content Hash Keys

L' oggetto che decrive le CHK al Client è Freenet.client.ClientCHK.

2.2.1 Creazione di una CHK all' inserimento dei dati.

Poiché le Content Hash Keys sono basate su una funzione dei dati, l' utente non deve (nè può) dare alcun valore alla key. Per creare una CHK per inserire dei dati, si dovrà usare la sintassi:

    public ClientCHK(InputStream in, long plainLength);

L' inputstream dovrà contenere i dati, ma non potrà più essere utilizzato dopo si è generata la key. Non può essere lo stesso stream che poi si darà da inserire al client (ovvero, i dati devono essere in un file di qualche tipo).

2.2.2 Ottenere il valore CHK.

L' intero valore della CHK non è conosciuto finché non vengono caricate le librerie del client. Quando i dati sono stati inseriti, si può usare getURI() metdod per ottenere l'URI necessario per richiedere la CHK.

2.2.3 Creazione di una CHK alla richiesta dei dati.

Quando viene richiesta una CHK, l' URI dovrà contenere tutti i dati per la key, quindi si può semplicemente usare la sintassi:

    public ClientCHK(FreenetURI uri) throws KeyException;

2.3 SVK - Signature Verifying Keys

La classe che descrive le SVK per il Client è Freenet.client.ClientSVK. Le SVK verranno utilizzate quando verrà implementata la funzione di update, ed allora sarà necessaria una maggiore documentazione per poterle utilizzare.

2.3.1 Creazione di una SVK all' inserimento dei dati.

All' inserimento, una nuova chiave privata ed una pubblica verranno generate per creare una nuova SVK. Per fare ciò, si deve usare la sintassi:

    public ClientSVK(Random r) throws KeyException;

Dive l' implementazione della java.util.Random e utilizzata per generare i valori della key. Si può utilizzare l' implementazione Freenet dello Yarrow cryptographic PRNG utilizzando il randSource field sul proprio Core (vedi 1.1).

2.3.2 Ottenere il valore dell' SVK.

L' intera SVK non sarà generata finché non verranno aperte le librerie del client, ma dopo che i dati saranno inseriti, si può usare il getURI() method dell' oggetto ClientSVK per ottenere l' URI completo di cui si ha bisogno per richiedere l' SVK.

2.3.3 Ottenere la key privata generata con una SVK.

Quando una SVK è generata, generalmente assieme viene generata anche una Private key. La chiave privata può essere usata per inserire dati in sottocategorie basate su SVK (vedi sotto), e per successivi aggiornamenti di dati indicizzati delle SVK. Dopo che l' inserimento è effettuato, si può ottenere il valore della chiave privata utilizzando il getPrivateKey() method del ClientSVK object (getPrivateKeyString() lo restituirà codificato in base64).

2.3.4 Creazione di una SVK alla richiesta dei dati.

Così come per le richieste, le SVK possono essere create direttamente dall' URI quando è necessario. Basta usare la sintassi:

    public ClientSVK(FreenetURI key) throws KeyException;

2.4 KSK - Keyword Signed Keys

La classe che descrive le KSK per il Client è Freenet.client.Client.

2.4.1 Creazione di una KSK all' inserimento dati

Per creare una KSK è necessario il valore della keyword, and una implementazione random (vedi le SVK) . Usare la sintassi:

    public ClientKSK(Random r, String keyword) throws KeyException;

2.4.2 Creazione di una KSK alla richiesta dati

Così come per le altre key, si ricava semplicemente partendo dalla URI:

    public ClientKSK(FreenetURI key) throws KeyException;

2.5 SSK - SVK Subspace Keys

Una volta generata, una SVK puo essere utilizzata come radice di un sottospazio, in cui è necessaria la chiave privata per inserire documenti nel sottospazio, ma è sufficiente la chiave pubblica per richiederlo. Le SSK sono descritte nella Client library dalla classe Freenet.client.ClientSSK.

2.5.1 Creazione di una SSK all' inserimento dati.

Quando si inserisce in una SSK, si deve utilizzare:

    public ClientSSK(Random r, FreenetURI key) throws KeyException

La funzione Random si è vista con le SVK, ma la FreenetURI in questo caso ha questa forma particolare:

freenet:SSK@,

Si noti che la seconda parte è rappresentata dalla chiave privata, di cui si ha bisogno per inserire dati nel subspace (vedi 2.3.3) non la chiave pubblica utilizzata nell' URI che è resa pubblica ed utiizzata per richiedere il documento.

2.5.2 Ottenere il valore della SSK.

I valori degli URI delle SSK sono gli stessi utilizzati dall' URI per costruire il ClientSSK, ma con la parte <SVK private key> sostituita dalla corrispondente SVK pubblica. Il valore può essere ottenuto utilizzando il the getURI() method del ClientSSK object.

2.5.2 Creazione di una SSK alla richiesta dati.

Così come per le altre key, quando si richiedono i dati si può ottenere l' SSK diretamnente dall' URI:

    public ClientSSK(FreenetURI key) throws KeyException;

3 Preparare un InsertRequest

I dati vengono inseriti utilizzando il prepare method del Client object (vedi 1.1 - 1.3) con i parametri:

     * @param htl   Il numero di HopsToLive da dare a questo messaggio.
     * @param data  un flusso di dati da inserire.
     * @param dataLength  Il numero di bytes o dati da inserire.
     * @param metadata    un flusso di Freenet standard document metadata.
     * @param metadataLength  La lunghezza dei metadata da inserire.
     * @param ckey    UnClientKey object to insert by.
     * @param cipherName  Il block cipher da usare.
     * @param ctBucket    Un luogo in cui memorizzare CipherText in as it is being
     *                    treated.

The method will return a RequestToken object that can be used to add event listeners and actually start the InsertRequest/DataRequest.

3.1 Hops To Live

Un richiesta di Insert request needs a number of hops to live set, that is the number of nodes that the message should reach. The hops to live of an insert message can generally be a little greater then a request, but no more then 20-25 should be necessary. The current limit of HTL on Freenet is 100.

3.2 Dati

To insert something, you will need an InputStream of the data in question. The data will not be read directly from this stream onto Freenet, but rather read as it is being encrypted and hashed, and dumped in a bucket (see below) from which it is inserted (so you can't use this stream as an indication of how much of the data has been inserted).

3.3 Lunghezza dei dati

The amount of data to read of the stream in 3.2.

3.4 Metadati

This should be a stream of metadata to insert before the data in Freenet. Observe that this is private metadata, not visible to anybody but those who request the document. Standards from Freenet metadata are documented elsewhere.

If you do not have any metadata, give any stream and set the metadata length to zero.

3.5 Lunghezza dei metadati

The amount of metadata to read off the stream in 2.4. If there is no metadata, set this to zero.

3.6 Key

The key to insert the data by in the form of Freenet.client.ClientKey object. See section 2 for how to generate different kinds of keys.

3.7 Cipher name

This should be the name of the cipher used to encrypt the data. Currently supported ciphers are Twofish and Rijndael, with Rijndael (AES) recommended.

3.8 Ciphertext Bucket

The client library needs to pass over the data twice, once to encrypt and calculate a hash of the ciphertext, and a second time to actually insert the ciphertext. Therefore the ciphertext needs to be dumped somewhere inbetween. To be as flexible as possible, you can give the library any instance implementing the Freenet.support.Bucket interface. To store the data in.

There are two bucket implementations already in Freenet.support. FileBucket is a simple bucket that places the data in a file on the disk, while Cryptbucket will do the same, but encrypt the data a second time with a random key while it is on the disk (this is to protect the user from data recovery from the disk later proving he inserted the data). Note that if you use either of these with a constructor that creates a temporary file, you will need to call Freenet.support.OnExitCleanup.doCleanUp() before exiting for the file to be deleted - if you target is java 1.2, you are better off creating a temporary file yourself with the JFC methods and giving that to File or CryptBucket.

4 Preparare a DataRequest

Requests are prepared by calling the prepare method of the Client object (see 1.1-1.3) with the following parameters:

     * @param htl  The HopsToLive to give the request.
     * @param ck    The key object to request.
     * @param data  A bucket to place the data in
     * @param metaData A bucket to place the metaData in (if there is any)

This method will return a RequestToken to which you can add event listeners and actually start the request.

4.1 Hops to live

This is the amount of nodes that the request should make it to. If Freenet is healthy and the data is available, you should need a value no higher than 15-20 to find the data. The absolute maximum that freenet allows is 100.

4.2 Key

The key to request, as described by a Freenet.client.ClientKey object. See section 2 for information on how to create ClientKey objects.

4.3 Data bucket

The client needs to download all the data before it can verify that it is correct, so it needs a place to store it. For flexibility, you can provide any implementation of Freenet.support.Bucket interface for this purpose.

There are two implementations of Bucket already in Freenet.support, see 3.8 for more information.

4.4 Metadata bucket

As with the data, the client needs to place the metadata somewhere while verifying it, so you must provide a bucket for that too. Metadata is generally small, so it may be more efficient to use a bucket that stores in memory then on disk.

5 ClientEventListeners

When a request (InsertRequest or DataRequest) executes, it will generate events that can be caught to read the status of the execution. Each event will be an implementation of Freenet.client.ClientEvent, and in order to cache them you need to have an object that implements the Freenet.client.ClientEventListener that you can add to the RequestToken generated in step 3 or 4, using:

    public void addEventListener(ClientEventListener cel);

You can add as many event listeners as you please. There is currently one ClientEventListener implemented with the client Library, EventLogger, which will send each events toString() to a Logger object (see 1.1.2).

5.1 Eventi

Gli eventi generati dalla Client library sono nel Freenet.client.events package. Ognuno ha la propria classe, ma anche un unico numero. Ecco qui un elenco degli eventi col proprio numero corrispondente e cosa questo significhi:

0 StateReached Event.



Questo appare quando cambia lo stato del client. Lo stato è descritto da un int, che può essere convertito in un nome con il metodo:

    Client.stateOf(int state);

Gli stati possibili sono:

    public static final int INIT = 0;
    public static final int PREPARED = 1;
    public static final int REQUESTING = 2;
    public static final int TRANSFERING = 3;
    public static final int DONE = 4;

    public static final int FAILED = -1;

Lo stato del client generalmente progredisce, ma può regredire da TRANSFERING a REQUESTING se i dati non vengono verificati. DONE e FAILED sono definitivi.

        1       SendEvent

E' stato inviato un messaggio a Freenet. Il metodo getMessageName() di questa classe indica il tipo il tipo di messaggio che è stato inviato.

        2       ReceiveEvent

E' stato ricevuto un messaggio da Freenet. Il metodo getMessageName() di questa classe indica iò tipo di messaggio che è stato ricevuto..

        3       ExceptionEvent

E' accaduta un' eccezione mentre si stava utilizzando il client. Le eccezioni più comuni sono le ConnectionExceptions se non ci di riesce a connettere ll' indirizzo specificato, o NullPointers se si è inviato un null da qualche parte per errore. La classe ha un metodo rethrow() che converte le eccezioni in domande (così si può ottenere un log).

        4       RestartedEvent

Ogni nodo Freenet tiene traccia del successivo, e si preoccupa che risponda regolarmente e che ritorna siano corretti. Se un nodo non risponde a una richiesta (data/insert), o non risponde correttamente, l' ultimo nodo funzionante node si attiverà, inviando un messaggio ai nodi precedenti per comunicare loro che possono resettare i loro timer. Quando un messaggio del genere raggiunge il client, avremo un RestartedEvent.

Questo è quello che potrebbe accadere quando avviene un NoReplyEvent (10) . L' evento 10 più o meno significa: "Io (il client) sono l' ultimo nodo correttamente funzionante, e non c'è molto altro che possa fare".

        5       RequestSuccessfulEvent

This indicates that either the data being requested was found, or the a path to insert the data was found. When this event occurs, the library starts actually transfering the data. This does not indicate that the transaction is complete - in the case of the request the data may be corrupted in which case the network will start over.

        6       RequestFailedEvent

This occurs when a DataRequest or InsertRequest failed gracefully, ie a message was received from the network rejecting the request. The getMessage() method will return the message in question, but only the type will be interesting to most clients.

On Insert:

A "DataReply" means the same thing as CollisionEvent below - data for this key was already found on Freenet.

A "TimedOut" also means that the key was found, but in this case the data was not found (this may seize to be a valid reply on inserts soon since it is insecure - nodes don't have to back up the clame with data to prove it).

A "RequestFailed" means that the InsertRequest went to every node it could reach on all of Freenet before it's hops to live ran out. This should not happen on a network with more than 10 nodes or so - if this happens the users node is most probably disconnected from the main network.

On Request:

A "TimedOut" means that the DataRequest went through the number of nodes you asked, but did not find the data. Increasing the hops to live may help, but it doubtful raising it beyond 25-30 will make any difference at all.

A "RequestFailed" means the same thing as for the InsertRequest.

        7       CollisionEvent

The key was found to already be on Freenet when attempting to insert. For SVKs, KSKs, and SSKs, this could mean other data is already on Freenet, but for CHKs this means the exact data is already on Freenet so the user can just link to/redirect to/use that data instead.

        10      NoReplyEvent

This is generated when no reply is heard from Freenet after a time calculated by a standard formula (90% confidence interval of an expected hop time and deviation of 12 seconds each). If the node you are talking to is working correctly, you should always get a reply within this time (warning: our nodes have been known to not always work correctly). If you fuck with the settings of hopTimeExpected and hopTimeDeviation in section 1.1.1 you can expect to see these.

        16      ErrorEvent

An error occured in the client library. This normally means we fucked up, and after checking the comment and making sure you didn't send anything really weird into the client, this may validate a bug report.

        128     TransferStartedEvent

This event indicates that the transfer of data has started.

        129     TransferedEvent

These events are produced at set intervals while the data is being transfered. The getProgress() method returns the number of bytes read or written thus far.

        130     TransferCompleteEvent

This event indicates that a transfer of data has been completed successfully.

        131     RequestCompleteEvent

This event is produced when all the data you requested has been sucessfully received and the final message accepted. Congratulations ! :-)

        132     SegmentCompleteEvent    

This event indicates that one segment of data has been transfered. Currently, Freenet has up two two segments (metadata and data). Note that this event does *NOT* guarantee that the data in the segment is correct. Only after a RequestCompleteEvent can one assume that the data was intact.

6 Executing the Request

When the you have added any ClientEventListeners (see section 5) to the RequestToken produced in section 3 or 4, you are ready to execute the request. All you need to do is call the

    makeRequest(RequestToken rt)

method of the same Client object that you got on that RequestToken from. The Request will occur asynchronously in another thread, so you all you need to do is wait and watch the events being produced. If you want to wait for the event to finish, you can write a simple EventListener that wakes you up when a StateReachedEvent (see 5.1, Event 0) for either DONE or FAILED occurs, and go to sleep.

Il materiale contenuto in questo sito è distribuito sotto la Gnu Documentation License , disponibile anche qui in italiano.