ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/pws2004/freenet.pod
Revision: 1.5
Committed: Thu May 13 21:45:50 2004 UTC (22 years, 4 months ago) by root
Branch: MAIN
Changes since 1.4: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 Freenet - Informationsfreiheit ohne Zensur, aber mit Perl
2    
3     =head2 Abstract
4    
5 root 1.2 Das Freenet-Projekt (http://www.freenetproject.org) hat es sich zur
6     Aufgabe gemacht, jedem die die Möglichkeit freier Meinungsäußerung zu
7     garantieren. Neben den Implementation einer Lösung für dieses Problem
8     geht es in diesem Artikel darum, wie man in Perl selbst Informationen
9     herunterlädt bzw. veröffentlicht.
10    
11     =head1 Einführung
12    
13     Das Freenet-Projekt (http://www.freenetproject.org) hat es sich zur
14     Aufgabe gemacht, jedem die die Möglichkeit freier Meinungsäußerung
15     zu garantieren. Als Mittel zur Umsetzung hat es ein virtuelles Netzwerk
16     gewählt, in dem es nicht realistisch möglich sein wird, Einspeisung
17     von neuen Daten zu verhindern oder den Urheber von Veröffentlichungen
18     ausfindig zu machen, solange er dies nicht selbst zugibt. Darüberhinaus
19     ist es für Betreiber von Freenet-Knoten nicht möglich, zu wissen, was
20     auf ihrer Festplatte gespeichert wurde oder wo die Daten herstammen, so
21     daß es ab einer bestimmten Netzwerkgröße nicht mehr möglich ist,
22     dem Betreiber einer Freenet-Node nachzuweisen, daß er illegale Inhalte
23     besitzt bzw. weitergegeben hat oder davon gewusst hat.
24    
25     In China beispielsweise wird eine frühe Version von Freenet für ein
26     rein chinesisches Freenet-Netzwerk aktiv benutzt, um politische Inhalte
27     verbreiten zu können ohne Maßnahmen von der Regierung zu befürchten.
28    
29     =head2 Freenet für den Benutzer
30    
31 root 1.4 Freenet speichert nur relativ kleine Dokumente (<= 1MB) am Stück. Diese
32 root 1.2 Dokumente können beliebige Daten enthalten, z.B. HTML-Seiten. Dise
33     Datenblöcke können mit einem für jeden Block eindeutigen Schlüssel
34     abgerufen werden, z.B. in einem Web-Browser.
35    
36     Hier ist ein Beispiel für eine solche HTML-Seite:
37    
38     freenet1.png
39    
40     Die hier verwendete URL ist C<http://129.13.162.73:8888/SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM/YoYo//Controversy.html>.
41     C<http://129.13.162.73:8888> ist die Adresse meines Freenet-Daemons,
42     üblicherweise C<http://127.0.0.1:8888>, der Sicherheit wegen sollte man
43     immer einen eigenen Freenet-Daemon laufen lassen. Der Schlüssel der
44     "Freesite" ist C<SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM> und in dieser wird
45     wiederum das Unterdokument C<YoYo//Controversy.html> angezeigt.
46    
47     Diese Seite ist Teil eines sehr bekannten Einsprungpunktes ins
48     Freenet. Generell bevorzugt das Freenet-Projekt keine bestimmten
49     Einsprungpunkte. Die Regel ist: kennt man den Einsprungpunkt nicht, findet
50     man die Inhalte nicht; es ist sogar unmöglich zu bestimmten, wieviel oder
51     welche Informationen im Freenet gespeichert sind: Ein Index a'la google
52     ist prinzipbedingt nicht möglich.
53    
54     Obwohl es einige Freenet-Spider und Directory-Freesites gibt ist der
55     überwältigende Teil der Information im Freenet nicht darüber zu
56     erreichen.
57    
58     Neben HTML-Inhalten gibt es auch Mail-, Foren- und Chat-Systeme im
59     Freenet.
60    
61     Die Einspeisung von Daten kann von überall im Netz
62     geschehen. Mehrfacheinspeisung ist möglich (und meistens notwendig) und
63     erhöht führt nicht zur Dupliziering von Daten.
64    
65     Natürlic gibts es auch einige Nachteile, die teilweise mit dem Design
66     und teilweise mit der Implementierung zusammenhängen. So ist der einzige
67     verfügbare Freenet-Daemon in Java implementiert und daher leider extrem
68     unportabel (läuft praktisch nur auf Linux/x86 und Windows, da das
69     benötige Sun-Java-JDK auf andreen Platformen nicht verfügbar oder
70     veraltet ist), sehr langsam und vor allem Speicherhungrig, und häufig
71     auch sehr instabil.
72    
73     Die aktuellen Versionen des Freenet-Daemons sind alle als "nicht sicher"
74     deklariert, da erst die Release 1.0 verspricht, alle Ideen umgesetzt zu
75     haben, wovon das Projekt noch 0.5 Versionen entfernt ist :)
76    
77     Designbedingt ist das Freenet eher langsam (extrem hohe Latenz,
78     akzeptabler Durchsatz) und für interkative Benutzung teilweise
79     ungeeignet, bzw. eine echte Geduldsprobe.
80    
81     Durch die Art der Speicherung kann nicht garantiert werden daß
82     Informationen überhaupt wiedergefunden werden. "Unpopuläre" Information
83     verschwindet, sofern sie nicht regelmäßig eingespeist oder abgerufen
84     wird, automatisch wieder aus dem Freenet.
85    
86     =head2 Die Freenet-Architektur
87    
88     Herkömmliche File-Sharing-Netzwerke haben zwei Große Nachteile: Entweder
89     sind sie nicht wirklich anonym (selbst anonymisierende Netzwerke oder
90     Proxies sind üblicherweise nicht vor Zugriff durch Regierungen sicher
91     (ein bekanntes Beispiel ist I<JAP>, C<http://anon.inf.tu-dresden.de>, das
92 root 1.3 zwar vollmundig vollkommene Anonymität garantiert aber schoneinmal durch
93     die Polizei gezwungen wurde, Log-Informationenen herauszugeben, wozu die
94     Betreiber nach deutschen Recht verpflichtet sind). Oder sie skalieren
95     nicht, da sie Broadcast-Algorithmen benutzen die größere Netzwerke von
96     vorneherein ausschließen (bekanntes Beispiel dafür ist I<gnutella>).
97 root 1.2
98     Das erste Problem umgeht Freenet (Achtung, vereinfacht!), indem es
99     jeden Datenblock hasht und aus diesem Hash einen Schlüssel für die
100     Verschlüsselung generiert und die Daten damit verschlüsselt (sic).
101    
102     Der Schlüssel wird ein zweitesmal gehasht. Dieser Hash wird zur
103     eindeutigen ID des Datenblocks. Diese ID und der verschlüsselte
104     Datenblock werden ins Freenet eingespeist. Dabei, wird immer derselbe
105     Key generiert (da er nur von den Daten abhängt) und der verschlüsselte
106     Block ist ebenfalls immer gleich. Daher kann er problemlos mehrfach und
107     von unterschiedlichen Parteien eingespeist werden ohne die Daten faktisch
108     mehrfach im Freenet abzulegen.
109    
110     Das ist sicher, da ein kryptographisch sicherer Hash (der die Grundlage
111     des Systems bilden muß) nur in eine Richtung funktioniert: Aus der ID
112     (== Hash des Hashes der Daten) kann nicht auf den Schlüssel geschlossen
113     werden. Speichert also ein Netzknoten die Daten und die ID dazu, kann man
114     die Daten zwar abrufen, jedoch nicht entschlüsseln. Selbst der Besitzer
115     eines Knotens kann mit vollkommenen Wissen über alle Vorgänge seines
116     Knotens die Inhalte nicht lesen.
117    
118     Eine weitere Konsequenz dieses Verfahrens ist die Tatsache, das Dokumente
119     niemals verändetr werden können: eine Änderung bewirkt eine Änderung
120     des Schlüssels und damit eine neue ID, praktisch eine völlig neue URL.
121    
122     Möchte man z.B. das grüne Blatt der Freesite "Thought Crime" herunterladen so
123     wird man mit folgendem Freenet-Key konfrontiert:
124    
125     CHK@pfAv9IejYPLQwaTLXDguEkUhiNUMAwI,blzDbhN~8Q28esq4JrfRWw
126    
127     C<CHK> steht für I<Content-Hash-Key>, der häufigste Schlüsseltyp im
128     Freenet, der das oben beschriebene Verfahren benutzt. Der CHK besteht
129     aus zwei Teilen: der ID, unter der der Schlüssel im Freenet abgelegt
130     wird (C<pfAv9IejYPLQwaTLXDguEkUhiNUMAwI>) und dem Schlüssel, mit dem die
131     Daten verschlüsselt wurden (C<blzDbhN~8Q28esq4JrfRWw>), durch ein Komma
132     getrennt.
133    
134     Das sind übrigens (mehr oder weniger) base64-encodete Daten, dekodiert
135     sieht der Schlüssel so aus:
136    
137     CHK@
138     ID = a5f02ff487a360f2d0c1a4cb5c382e12452188d50c0302
139     Key = 6e5cc36e137ef10dbc7acab826b7d15b
140    
141     (Man kann daraus tatsächlich ablesen, das der Datenblock 4k groß (0x0c
142     am Ende der ID bedeutet 2**12) ist und Twofish benutzt. Aber derartige
143     Details überläßt man lieber einem Perl-Modul).
144    
145     Nur der erste Teil wird als Anfrage ins Freenet geschickt, der zweite Teil
146     bleibt im lokalen Freenet-Daemon. Wird nun der Datenblock geliefert, kann
147     er mit dem Schlüssel dekodiert werden und wird an den Benutzer geliefert.
148    
149     Das zweite Problem (der Skalierbarkeit) wird durch lineare
150     statt exponentielle Suche gelöst: mit jeder Anfrage wird eine
151     I<Hops-To-Live>-Angabe (HTL, ähnlich wie das TTL in IPv4)
152     verknüpft. Diese Zahl, die üblicherweise zwischen 5 und 25 (maximal)
153     liegt, gibt an, wie viele Knoten die Anfrage maximal weitergeleitet
154     wird. Jeder Knoten, der die Daten nicht lokal vorrätig hat, leitet sie an
155     denjenigen Knoten weiter, der die Daten am wahrscheinlichsten hat. Da das
156     Verfahren linear ist und nicht exponentiell, skaliert es ebenfalls linear
157     mit der Netzwerkgröße.
158    
159     Lädt man das Dokument, wird man mit Metadaten und "normalen" Daten
160     konfrontiert.
161    
162     Metadaten:
163    
164     Revision=1
165     EndPart
166     Document
167     Info.Format=image/png
168     End
169    
170     Daten:
171    
172     PNG^Z...
173    
174     Nun stellt sich die Frage, wie man sicherstellt, daß Daten im Freenet
175     bleiben, da es nur endliche Speicherkapazität hat. Die Antwort ist
176     überraschend: Es geht nicht. Wenn ein Knoten sich entscheidet, Daten
177     zu löschen oder Platz für neue Daten zu schaffen, gehen zwangsläufig
178     andere Daten verloren. Dagegen hilft nur wiederholtes Einfügen und der
179     Wunsch, daß die eigenen Daten beliebt genug sind, damit sie abgerufen
180     werden und damit länger überleben.
181    
182     Absolute Anonymität und nichtnachvollziehbarkeit von Transaktionen ist
183     eben nicht vereinbar mit garantierter Datenspeicherung. Könnte man
184     garantiert Nachweisen, das ein Dokument existiert, so wäre es für
185     einen Knotenbetreiber schlecht möglich, Wissen über die Art der Daten
186     abzustreiten, die er speichert.
187    
188     =head3 SSK-Schlüssel
189    
190     Der zweite gebräuchliche Schlüsseltyp, mit dem man im Freenet
191     konfrontiert wird, ist ein C<SSK>-Schlüssel (C<SSK> steht für I<Signed
192     Subspace Key>). Hier ist einer:
193    
194     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM
195    
196     Zwei Dinge unterscheiden Sie von CHK-Schlüsseln:
197    
198     Erstens ist ein SSK-Schlüssel eigentlich ein Schlüsselpaar, ein
199     Schlüssel (der private Schlüssel) muss für das Einfügen von Daten
200     benutzt werden, der zweite Schlüssel (der öffentliche Key) wird für
201     das Auslesen der Daten benutzt. Da die Daten signiert sind, kann nur der
202     oder die Besitzer des privaten Schlüssels Daten unter diesem einfügen,
203     während die Besitzer des öffentlichen Schlüssels überprüfen könenn,
204     ob die Daten tatsächlich mit dme richtigen Schlüssel signiert wurden.
205    
206     Zweitens ist die ID, unter dem diese Date abgelegt werden, nicht von den
207     Daten sondern nur vom "Namen" (dem Key) abhängig, man kann also URIs
208     erzeugen auf Dateien, die noch nicht eingefügt wurden.
209    
210     Diese Art Schlüssel ist relativ ineffizient und kann keine großen
211 root 1.5 Datenmengen speichern (<= 32KB!), SSKs werden also vorwiegend für
212     Weiterleitungen auf CHKs benutzt.
213 root 1.2
214     Der obige SSK gehört übrigens zur "Freenet Explained"-Freesite, die die
215     einzelnen Schlüsseltypen (und mehr) erklärt. Finden kann man sie hier:
216    
217     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//
218    
219     bzw., wenn man einen Freenet-Daemon am Laufen hat, hier:
220    
221     http://127.0.0.1:8888/SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//
222    
223     =head1 Und das ganze in Perl
224    
225     Das ganze wäre relativ witzlos, wenn man nur irgendwelche
226     langweiligen Tools und/oder Java benutzen kann. Es gibt
227     zwei Perl-Module (bzw. Modulfamilien), C<Net::Freenet::FCP>
228     (C<http://www.sf.net/projects/perlfcp>) und C<Net::FCP>.
229    
230     Ersteres ist älter (und vielleicht besser benannt) und konzentriert
231     sich mehr auf die Verarbeitung von Metadaten, ist aber recht schwach
232     beim eigentlichen Protokoll-Handling. Letzteres ist sehr gut beim
233     Protokoll-Handling und stark beim Kodieren und Dekodieren von Daten,
234     überläßt das Metadatenhandling aber dem Programmierer.
235    
236     C<Net::FCP> stammt von mir und ist entstanden, weil ich das andere Modul,
237     die es nur im Freenet gab, nicht herunterladen konnte (tja, so ist das
238     eben als Freenetter). Daher gehe ich auch nur auf dieses Module ein :)
239    
240     =head2 Installation
241    
242     Zuerst braucht man einen Freenet-Daemon
243     (http://www.freenetproject.org/). Die Installation ist unglaublich komplex
244     (unter Windows gibt es einen Installer, unter Unix ein paar Skripte). Der
245     Daemon braucht eine Weile, bis er warm wird (Minuten bis Stunden), was man
246     durch Surf-Versuche unterstützen sollte.
247    
248     Das C<Net::FCP>-Paket gibt es ganz normal per CPAN.
249    
250     =head2 Einfache Abfragen
251    
252     Die Abkürzung I<FCP> steht für I<Freenet Client Protocol> und ist das
253     Protokoll zwischen Freenet-Anwedungen und dem Freenet-Daemon. Es ist
254     unglaublich ineffizient und noch dazu nicht verschlüsselt. Dies ist der
255     Grund für die Empfehlung, den Daemon nur auf dem lokalen Rechner laufen
256     zu lassen: Wäre dumm, wenn man aus dem hochsicheren Freenet über eine
257     ungesicherte Internetverbindung die Windows-Sourcen herunterlädt und
258     sicher erwischen läßt (ist alles schon vorgekommen)...
259    
260     In Perl geht dies denkbar einfach:
261    
262     use Net::FCP;
263     my $fcp = new Net::FCP;
264    
265     Man kann dem Konstruktor von C<Net::FCP> direkt Namen und Port des
266     Freenet-Daemons übergeben. Üblicherweise holt er sich diese aber aus dne
267     Environment-Variablen C<FREDHOST> und C<FREDPORT>, die auf C<127.0.0.1>
268     bzw. C<8481> defaulten.
269    
270     Übrigens wird nur eine virtuelle Verbindung aufgebaut. Möchte man
271     sicherstellen, daß tatsächlich ein Daemon erreichbar ist, kann man einen
272     C<client_hello>-Request ausführen oder einfach loslegen.
273    
274     Im Normalfall heißt das also:: no configuration required.
275    
276     Hat man ein C<Net::FCP>-Objekt, kann man schon alle Requests durchführen,
277     die man amchen möchte:
278    
279     my ($meta, $data) = @{ $fcp->client_get (
280     "freenet:SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//"
281     15
282     ) };
283    
284     Der C<client_get>-Request erwartet zwei Argumente: eine Freenet-URI
285     (C<< freenet:<freenet-schlüssel> >>) und eine HTL. Letztere muss man
286     nicht angeben, man sollte es aber, und vor allem sollte man dem Benutzer
287     die Wahl der HTL überlassen. Das dritte, optionale, Argument ist für
288     spezielle Anwendungen: am besten ignorieren.
289    
290     Als Ergebnis erhält man immer eine Array-Referenz mit den Metadaten
291     und den Daten. Immer. Sollte ein Fehler auftreten wird eine Exception
292     ausgelöst (auf Perl: das Modul C<die>d). C<eval {}> hilft also im
293     Zweifelsfalle, für einfache Anwendungen ist das aber overkill.
294    
295     Bricht das Programm ab, so sieht das folgendermaßen aus:
296    
297     Net::FCP::Exception<<short_data,reason:unexpected eof or internal node error>>
298    
299     Oder, wenn die Daten nicht gefunden wurden:
300    
301     Net::FCP::Exception<<data_not_found,>>
302    
303     Letzteres bedeutet übrigens nicht aufgeben, sondern nochmal versuchen,
304     z.B. mit einer höheren HTL. Ein fehlgeschlagener Versuch mit hoher HTL
305     bedeutet imemr noch nicht aufgeben. Daemons in der "Nähe" wissen nun,
306     das die Daten verlangt werden. Nach einiger Zeit ist es wahrscheinlich,
307     das die Daten auf einmal in der Nähe liegen: daher nie aufgeben, nochmal
308     probieren. Niemand hat behauptet, man bekäme die Daten einfach aus dem
309     Freenet wieder heraus...
310    
311     Die Metadaten sind als Textdokument gespeichert. das Parsen dieser
312     Metadaten ist aber relativ krank, weshalb das Perl-Modul einen Hash mit
313     den geparsten Daten liefert.
314    
315     Die Metadaten und Daten kann man ausgeben:
316    
317     use Data::Dumper;
318     print STDERR Dumper $meta;
319     print $data;
320    
321     Dies ist genau der Quelltext für das "Tool" C<eg/fetch1>, mit dem man
322     I<einen> Freenet-Datenblock herunterladen kann.
323    
324     Ich benutze es üblicherweise so:
325    
326     eg/fetch1 SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3// >data
327    
328     Das schreibt die Daten in eine Datei und gibt gleichzeitig dessen
329     Metadaten aus.
330    
331     Für den Key C<SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//> ergibt dies:
332    
333     'version' => { 'revision' => '1' },
334     'document' => [
335     {
336     'info' => { 'format' => 'image/png' },
337     'redirect' => { 'target' => 'freenet:CHK@pMKWiJyE3t-ZWvL1W9VdZvB3jcIKAwI,7pdyQiW7Hp9-8fEp-25C2w' },
338     'name' => 'activelink.png'
339     },
340     {
341     'info' => { 'format' => 'text/plain' },
342     'redirect' => { 'target' => 'freenet:CHK@2hewSY-I5ZhWpJ84dwhPbtdlvyIKAwI,ovcsrpD79xZfoW2021z91w' },
343     'name' => 'description.txt'
344     },
345     {
346     'info' => { 'format' => 'text/html' },
347     'redirect' => { 'target' => 'freenet:CHK@kME~NY2lX3q-em40ExTAFkyr~NkQAwI,3T2hFmTrPbLhCBANdy4OkA' },
348     'name' => 'index.html'
349     },
350     {
351     'info' => { 'format' => 'text/html' },
352     'redirect' => { 'target' => 'freenet:SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4' },
353     'name' => '.next'
354     },
355     {
356     'info' => { 'format' => 'text/html' },
357     'redirect' => { 'target' => 'freenet:CHK@kME~NY2lX3q-em40ExTAFkyr~NkQAwI,3T2hFmTrPbLhCBANdy4OkA' }
358     }
359     ],
360     'raw' => 'Version
361     Revision=1
362     [... gekürzt...]
363     Info.Format=text/html
364     End
365     '
366     };
367    
368     Die eigentlichen Daten sind leer (Null Byte gross), das Dokument enthät
369     also nur Metadaten! Man beachte vor allem die teilweise recht tiefe
370     Verschachtelung.
371    
372     Der Key C<< $metadata->{raw} >> enthält die Metadaten, wie sie vom
373     Freenet kamen. Dies ist notwendig, da man manchmal die Metadaten
374     zum Prüfen in der exkaten Form benötigt, wie sie hochgeladen
375     wurden. Ansonsten kann man sie ignorieren.
376    
377     Der Key C<< $metadata->{document} >> enthält Informationen über das
378     Dokument oder in diesem Fall die Dokumente: Ein Hash pro Dokument. Schaut
379     man sich den Inhalt an (ein Array), so sieht man in jedem Hash ein
380     C<name>-Key, der den Namen des Subdokuments angibt (bis auf den letzten,
381     darauf komme ich gleich).
382    
383     Schaut man sich dir Freenet-URIs dieser Freesite an, so sieht man
384     derartige URIs:
385    
386     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3// # Hauptseite
387     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//activelink.png # Icon
388     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//description.txt # Beschreibung für Spider
389    
390     Wenn diese Dokumente mit C<client_get> anfordert, bekommt man immer obiges
391     Dokument. Freenet ignoriert nämlich alles, was hinter dem C<//> steht
392     und liefert daher immer das gleiche Dokument aus dem SSK-Bereich. (Darauf
393     verlassen soltle man sich nicht unbedingt, in Zukunft reagiert der
394     Freenet-Daemon vielleicht anders. Als Freenetter hat mans eben nicht
395     leicht).
396    
397     Der Teil hinter den beiden Slashes (C<//>) ist nun das Unterdokument, das
398     man über den Namen identifizieren kann:
399    
400     $activelink = grep $_->{name} eq "activelink.png",
401     @{ $metadata->{document} };
402    
403     Fehlet der C<name>-Eintrag ist es der Eintrag mit dem leeren Namen, in
404     diesem Fall der erste Link, der nichts nach den C<//> stehen hat. Der
405     C<name>-Key I<kann> vorhanden und leer sein: Freenet überprüft die Daten
406     nicht, daher gibt es durchaus unterschiedliche Auslegungen für das genau
407     Format.
408    
409     Die Metadaten sind etwas genauer in dem *hüstel* etwas veralteten
410     *hüstel* I<Freenet Explained>-Dokument beschrieben.
411    
412     Möchte man nun das C<activelink.png> herunterladen muss man in C<<
413     $activelink->{redirect} >> nachsehen. Meistens verweist dies auf ein
414     C<CHK>-Schlüssel. Letztere sind sozusagen Allgemeingut: jeder kann
415     sie benutzen, und da sie nicht änderbar sind, kann sie auch niemand
416     fälschen, daher muss man sie nicht signieren.
417    
418     Auf C<< $activelink->{info}{format} > sollte man sich übrigens nicht
419     verlassen.
420    
421     Und sollte kein C<redirect>-Key vorhanden sein, so ist das Dokument im
422     mitgelieferten Datenblock. Alles ganz logisch, einfach, und klar, wie man
423     sieht.
424    
425     Egal, der CHK-Key für C<activelink.png> liefert folgendes:
426    
427     'version' => { 'revision' => '1' },
428    
429     Und die Daten stellen ein PNG dar. Naja, nicht gerade üppig, die
430     Metadaten, aber man hätt's ja im Link auf dieses Dokument sehen
431     können. Als Freenetter hat mans nicht leicht.
432    
433     Aber mit diesen Erklärungen kann man schon einfacher Freenet-Spider
434     bauen. Die wichtigsten Tools sind veraltete und spärliche Dokumente
435     wie I<Freenet Explained> und Tools wie C<Data::Dumper>. "Veraltet" ist
436     übrigens nicht immer schlecht, da viele Dokumente auch alt sind oder sich
437     eh' nicht exakt an den "Standard" halten. Mit der Zeit wird hier sicher
438     eine Besserung eintreten.
439    
440     =head2 Methoden des Freesite-Managements
441    
442     Oder: "Wenn man Inhalte nicht mehr verändern kann, wie verändert man
443     sie?"
444    
445     Die offensichtliche Methode ist: "Man macht sie veränderbar". In
446     der Freenet-Gemeinde geistert seit langer Zeit der Mythos des
447     I<TUK>-Schlüssels, des I<Time Updatable Keys>. Diese Schlüssel tauchen
448     immer auf, wenn jemand über veränderbare Schlüssel spricht. Leider
449     weiss niemand, wie man sie implementieren soll, und ganz persönlich
450     fürchte ich, es wird niemals veränderbare Keys geben.
451    
452     =head3 "Editions"
453    
454     Die nächstliegende Methode nutzt die Eigenschaft von SSKs (genauer:
455     redirects) aus, das man den Namen sofort generieren kann, den Inhalt aber
456     erst später einfügt.
457    
458     Dies nutzt man aus, indem man I<Editionen> herausgibt und
459     durchnummeriert. Jede Edition enthält einen Link auf die nächste. Bis
460     man die nächste Edition einfügt, wird der Link-Inhalt nicht gefunden.
461    
462     Üblicherweise benutzt man in HTML ein C<IMG>-Element mit Link auf ein
463     Bild aus der nächsten Edition. Sieht man das Bild, weiss man, die
464     nächste Edition existiert und kann sich hinklicken.
465    
466     I<Freenet Explained> benutzt diese Methode. Der Link auf Edition 4 lautet:
467    
468     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4// # Link-Ziel
469     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4//activelink.png # IMG-SRC
470    
471     Beides existierte nicht als ich diesen Artikel schrieb (bzw. Freenet
472     konnte es nicht finden :). Wenn eine neue Edition heruasgegeben werden
473     soll, fügt der Autor einfach einen neuen Datenblock mit vielen Redirects
474     unter obigem SSK-Key ein.
475    
476     Ein C<.next>-Eintrag ist ebenfalls vorhanden: manche Spider folgen diesem
477     und können auf diese Weise immer die aktuelle Edition anzeigen, sofern
478     sie häufig genug scannen.
479    
480     Editionen sind einfach für den Autor einer Freesite und erfordern
481     keine spezielle Unterstützung. In der Benutzung sind sie allerdings
482     umständlich und häufige Updates sind auch nicht effizient.
483    
484     Daher hat sich eine zweite Methode entwickelt, die gerade bei häufig
485     geänderten Inhalten besser funktioniert:
486    
487     =head3 "Date Based Redirects"
488    
489     Freesites, die "Date Based Redirects" benutzen (kurz "DBR-Sites")
490     funktionieren ähnlich wie Editionen-basierte Freesites, nur wird statt
491     einer fortlaufenden Nummer die aktuelle Zeit benutzt.
492    
493     Die Freesite I<The Freenet Help Index>
494     (C<SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/FreenetHelp//>) benutzt diese
495     Methode. Folgende Metadaten wurden dazu hinterlegt:
496    
497     'version' => { 'revision' => '1' },
498     'document' => [
499     { 'date_redirect' => {
500     'increment' => '15180',
501     'target' => 'SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/FreenetHelp'
502     }
503     }
504     ],
505    
506     Statt einem einfachen C<redirect> gibt es jetzt einen
507     C<date_redirect>-Eintrag, und darin ein C<target> (dürfte bekannt sein)
508     und der Key C<increment>. Letzterer darf wie üblich fehlen, man sollte
509     dann C<86400> annehmen (ein Tag hat 86400 Sekunden). In diesem Beispiel
510     wird 86400 genommen (Hexadezimal 15180).
511    
512     Habe ich eigentlich schon erwähnt das jede Zahl im FCP-Protokoll oder
513     im Freenet hexadezimal kodiert wird? Also, praktisch immer, außer wenn
514     es mal nicht so ist. Und wozu man einen Offset addiert ist mir auch
515     schleierhaft. In der freien Wildbahn habe ich sowieso noch keinen gesehen.
516    
517     Der Link in C<target> ist nicht vollständig (unter anderem fehlt
518     hinten ein C<//>). Der komplette Link wird generiert, "indem die
519     aktuelle Zeit (POSIX-Zeit, üblicherweise das, was C<time> liefert),
520     auf C<increment>-Schritte gerundet und C<offset> addiert, als
521     Big-Endian-Hexadezimalzahl nach dem ersten Slash mit folgendem
522     Minuszeichen eingefügt wird."
523    
524     Und jetzt in Perl - zum Verstehen:
525    
526     my $doc = $metadata->{document][0]; # Oder [1] oder ...
527    
528     my $increment = (hex $doc->{increment}) || 86400;
529     my $offset = (hex $doc->{offset}) || 0;
530     my $target = $doc->{target} || die;
531    
532     my ($head, $tail) = split /\//, $target, 2;
533    
534     my $NOW = time;
535     my $time = $NOW - $NOW % $increment + $offset;
536    
537     my $result = sprintf "%s/%x-%s", $head, $time, $tail;
538    
539     Für "jetzt" (C<time() == 1084388445>) liefert der Algorithmus folgenden Link:
540    
541     SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/40a16900-FreenetHelp
542    
543     Und tatsächlich, unter diesem Key findet man wieder ein Dokument mit
544     vielen Redirects.
545    
546     DBRs haben den Vorteil, "automatisch" aktuell zu sein. Solange man nur
547     Redirects einfügt, ist die Belastung durch neue das Einfügen vieler
548     neuer Daten gering, Freenet kommt damit zurecht und löscht alte DBRs
549     bei Bedarf automatisch (ohne natürlich zu wissen, das sich in dne
550     Datenblöcken DBRs befinden, dnen die kann ja niemand dekodieren,d er
551     nicht den Schlüssel besitzt).
552    
553     Der Nachteil ist, das man eine aktuelle Uhrzeit braucht um die Site zu
554     finden. Schlimemr noch: die Freesite muss regelmäßig neu eingefügt
555     werden, eben alle C<increment> Sekunden.
556    
557     DBR-Freesites, die nicht mehr maintained werden, verschwinden deshalb fast
558     sofort, solange man nicht einen Zeitpunkt kennt, zu dem sie noch (bzw.
559     schon!) existierte.
560    
561     All dieses I<sollte> eigentlich in einem Metadata-Modul stattfinden. So
562     weit bin ich aber nicht, da mein Hauptziel das effiziente Herunterladen
563     großer Dateien ist.
564    
565     Womit ich beim Thema wäre.
566    
567     =head1 Effizient herunterladen
568    
569 root 1.4 Transfers im Freenet zeichnen sich durch zwei Eigenschaften aus: hohe
570     Latenz und vergleichsweise hoher Durchsatz. Dies ist ungewöhnlich, ergibt
571     sich aber aus dem Suchverfahren: die Suche nach einem Datenblock kann sehr
572     lange dauern (sogar einige Male fehlschlagen bevor die Daten eintreffen),
573     sie sind jedoch einfach parallelisierbar.
574    
575     Wie in Java üblich, erreichte man dies früher durch massive Benutzung
576     von Threads. Der Freenet-Referenz-Daemon ist davon inzwsichen abgekommen
577     (zu langsam, zu viel Speicher und zu hohe Komplexität), doche viele
578     Clients haben auch heute noch Voreinstellungen wie "Anzahl der Threads pro
579     Download" und viele sind auch so implementiert.
580    
581     Da Perls Thread-Implementierung ein Prozessmodell emuliert (kaum sharing
582     möglich) ist der overhead ebenfalls entsprechend hoch, und man erhält
583     durch Threads keinen nennenswerten Vorteil, ausser das alles etwas
584     langsamer läuft und man besser zuschauen kann :)
585    
586     Für C<Net::FCP> (oder besser: für mich) kam es deshalb nicht in Frage,
587     Parallelisierung auf Thread-Basis zu implementieren. Da sProblem auf
588     den Modul-Benutzer abzuschieben erschien mir auch nicht fair, daher
589     unterstützt C<Net::FCP> die Module L<Coro>, L<Event>, L<Glib> oder L<Tk>,
590     bzw. deren Event-System. Man hat also die Wahl, auf welcher Basis man sein
591     Programm aufbaut, und es geht auch ganz ohne Event-System (wie die obigen
592     einfachen Beispiele zeigen).
593    
594     =head2 Transaktionen
595    
596     C<Net::FCP> benutzt für jeden Request eine I<Transaktion>. Das ist ein
597     Objekt, das den Zustand und eintreffende Ergebnisse speichert.
598    
599     Jede Anfrage, (z.B. C<client_get>) wird intern in ein solches
600     Transaktionsobjekt verwandelt. Für jede "einfache" Methode wie
601     C<client_get> oder C<insert_private_key> gibt es eine entsprechende
602     Methode mit C<txn_>-Präfix, die statt des Resultats ein solches Objekt
603     liefert.
604    
605     Transaktionsobjekte besitzen einige Methoden, mit denen sie konfiguriert
606     werden können oder Ergebnisse abgefragt werden können. Die wichtigste
607     ist die C<result>-Methode, die auf das Ergebnis wartet und es
608     zurückliefert:
609    
610     # $fcp->client_get wird intern so implementiert:
611     my $txn = $fcp->txn_client_get (...);
612     return $tdn->result;
613    
614     Es ist übrigens immer gefahrlos, Transaktionsobjekte zu erzeugen. Etwaige
615     Fehler bei der Durchführung werden immer erst beim Aufruf von C<result>
616     und immer als Perl-Exceptions gemeldet.
617    
618     Um also alle Freenet-Objekte im Array C<@download> gleichzeitig
619     anzufordern, reicht folgender Code:
620    
621     map $_->result,
622     map $_->txn_client_get ($_),
623     @download;
624    
625     Zuerst werden alle URIs auf eine entsprechende Transaktion gemapped, und
626     dann von allen Transaktionen das Resultat angefordert.
627    
628     Natürlich brauchen manche Zugriffe länger als andere. Wenn Transaktionen
629     früher beendet werden, sollte man gleich eine neue starten, um Freenet
630     bei Lauen zu halten.
631    
632     Dazu benötigt man ein Signal, das eine Transaktion beendet ist. Dies
633     macht C<Net::FCP> mit Hilfe eines Callbacks:
634    
635     my $txn = $fcp->txn_client_get (...)
636     ->cb (\&callback);
637    
638     Diesen setzt man mit Hilfe der C<cb>-Methode. Alle Methoden, die ein
639     Transaktionsobjekt konfigurieren liefern das Transaktionsobjekt zurück,
640     man kann also Aufrufketten wie im Beispiel bilden.
641    
642     Der Callback wird aufgerufen, wenn der Request (erfolgreich oder nicht)
643     beendet wurde.
644    
645     Nun braucht man allerdings die Hauptschleife des jeweiligen Event-Systens,
646     da man ja nicht mehr auf ein bestimmtes Ergebis wartet, sondern auf ein
647     beliebiges.
648    
649     Dazu sollte man C<Net::FCP> auf ein bestimmtes Event-System zwingen. Das
650     folgende Beispiel tut dies und lädt die Kommandozeileargumente parallel
651     herunter:
652    
653     use Net::FCP qw(event=Event); # oder event=Glib, oder...
654    
655     my $fcp = new Net::FCP;
656     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
657    
658     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
659    
660     Die Transaktionsobjekte werden übrigens nicht gespeichert, denn sie
661     werden dem Callback übergeben und nur dort werden sie gebraucht. Der
662     Callback könnte so aussehen:
663    
664     sub finished {
665     my ($txn) = @_;
666     my ($meta, $data) = @{ $txn->result };
667     # tue etwas
668     }
669    
670     Oder, mit Fehlerprüfung:
671    
672     sub finished {
673     my ($txn) = @_;
674     my ($meta, $data) = eval { @{ $txn->result } };
675     if ($@) {
676     warn "fehler: $@, wird einfach ignoriert :)"
677     } else {
678     warn "Lo and Behold! We got soemthing!";
679     # tue etwas
680     }
681     }
682    
683     =head2 Fortschritt ist nicht aufzuhalten
684    
685     Da Zugriffe recht lange dauern können, schickt Freenet regelmäig
686     Fortschrittsreports. Man kann sich das vorstellen wie C<warn> und
687     C<die>: C<warn> liefert wichtige Informationen, C<die> einen Fehler.
688    
689     Die Fortschrittsanzeigen werden normalerweise nicht von C<Net::FCP>
690     ausgegeben (wäre irgendwie kontraproduktiv in einem GUI-Programm), man
691     kann aber einen Callback installieren:
692    
693     my $fcp = Net::FCP progress => \&progress;
694    
695     Ein solcher Progress-Callback könnte so aussehen:
696    
697     sub progress_cb {
698     my ($self, $txn, $type, $attr) = @_;
699    
700     warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
701     }
702    
703     Möchte man Informationen für den Callback bereitstellen, so bietet sich
704     die C<userdata>-Methode von Transaktionsobjekten an.
705    
706     Hier ist ein kompletter parallelisierender Client mit Fortschrittsanzeige:
707    
708     my $fcp = Net::FCP progress => \&progress;
709    
710     Ein solcher Progress-Callback könnte so aussehen:
711    
712     use Net::FCP qw(event=Event); # oder event=Glib, oder...
713    
714     my $fcp = new Net::FCP progress => \&progress_cb;
715    
716     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
717    
718     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
719    
720     sub finished {
721     my ($txn) = @_;
722     my ($meta, $data) = eval { @{ $txn->result } };
723     if ($@) {
724     warn "$txn: fehler: $@, wird einfach ignoriert :)\n"
725     } else {
726     warn "$txn: Lo and Behold! We got something!\n";
727     # tue etwas
728     }
729     }
730    
731     sub progress_cb {
732     my ($self, $txn, $type, $attr) = @_;
733    
734     warn "$txn: progress<$txn,$type," . (join ":", %$attr) . ">\n";
735     }
736    
737     Startet man ihn mit einigen gültigen und ungültigen Freenet-URIs,
738     bekommt man vieleicht folgende Ausgabe:
739    
740     Net::FCP::Txn::ClientGet=HASH(0x81e0f64): fehler: Net::FCP::Exception<<uri_error,reason:Unspecified document name>>, wird einfach ignoriert :)
741     Net::FCP::Txn::ClientGet=HASH(0x81e7254): fehler: Net::FCP::Exception<<uri_error,reason:Unknown keytype>>, wird einfach ignoriert :)
742     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c),data_found,data_length:74:metadata_length:74>
743     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c),data,chunk:116:total:116:received:116>
744     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): Lo and Behold! We got something!
745     ... usw.
746    
747     Benutzt man das C<Coro>-Modul, so kann man Callback-basierte und Thread-
748     (bzw. Coroutinen-) basierte Programmierung mischen:
749    
750     # Achtung. Pseudocode :)
751     for my $url (@urls) {
752     async {
753     my ($meta, $data) = @{ $fcp->client_get ($url) };
754    
755     if ($meta ist ein splitfile) {
756     for (@alle_splitfile_blöcke) {
757     $fcp->txn_client_get (...)
758     ->cb (\&save_splitfile_block);
759     }
760     } else {
761     # direkt speichern
762     }
763     };
764     }
765    
766     =head2 Splitfiles
767    
768     Das Them Splitfiles möchte ich hier noch erwähnen, aber nicht
769     ausführlich besprechen.
770    
771     Da die Datenblöcke im Freenet (zur Zeit) nicht größer als 1MB sein
772     können, müssen größere Dateien (Filme z.B.) aufgeteilt werden. Wenn
773     man viele Datenblöcke für eine Datei braucht, erhöht sich die
774     Wahrscheinlichkeit dafür, das ein Block mal fehlt oder nicht gefundne
775     werden kann, stark auf einen Wert, der das Herunterladen großer Dateien
776     unmöglich macht.
777    
778     Daher wird mit Fehlerkorrekturcodes die Anzahl der Blöcke um 50%
779     erhöht. Metadaten für ein Splitfile sehen so aus:
780    
781     'version' => { 'revision' => '1' },
782     'document' => [ {
783     'info' => {
784     'checksum' => '3bab309132f812cb2a281357b0dc1036920672e2',
785     'format' => 'video/mpeg',
786     'description' => 'Onion FEC v1.2 file inserted by Fuqid'
787     },
788     'split_file' => {
789     'algo_name' => 'OnionFEC_a_1_2',
790     'check_block_count' => '3f',
791     'block_count' => '7f',
792     'check_block' => {
793     '33' => 'freenet:CHK@eTNQmnQ8aoZor3CBzTSsI~oPn6EUAwI,I4XDi1SbdKlFnyGoIRZBLg',
794     '32' => 'freenet:CHK@Ee1h~wxPSqSOFKg3WZ90KXqwQZMUAwI,ppnEYTKkp7Dyk1z3Sempqw',
795     '1e' => 'freenet:CHK@5gBBquHWc7WKjfjO27kOtU0hZgkUAwI,OkfOgz9BPvMxwvtX~wfVNA',
796     [viele Zeilen fehlen hier :]
797     '3d' => 'freenet:CHK@iIT1KzjEyXptPyOToFsyyytV-roUAwI,9u3qhWXzi8v--eY7Onag1g',
798     '5' => 'freenet:CHK@EeVsnou1WxkZg9CNBNVzvTTQ1l0UAwI,q6edARpUGxg5Oh-cc79WBA'
799     },
800     'block' => {
801     '33' => 'freenet:CHK@j522RLeAYwiNah2ZK1D1q4UDnFgUAwI,XwoBGzhdGMkyXFwI5f9iyw',
802     '32' => 'freenet:CHK@VU5v9PNahADgvy-Dnk8SCt9ZKbYUAwI,iX~d0b-YKcwt3Yk0rmpM9w',
803     '5d' => 'freenet:CHK@-wbXzvl32R8aSuotK~uZbJp14zsUAwI,-bgXeuzi6SpKYvbwQeYtyA',
804     [viele Zeilen fehlen hier :]
805     '43' => 'freenet:CHK@zMXcOMm1DlNB4ee1cftvdK4~QukUAwI,o99L7InOc4E7adXUXGjrlg',
806     '5' => 'freenet:CHK@1ZDivRb~iBdn2NYTKQbDq1yCPWAUAwI,B9fXQ8jF~DzmfOx~PTZkAA',
807     '3d' => 'freenet:CHK@ZGxa5ipGqKuZEqQIf6rbr-g8btMUAwI,f0xOciFlFA0bmmxoJRzaLw'
808     },
809     'size' => '7e8e804'
810     }
811     } ],
812     }
813    
814     Die Datenblöcke und die zusätzlichen Korrekturblöcke sind getrennt
815     aufgeführt. Man braucht nur so viele Blöcke insgesamt, wie die Datei
816     groß ist. Den Rest kann man aus den heruntergeladenen erzeugen.
817    
818     Wie genau die Daten auf die Blöcker verteilt werden unterliegt einem
819     Algorithmus und ist nicht in dne Metadaten gespeichert. Der Grund ist,
820     das jedes Programm denselben Algorithmus benutzen muss und Dateien daher
821     immer gleich auf die Blöcke verteilt werden, so das Mehrfacheinfügen ins
822     Freenet keine Duplikate erstellt.
823    
824     Der genauen Algorithmus kann man in der Datei C<bin/fmd>, dem I<Freenet
825     Mass Downloader> in der Funktion C<state_splitfile> nachlesen. Der Code
826     ist weder besonders klar, noch besonders kurz noch besonders hübsch. Auch
827     dieser Teil wird irgendwann einmal in ein Modul fließen, damit er anderen
828     zugänglicher wird.
829    
830     Interessant ist noch, das man CHK-Inhalte, die aus dem Freenet
831     heruntergeladen werden einfach auf Konsistenz prüfen kann: Sie enthalten
832     schließlich den SHA1-Hash der Daten:
833    
834     use Net::FCP::Util;
835    
836     my ($meta, $data) = @{ $fcp->client_get ($uri) };
837    
838     # Extrahiere den Hash aus der URI
839     my $k1 = Net::FCP::Util::extract_chk_hash $uri;
840     # Berechne den Hash aus den Daten
841     my $k2 = Net::FCP::Util::generate_chk_hask "$meta->{raw}$data";
842    
843     # Idealerweie stimmen beide überein...
844     $k1 eq $k2 or die "Key/Content mismatch!";
845    
846     Zwar ist der Freenet-Daemon angeblich perfekt, aber früher passierte
847     es tatsächlich oft, daß man korrupte Daten geliefert bekam. Retryen
848     half. Man hat es eben nicht so leicht...
849    
850     Heute ist das zwar nicht mehr der Fall (...), aber prüfen kostet fast
851     nichts.
852    
853 root 1.2 =head1 Inserts
854 root 1.1
855 root 1.4 Das Freenet lebt von vielen Dingen: Den Entwicklern, den Benutzern die
856     ihr Interesse bekunden, den Regierungen, die die Meinungsäußerung
857     einschränken aber vor allem dadurch, das es sinnvolle Inhalte darin gibt.
858    
859     Ich bin mir nicht so sicher, I<ob> es so viele sinnvolle Inhalte gibt,
860     aber noch gab es ja auch keine 1.0-Release...
861    
862     Nun ja, solange man nichts ins Netz stellt, darf man auch nicht klagen.
863    
864     Das kann man ändern, indem man so langweilige Dinge wie den I<Freenet
865     Insertion Wizard> oder das beliebte I<Fuqid> benutzt. Aber die benutzen
866     kein Perl und sind damit langweilig.
867    
868     Mit C<Net::FCP> geht es erstmal sehr einfach:
869    
870     $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
871    
872     (Auch hier gibt es natürlich wieder die entsprechende C<txn_>-Variante).
873 root 1.1