ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/pws2004/freenet.mgp
Revision: 1.1
Committed: Thu Jun 17 20:06:18 2004 UTC (22 years, 3 months ago) by root
Branch: MAIN
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 %include "std.mgp"
2     %%default 1 bimage "gtk/gtk-window.jpg" 1024x768, center
3     %page
4     %%bimage "gtk/screenshot.jpg" 1600x1200
5    
6     Freenet
7    
8     "I worry about my child and the Internet all
9     the time, even though she's too young to have
10     logged on yet. Here's what I worry about. I
11     worry that 10 or 15 years from now, she will
12     come to me and say 'Daddy, where were you
13     when they took freedom of the press away from
14     the Internet?'"
15    
16     --Mike Godwin, Electronic Frontier Foundation
17    
18    
19     %center
20     %%image "gtk/hello31.jpg" 0 200 200 1
21    
22     %page
23    
24     Ziele
25    
26    
27     Projekt, Dokumentation und Implementation frei und für jeden zugänglich
28     %pause
29    
30     Informationsfreiheit ohne Zensur
31     %pause
32    
33     garantiert anonyme Veröffentlichung
34     %pause
35    
36     garantierter Zugang zur Information
37    
38     %page
39    
40     Implementation
41    
42    
43     virtuelles Netzwerk
44     %pause
45     Ausfall auch großer Teile des Netzes werden kompensiert
46    
47     %pause
48     einzelne Knoten
49     %pause
50     kennen die gespeicherten Daten nicht
51     %pause
52     können Datenverkehr andrere Knoten nicht lesen
53     %pause
54     kennen Sender und Empfänger von Daten nicht
55    
56    
57     %page
58    
59     Freenet aus Benutzersicht
60    
61     %pause
62     Alle Daten werden in Datenblöcken (keys) organisiert
63     %pause
64     Keys werden in das Netzwerk "eingefügt"
65     %pause
66     Maximale Größe eines Datenblocks: 1MB
67     %pause
68     HTML-Seiten werden üblicherweise in einem Key gespeichert
69     %pause
70     Große Dateien werden mit forward-error-correction in kleinere Blöcke geteilt
71     Algorithm::FEC
72    
73    
74     %page
75    
76     Freenet-URLs
77    
78     Übliche Form eines Freenet-Links:
79    
80     %font "t3"
81     http://localhost:8888/
82     CHK@pfAv9IejYPLQwaTLXDguEkUhiNUMAwI,blzDbhN~8Q28esq4JrfRWw
83     //Suffix
84     %font "s"
85    
86     %pause
87     http://localhost:8888/ nur der Pfad zum HTTP-Proxy des Knotens
88     %pause
89     CHK ist der Typ (hier: Content Hash Key)
90     %pause
91     Dahinter Folgen Parameter (meist Schlüssel u.ä.), "base64"-codiert
92     %pause
93     Suffix (nach //) werden von Freenet ignoriert
94    
95     %page
96    
97     Content Hash Keys
98    
99     %font "t3"
100     CHK@pfAv9IejYPLQwaTLXDguEkUhiNUMAwI,blzDbhN~8Q28esq4JrfRWw
101     ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
102     Typ Routing-Key Crypto-Key
103     %font "s"
104    
105     %pause
106     Crypto-Key wird aus den Daten generiert
107     verschlüsselt die Daten
108     %pause
109     Routing-Key wird aus dem Crypto-Key generiert
110     dient zum Indizieren und Suchen der Daten
111     %pause
112     Daten können mehrfach eingefügt werden. Da gleiche Daten == gleicher Key wird das Netzwerk nicht belaste
113    
114     %page
115    
116     Signed Subspace Key
117    
118     %font "t3"
119     SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM,Jrd6HtbjJfw1UO1g/YoYo
120     ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
121     Typ Public Key PAgM Entropie + Daten
122     %font "s"
123    
124     %pause
125     Daten werden mit einem private Key signiert
126     %pause
127     maximale Größe 32K
128     %pause
129     mit einem Schüsselpaar können viele Keys eingefügt werden
130     Unterscheidung über angehängte Daten
131    
132     %page
133    
134     Freenet Live
135    
136     %system "/opt/bin/links -g http://129.13.162.73:8888/"
137    
138     %page
139     %page
140     %page
141     %page
142     %page
143     %page
144    
145    
146     =for html
147     <img src="freenet1.png" />
148    
149     =for latex
150     \begin{center}\includegraphics[scale=0.55]{freenet1.png}\end{center}
151    
152     =for pp
153     \IMAGE{src="freenet1.png"}
154    
155     Die hier verwendete URL ist:
156    
157     http://129.13.162.73:8888/...
158     ...SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM/YoYo//Controversy.html
159    
160     =for latex
161     \begin{sloppypar}
162    
163     C<http://129.13.162.73:8888> ist die Adresse meines Freenet-Daemons,
164     üblicherweise C<http://127.0.0.1:8888>, der Sicherheit wegen sollte man
165     immer einen eigenen Freenet-Daemon laufen lassen. Der Schlüssel der
166     ``Freesite'' ist C<SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM> und in dieser wird
167     wiederum das Unterdokument C<YoYo//Controversy.html> angezeigt.
168    
169     =for latex
170     \end{sloppypar}
171    
172     Diese Seite ist Teil eines sehr bekannten Einsprungpunktes ins
173     Freenet. Generell bevorzugt das Freenet-Projekt keine bestimmten
174     Einsprungpunkte. Die Regel ist: kennt man den Einsprungpunkt nicht, findet
175     man die Inhalte nicht; es ist sogar unmöglich zu bestimmten, wieviel oder
176     welche Informationen im Freenet gespeichert sind: Ein Index a'la google
177     ist prinzipbedingt nicht möglich.
178    
179     Obwohl es einige Freenet-Spider und Directory-Freesites gibt ist der
180     überwältigende Teil der Information im Freenet nicht darüber zu
181     erreichen.
182    
183     Neben HTML-Inhalten gibt es auch Mail-, Foren- und Chat-Systeme im
184     Freenet.
185    
186     Die Einspeisung von Daten kann von überall im Netz
187     geschehen. Mehrfacheinspeisung ist möglich (und meistens notwendig) und
188     führt nicht zur Dupliziering von Daten.
189    
190     Natürlich gibts es auch einige Nachteile, die teilweise mit dem Design
191     und teilweise mit der Implementierung zusammenhängen. So ist der einzige
192     verfügbare Freenet-Daemon in Java implementiert und daher leider extrem
193     unportabel (läuft praktisch nur auf Linux/x86 und Windows, da das
194     benötige Sun-Java-JDK auf anderen Platformen nicht verfügbar oder
195     veraltet ist), sehr langsam und vor allem speicherhungrig, und häufig
196     auch sehr instabil.
197    
198     Die aktuellen Versionen des Freenet-Daemons sind alle als ``nicht sicher''
199     deklariert, da erst die Release 1.0 verspricht, alle Ideen umgesetzt zu
200     haben, wovon das Projekt noch 0.5 Versionen entfernt ist :)
201    
202     Designbedingt ist das Freenet eher langsam (extrem hohe Latenz,
203     akzeptabler Durchsatz) und für interaktive Benutzung teilweise
204     ungeeignet, bzw. eine echte Geduldsprobe.
205    
206     Durch die Art der Speicherung kann nicht garantiert werden, daß
207     Informationen überhaupt wiedergefunden werden. ``Unpopuläre'' Information
208     verschwindet, sofern sie nicht regelmäßig eingespeist oder abgerufen
209     wird, automatisch wieder aus dem Freenet.
210    
211     =head3 Die Freenet-Architektur
212    
213     =for latex
214     \begin{sloppypar}
215    
216     Herkömmliche File-Sharing-Netzwerke haben zwei große Nachteile: Entweder
217     sind sie nicht wirklich anonym (selbst anonymisierende Netzwerke oder
218     Proxies sind üblicherweise nicht vor Zugriff durch Regierungen sicher
219     (ein bekanntes Beispiel ist I<JAP>, L<http://anon.inf.tu-dresden.de>, das
220     zwar vollmundig vollkommene Anonymität garantiert, aber schon einmal durch
221     die Polizei gezwungen wurde, Log-Informationenen herauszugeben, wozu die
222     Betreiber nach deutschem Recht verpflichtet sind). Oder sie skalieren
223     nicht, da sie Broadcast-Algorithmen benutzen, die größere Netzwerke von
224     vorneherein ausschließen (bekanntes Beispiel dafür ist I<gnutella>).
225    
226     =for latex
227     \end{sloppypar}
228    
229     Das erste Problem umgeht Freenet (Achtung, vereinfacht!), indem es
230     jeden Datenblock hasht und aus diesem Hash einen Schlüssel für die
231     Verschlüsselung generiert und die Daten damit verschlüsselt (sic).
232    
233     Der Schlüssel wird ein zweitesmal gehasht. Dieser Hash wird zur
234     eindeutigen ID des Datenblocks. Diese ID und der verschlüsselte
235     Datenblock werden ins Freenet eingespeist. Dabei wird immer derselbe
236     Key generiert (da er nur von den Daten abhängt), und der verschlüsselte
237     Block ist ebenfalls immer gleich. Daher kann er problemlos mehrfach und
238     von unterschiedlichen Parteien eingespeist werden ohne die Daten faktisch
239     mehrfach im Freenet abzulegen.
240    
241     Das ist sicher, da ein kryptographisch sicherer Hash (der die Grundlage
242     des Systems bilden muß) nur in eine Richtung funktioniert: Aus der ID
243     (== Hash des Hashes der Daten) kann nicht auf den Schlüssel geschlossen
244     werden. Speichert also ein Netzknoten die Daten und die ID dazu, kann man
245     die Daten zwar abrufen, jedoch nicht entschlüsseln. Selbst der Besitzer
246     eines Knotens kann mit vollkommenen Wissen über alle Vorgänge seines
247     Knotens die Inhalte nicht lesen.
248    
249     Eine weitere Konsequenz dieses Verfahrens ist die Tatsache, das Dokumente
250     niemals verändert werden können: eine Änderung bewirkt eine Änderung
251     des Schlüssels und damit eine neue ID, praktisch eine völlig neue URL.
252    
253     Möchte man z.B. das grüne Blatt der Freesite ``Thought Crime'' herunterladen so
254     wird man mit folgendem Freenet-Key konfrontiert:
255    
256     CHK@pfAv9IejYPLQwaTLXDguEkUhiNUMAwI,blzDbhN~8Q28esq4JrfRWw
257    
258     C<CHK> steht für I<Content-Hash-Key>, der häufigste Schlüsseltyp im
259     Freenet, der das oben beschriebene Verfahren benutzt. Der CHK besteht
260     aus zwei Teilen: der ID, unter der der Schlüssel im Freenet abgelegt
261     wird (C<pfAv9IejYPLQwaTLXDguEkUhiNUMAwI>) und dem Schlüssel, mit dem die
262     Daten verschlüsselt wurden (C<blzDbhN~8Q28esq4JrfRWw>), durch ein Komma
263     getrennt.
264    
265     Das sind übrigens (mehr oder weniger) base64-encodete Daten, dekodiert
266     sieht der Schlüssel so aus:
267    
268     CHK@
269     ID = a5f02ff487a360f2d0c1a4cb5c382e12452188d50c0302
270     Key = 6e5cc36e137ef10dbc7acab826b7d15b
271    
272     (Man kann daraus tatsächlich ablesen, das der Datenblock 4k groß (0x0c
273     am Ende der ID bedeutet 2**12) ist und Twofish benutzt. Aber derartige
274     Details überläßt man lieber einem Perl-Modul).
275    
276     Nur der erste Teil wird als Anfrage ins Freenet geschickt, der zweite Teil
277     bleibt im lokalen Freenet-Daemon. Wird nun der Datenblock geliefert, kann
278     er mit dem Schlüssel dekodiert werden und wird an den Benutzer geliefert.
279    
280     Das zweite Problem (der Skalierbarkeit) wird durch lineare
281     statt exponentielle Suche gelöst: mit jeder Anfrage wird eine
282     I<Hops-To-Live>-Angabe (HTL, ähnlich wie das TTL in IPv4)
283     verknüpft. Diese Zahl, die üblicherweise zwischen 5 und 25 (maximal)
284     liegt, gibt an, wie viele Knoten die Anfrage maximal weitergeleitet
285     wird. Jeder Knoten, der die Daten nicht lokal vorrätig hat, leitet sie an
286     denjenigen Knoten weiter, der die Daten am wahrscheinlichsten hat. Da das
287     Verfahren linear ist und nicht exponentiell, skaliert es ebenfalls linear
288     mit der Netzwerkgröße.
289    
290     Lädt man das Dokument, wird man mit Metadaten und ``normalen'' Daten
291     konfrontiert.
292    
293     Metadaten:
294    
295     Revision=1
296     EndPart
297     Document
298     Info.Format=image/png
299     End
300    
301     Daten:
302    
303     PNG^Z...
304    
305     Nun stellt sich die Frage, wie man sicherstellt, daß Daten im Freenet
306     bleiben, da es nur endliche Speicherkapazität hat. Die Antwort ist
307     überraschend: Es geht nicht. Wenn ein Knoten sich entscheidet, Daten
308     zu löschen oder Platz für neue Daten zu schaffen, gehen zwangsläufig
309     andere Daten verloren. Dagegen hilft nur wiederholtes Einfügen und der
310     Wunsch, daß die eigenen Daten beliebt genug sind, damit sie abgerufen
311     werden und damit länger überleben.
312    
313     Absolute Anonymität und nichtnachvollziehbarkeit von Transaktionen ist
314     eben nicht vereinbar mit garantierter Datenspeicherung. Könnte man
315     garantiert nachweisen, das ein Dokument existiert, so wäre es für
316     einen Knotenbetreiber schlecht möglich, Wissen über die Art der Daten
317     abzustreiten, die er speichert.
318    
319     =head4 SSK-Schlüssel
320    
321     Der zweite gebräuchliche Schlüsseltyp, mit dem man im Freenet
322     konfrontiert wird, ist ein C<SSK>-Schlüssel (C<SSK> steht für I<Signed
323     Subspace Key>). Hier ist einer:
324    
325     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM
326    
327     Zwei Dinge unterscheiden Sie von CHK-Schlüsseln:
328    
329     Erstens ist ein SSK-Schlüssel eigentlich ein Schlüsselpaar, ein
330     Schlüssel (der private Schlüssel) muss für das Einfügen von Daten
331     benutzt werden, der zweite Schlüssel (der öffentliche Key) wird für
332     das Auslesen der Daten benutzt. Da die Daten signiert sind, kann nur der
333     oder die Besitzer des privaten Schlüssels Daten unter diesem einfügen,
334     während die Besitzer des öffentlichen Schlüssels überprüfen können,
335     ob die Daten tatsächlich mit dem richtigen Schlüssel signiert wurden.
336    
337     Zweitens ist die ID, unter dem diese Daten abgelegt werden, nicht von den
338     Daten sondern nur vom ``Namen'' (dem Key) abhängig, man kann also URIs
339     erzeugen auf Dateien, die noch nicht eingefügt wurden.
340    
341     Diese Art Schlüssel ist relativ ineffizient und kann keine großen
342     Datenmengen speichern (<= 32KB!), SSKs werden also vorwiegend für
343     Weiterleitungen auf CHKs benutzt.
344    
345     Der obige SSK gehört übrigens zur ``Freenet Explained''-Freesite, die die
346     einzelnen Schlüsseltypen (und mehr) erklärt. Finden kann man sie hier:
347    
348     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//
349    
350     bzw., wenn man einen Freenet-Daemon am Laufen hat, hier:
351    
352     http://127.0.0.1:8888/SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//
353    
354     =head2 Und das ganze in Perl
355    
356     Das ganze wäre relativ witzlos, wenn man nur irgendwelche
357     langweiligen Tools und/oder Java benutzen kann. Es gibt
358     zwei Perl-Module (bzw. Modulfamilien), C<Net::Freenet::FCP>
359     (L<http://www.sf.net/projects/perlfcp>) und C<Net::FCP>.
360    
361     Ersteres ist älter (und vielleicht besser benannt) und konzentriert
362     sich mehr auf die Verarbeitung von Metadaten, ist aber recht schwach
363     beim eigentlichen Protokoll-Handling. Letzteres ist sehr gut beim
364     Protokoll-Handling und stark beim Kodieren und Dekodieren von Daten,
365     überläßt das Metadatenhandling aber dem Programmierer.
366    
367     C<Net::FCP> stammt von mir und ist entstanden, weil ich das andere Modul,
368     das es nur im Freenet gab, nicht herunterladen konnte (tja, so ist das
369     eben als Freenetter). Daher gehe ich auch nur auf dieses Modul ein :)
370    
371     =head3 Installation
372    
373     Zuerst braucht man einen Freenet-Daemon
374     (L<http://www.freenetproject.org>). Die Installation ist unglaublich komplex
375     (unter Windows gibt es einen Installer, unter Unix ein paar Skripte). Der
376     Daemon braucht eine Weile, bis er warm wird (Minuten bis Stunden), was man
377     durch Surf-Versuche unterstützen sollte.
378    
379     Das C<Net::FCP>-Paket gibt es ganz normal per CPAN.
380    
381     =head3 Einfache Abfragen
382    
383     Die Abkürzung I<FCP> steht für I<Freenet Client Protocol> und ist das
384     Protokoll zwischen Freenet-Anwendungen und dem Freenet-Daemon. Es ist
385     unglaublich ineffizient und noch dazu nicht verschlüsselt. Dies ist der
386     Grund für die Empfehlung, den Daemon nur auf dem lokalen Rechner laufen
387     zu lassen: wäre dumm, wenn man aus dem hochsicheren Freenet über eine
388     ungesicherte Internetverbindung die Windows-Sourcen herunterlädt und
389     sich erwischen läßt (ist alles schon vorgekommen) ...
390    
391     In Perl geht dies denkbar einfach:
392    
393     use Net::FCP;
394     my $fcp = new Net::FCP;
395    
396     Man kann dem Konstruktor von C<Net::FCP> direkt Namen und Port des
397     Freenet-Daemons übergeben. Üblicherweise holt er sich diese aber aus den
398     Environment-Variablen C<FREDHOST> und C<FREDPORT>, die auf C<127.0.0.1>
399     bzw. C<8481> defaulten.
400    
401     Übrigens wird nur eine virtuelle Verbindung aufgebaut. Möchte man
402     sicherstellen, daß tatsächlich ein Daemon erreichbar ist, kann man einen
403     C<client_hello>-Request ausführen oder einfach loslegen.
404    
405     Im Normalfall heißt das also:: no configuration required.
406    
407     Hat man ein C<Net::FCP>-Objekt, kann man schon alle Requests durchführen,
408     die man machen möchte:
409    
410     my ($meta, $data) = @{ $fcp->client_get (
411     "freenet:SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//"
412     15
413     ) };
414    
415     =for latex
416     \begin{sloppypar}
417    
418     Der C<client_get>-Request erwartet zwei Argumente: eine Freenet-URI
419     (C<< freenet:<freenet-schlüssel> >>) und eine HTL. Letztere muss man
420     nicht angeben, man sollte es aber, und vor allem sollte man dem Benutzer
421     die Wahl der HTL überlassen. Das dritte, optionale, Argument ist für
422     spezielle Anwendungen: am besten ignorieren.
423    
424     =for latex
425     \end{sloppypar}
426    
427     Als Ergebnis erhält man immer eine Array-Referenz mit den Metadaten
428     und den Daten. Immer. Sollte ein Fehler auftreten wird eine Exception
429     ausgelöst (auf Perl: das Modul C<die>d). C<eval {}> hilft also im
430     Zweifelsfalle, für einfache Anwendungen ist das aber overkill.
431    
432     Bricht das Programm ab, so sieht das folgendermaßen aus:
433    
434     Net::FCP::Exception<<short_data,reason:unexpected eof or
435     internal node error>>
436    
437     Oder, wenn die Daten nicht gefunden wurden:
438    
439     Net::FCP::Exception<<data_not_found,>>
440    
441     Letzteres bedeutet übrigens nicht aufgeben, sondern nochmal versuchen,
442     z.B. mit einer höheren HTL. Ein fehlgeschlagener Versuch mit hoher HTL
443     bedeutet immer noch nicht aufgeben. Daemons in der ``Nähe'' wissen nun,
444     das die Daten verlangt werden. Nach einiger Zeit ist es wahrscheinlich,
445     das die Daten auf einmal in der Nähe liegen: daher nie aufgeben, nochmal
446     probieren. Niemand hat behauptet, man bekäme die Daten einfach aus dem
447     Freenet wieder heraus...
448    
449     Die Metadaten sind als Textdokument gespeichert. Das Format
450     dieser Metadaten ist aber relativ ``krank'', weshalb das Perl-Modul
451     einen Hash mit den geparsten Daten liefert (genaugenommen ein
452     C<Net::FCP::Metadata>-Objekt, aber dieses Modul ist noch in Entwicklung).
453    
454     Die Metadaten und die eigentlichen Nutzdaten kann man ausgeben:
455    
456     use Data::Dumper;
457     print STDERR Dumper $meta;
458     print $data;
459    
460     Damit haben wir im wesentlichen den Quelltext für das ``Tool''
461     C<eg/fetch1>, mit dem man I<einen> Freenet-Datenblock herunterladen kann.
462    
463     Ich benutze es üblicherweise so:
464    
465     eg/fetch1 SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3// >data
466    
467     Das schreibt die Daten in eine Datei und gibt gleichzeitig dessen
468     Metadaten aus.
469    
470     Für den Key C<SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//> ergibt dies:
471    
472     'version' => { 'revision' => '1' },
473     'document' => [
474     {
475     'info' => { 'format' => 'image/png' },
476     'redirect' => { 'target' => 'freenet:CHK@pMKW...KAwI,7...-25C2w' },
477     'name' => 'activelink.png'
478     },
479     {
480     'info' => { 'format' => 'text/plain' },
481     'redirect' => { 'target' => 'freenet:CHK@2hew...KAwI,o...21z91w' },
482     'name' => 'description.txt'
483     },
484     {
485     'info' => { 'format' => 'text/html' },
486     'redirect' => { 'target' => 'freenet:CHK@kME~...QAwI,3...dy4OkA' },
487     'name' => 'index.html'
488     },
489     {
490     'info' => { 'format' => 'text/html' },
491     'redirect' => { 'target' => 'freenet:SSK@0OhV...PAgM/fx/4' },
492     'name' => '.next'
493     },
494     {
495     'info' => { 'format' => 'text/html' },
496     'redirect' => { 'target' => 'freenet:CHK@kME~...QAwI,3...dy4OkA' }
497     }
498     ],
499     'raw' => 'Version
500     Revision=1
501     [... gekürzt...]
502     Info.Format=text/html
503     End
504     '
505     };
506    
507     Die eigentlichen Daten sind leer (null Byte gross), das Dokument enthät
508     also nur Metadaten! Man beachte vor allem die teilweise recht tiefe
509     Verschachtelung.
510    
511     Der Key C<< $metadata->{raw} >> enthält die Metadaten, wie sie vom
512     Freenet kamen. Dies ist notwendig, da man manchmal die Metadaten
513     zum Prüfen in der exakten Form benötigt, wie sie hochgeladen
514     wurden. Ansonsten kann man sie ignorieren.
515    
516     Der Key C<< $metadata->{document} >> enthält Informationen über das
517     Dokument oder in diesem Fall die Dokumente: Ein Hash pro Dokument. Schaut
518     man sich den Inhalt an (ein Array), so sieht man in jedem Hash ein
519     C<name>-Key, der den Namen des Subdokuments angibt (bis auf den letzten,
520     darauf komme ich gleich).
521    
522     Schaut man sich dir Freenet-URIs dieser Freesite an, so sieht man
523     derartige URIs:
524    
525     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3// # Hauptseite
526     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//activelink.png # Icon
527     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//description.txt # für Spider
528    
529     Wenn diese Dokumente mit C<client_get> anfordert, bekommt man immer obiges
530     Dokument. Freenet ignoriert nämlich alles, was hinter dem C<//> steht
531     und liefert daher immer das gleiche Dokument aus dem SSK-Bereich. (Darauf
532     verlassen soltle man sich nicht unbedingt, in Zukunft reagiert der
533     Freenet-Daemon vielleicht anders. Als Freenetter hat mans eben nicht
534     leicht).
535    
536     Der Teil hinter den beiden Slashes (C<//>) ist nun das Unterdokument, das
537     man über den Namen identifizieren kann:
538    
539     $activelink = grep $_->{name} eq "activelink.png",
540     @{ $metadata->{document} };
541    
542     Fehlt der C<name>-Eintrag, ist es der Eintrag mit dem leeren Namen, in
543     diesem Fall der erste Link, der nichts nach den C<//> stehen hat. Der
544     C<name>-Key I<kann> vorhanden und leer sein: Freenet überprüft die Daten
545     nicht, daher gibt es durchaus unterschiedliche Auslegungen für das genaue
546     Format.
547    
548     Die Metadaten sind etwas genauer in dem *hüstel* etwas veralteten
549     *hüstel* I<Freenet Explained>-Dokument beschrieben.
550    
551     =for latex
552     \begin{sloppypar}
553    
554     Möchte man nun das C<activelink.png> herunterladen muss man in C<<
555     $activelink->{redirect} >> nachsehen. Meistens verweist dies auf einen
556     C<CHK>-Schlüssel. Letztere sind sozusagen Allgemeingut: jeder kann
557     sie benutzen, und da sie nicht änderbar sind, kann sie auch niemand
558     fälschen, daher muss man sie nicht signieren.
559    
560     =for latex
561     \end{sloppypar}
562    
563     Auf C<< $activelink->{info}{format} >> sollte man sich übrigens nicht
564     verlassen.
565    
566     Und sollte kein C<redirect>-Key vorhanden sein, so ist das Dokument im
567     mitgelieferten Datenblock. Alles ganz logisch, einfach, und klar, wie man
568     sieht.
569    
570     Egal, der CHK-Key für C<activelink.png> liefert folgendes:
571    
572     'version' => { 'revision' => '1' },
573    
574     Und die Daten stellen ein PNG dar. Naja, nicht gerade üppig, die
575     Metadaten, aber man hätt's ja im Link auf dieses Dokument sehen
576     können. Als Freenetter hat mans nicht leicht.
577    
578     Aber mit diesen Erklärungen kann man schon einfache Freenet-Spider
579     bauen. Die wichtigsten Tools sind veraltete und spärliche Dokumente
580     wie I<Freenet Explained> und Tools wie C<Data::Dumper>. ``Veraltet'' ist
581     übrigens nicht immer schlecht, da viele Dokumente auch alt sind oder sich
582     eh' nicht exakt an den ``Standard'' halten. Mit der Zeit wird hier sicher
583     eine Besserung eintreten.
584    
585     =head3 Methoden des Freesite-Managements
586    
587     Oder: ``Wenn man Inhalte nicht mehr verändern kann, wie verändert man
588     sie?''
589    
590     Die offensichtliche Methode ist: ``Man macht sie veränderbar''. In
591     der Freenet-Gemeinde geistert seit langer Zeit der Mythos des
592     I<TUK>-Schlüssels, des I<Time Updatable Keys>. Diese Schlüssel tauchen
593     immer auf, wenn jemand über veränderbare Schlüssel spricht. Leider
594     weiss niemand, wie man sie implementieren soll, und ganz persönlich
595     fürchte ich, es wird niemals veränderbare Keys geben.
596    
597     =head4 ``Editions''
598    
599     Die nächstliegende Methode nutzt die Eigenschaft von SSKs (genauer:
600     redirects) aus, das man den Namen sofort generieren kann, den Inhalt aber
601     erst später einfügt.
602    
603     Dies nutzt man aus, indem man I<Editionen> herausgibt und
604     durchnummeriert. Jede Edition enthält einen Link auf die nächste. Bis
605     man die nächste Edition einfügt, wird der Link-Inhalt nicht gefunden.
606    
607     Üblicherweise benutzt man in HTML ein C<IMG>-Element mit Link auf ein
608     Bild aus der nächsten Edition. Sieht man das Bild, weiss man, die
609     nächste Edition existiert und kann sich hinklicken.
610    
611     I<Freenet Explained> benutzt diese Methode. Der Link auf Edition 4 lautet:
612    
613     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4// # Link-Ziel
614     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4//activelink.png # IMG-SRC
615    
616     Beides existierte nicht, als ich diesen Artikel schrieb (bzw. Freenet
617     konnte es nicht finden :). Wenn eine neue Edition herausgegeben werden
618     soll, fügt der Autor einfach einen neuen Datenblock mit vielen Redirects
619     unter obigem SSK-Key ein.
620    
621     Ein C<.next>-Eintrag ist ebenfalls vorhanden: manche Spider folgen diesem
622     und können auf diese Weise immer die aktuelle Edition anzeigen, sofern
623     sie häufig genug scannen.
624    
625     Editionen sind einfach für den Autor einer Freesite und erfordern
626     keine spezielle Unterstützung. In der Benutzung sind sie allerdings
627     umständlich und häufige Updates sind auch nicht effizient.
628    
629     Daher hat sich eine zweite Methode entwickelt, die gerade bei häufig
630     geänderten Inhalten besser funktioniert:
631    
632     =head4 ``Date Based Redirects''
633    
634     Freesites, die ``Date Based Redirects'' benutzen (kurz ``DBR-Sites'')
635     funktionieren ähnlich wie Editionen-basierte Freesites, nur wird statt
636     einer fortlaufenden Nummer die aktuelle Zeit benutzt.
637    
638     =for latex
639     \begin{sloppypar}
640    
641     Die Freesite I<The Freenet Help Index>
642     (C<SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/ FreenetHelp//>) benutzt diese
643     Methode. Folgende Metadaten wurden dazu hinterlegt:
644    
645     =for latex
646     \end{sloppypar}
647    
648     'version' => { 'revision' => '1' },
649     'document' => [
650     { 'date_redirect' => {
651     'increment' => '15180',
652     'target' => 'SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/FreenetHelp'
653     }
654     }
655     ],
656    
657     Statt einem einfachen C<redirect> gibt es jetzt einen
658     C<date_redirect>-Eintrag, und darin ein C<target> (dürfte bekannt sein)
659     und den Key C<increment>. Letzterer darf wie üblich fehlen, man sollte
660     dann C<86400> annehmen (ein Tag hat 86400 Sekunden). In diesem Beispiel
661     wird 86400 genommen (Hexadezimal 15180).
662    
663     Habe ich eigentlich schon erwähnt, daß jede Zahl im FCP-Protokoll oder
664     im Freenet hexadezimal kodiert wird? Also, praktisch immer, außer wenn
665     es mal nicht so ist. Und wozu man einen Offset addiert, ist mir auch
666     schleierhaft. In der freien Wildbahn habe ich sowieso noch keinen gesehen.
667    
668     Der Link in C<target> ist nicht vollständig (unter anderem fehlt
669     hinten ein C<//>). Der komplette Link wird generiert, ``indem die
670     aktuelle Zeit (POSIX-Zeit, üblicherweise das, was C<time> liefert),
671     auf C<increment>-Schritte gerundet und C<offset> addiert, als
672     Big-Endian-Hexadezimalzahl nach dem ersten Slash mit folgendem
673     Minuszeichen eingefügt wird.''
674    
675     Und jetzt in Perl - zum Verstehen:
676    
677     my $doc = $metadata->{document][0]; # Oder [1] oder ...
678    
679     my $increment = (hex $doc->{increment}) || 86400;
680     my $offset = (hex $doc->{offset}) || 0;
681     my $target = $doc->{target} || die;
682    
683     my ($head, $tail) = split /\//, $target, 2;
684    
685     my $NOW = time;
686     my $time = $NOW - $NOW % $increment + $offset;
687    
688     my $result = sprintf "%s/%x-%s", $head, $time, $tail;
689    
690     Für ``jetzt'' (C<time() == 1084388445>) liefert der Algorithmus folgenden Link:
691    
692     SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/40a16900-FreenetHelp
693    
694     Und tatsächlich, unter diesem Key findet man wieder ein Dokument mit
695     vielen Redirects.
696    
697     DBRs haben den Vorteil, ``automatisch'' aktuell zu sein. Solange man nur
698     Redirects einfügt, ist die Belastung durch neue das Einfügen vieler
699     neuer Daten gering, Freenet kommt damit zurecht und löscht alte DBRs
700     bei Bedarf automatisch (ohne natürlich zu wissen, das sich in den
701     Datenblöcken DBRs befinden, denn die kann ja niemand dekodieren, der
702     nicht den Schlüssel besitzt).
703    
704     Der Nachteil ist, das man eine aktuelle Uhrzeit braucht um die Site zu
705     finden. Schlimmer noch: die Freesite muss regelmäßig neu eingefügt
706     werden, eben alle C<increment> Sekunden.
707    
708     DBR-Freesites, die nicht mehr maintained werden, verschwinden deshalb fast
709     sofort, solange man nicht einen Zeitpunkt kennt, zu dem sie noch (bzw.
710     schon!) existierte.
711    
712     All dieses I<sollte> eigentlich in einem Metadata-Modul stattfinden. So
713     weit bin ich aber nicht, da mein Hauptziel das effiziente Herunterladen
714     großer Dateien ist.
715    
716     Womit ich beim Thema wäre.
717    
718     =head2 Effizient herunterladen
719    
720     Transfers im Freenet zeichnen sich durch zwei Eigenschaften aus: hohe
721     Latenz und vergleichsweise hoher Durchsatz. Dies ist ungewöhnlich, ergibt
722     sich aber aus dem Suchverfahren: die Suche nach einem Datenblock kann sehr
723     lange dauern (sogar einige Male fehlschlagen bevor die Daten eintreffen),
724     sie sind jedoch einfach parallelisierbar.
725    
726     Wie in Java üblich, erreichte man dies früher durch massive Benutzung
727     von Threads. Der Freenet-Referenz-Daemon ist davon inzwischen abgekommen
728     (zu langsam, zu viel Speicher und zu hohe Komplexität), doch viele
729     Clients haben auch heute noch Voreinstellungen wie `Anzahl der Threads pro
730     Download'' und viele sind auch so implementiert.
731    
732     Da Perls aktuelle Thread-Implementierung ein Prozessmodell simuliert
733     ist der Overhead ebenfalls entsprechend hoch, und man erhält bei
734     Thread-Benutzung viele ihrer Nachteile, jedoch keine nennenswerten
735     Vorteil, außer das alles etwas langsamer läuft und man daher besser
736     zuschauen kann :)
737    
738     Für C<Net::FCP> (oder besser: für mich) kam es deshalb nicht in Frage,
739     Parallelisierung auf Thread-Basis zu implementieren. Das Problem auf
740     den Modul-Benutzer abzuschieben erschien mir auch nicht fair, daher
741     unterstützt C<Net::FCP> die Module L<Coro>, L<Event>, L<Glib> oder L<Tk>,
742     bzw. deren Event-Steuerung. Man hat also die Wahl, auf welcher Basis man sein
743     Programm aufbaut, und es geht auch ganz ohne Event-System (wie die obigen
744     einfachen Beispiele zeigen).
745    
746     =head3 Transaktionen
747    
748     C<Net::FCP> benutzt für jeden Request eine I<Transaktion>. Das ist ein
749     Objekt, das den Zustand und eintreffende Ergebnisse speichert.
750    
751     Jede Anfrage, (z.B. C<client_get>) wird intern in ein solches
752     Transaktionsobjekt verwandelt. Für jede ``einfache'' Methode wie
753     C<client_get> oder C<insert_private_key> gibt es eine entsprechende
754     Methode mit C<txn_>-Präfix, die statt des Resultats ein solches Objekt
755     liefert.
756    
757     Transaktionsobjekte besitzen einige Methoden, mit denen sie konfiguriert
758     werden können oder Ergebnisse abgefragt werden können. Die wichtigste
759     ist die C<result>-Methode, die auf das Ergebnis wartet und es
760     zurückliefert:
761    
762     # $fcp->client_get wird intern so implementiert:
763     my $txn = $fcp->txn_client_get (...);
764     return $tdn->result;
765    
766     Es ist übrigens immer gefahrlos, Transaktionsobjekte zu erzeugen. Etwaige
767     Fehler bei der Durchführung werden immer erst beim Aufruf von C<result>
768     und immer als Perl-Exceptions gemeldet.
769    
770     Um also alle Freenet-Objekte im Array C<@download> gleichzeitig
771     anzufordern, reicht folgender Code:
772    
773     map $_->result,
774     map $_->txn_client_get ($_),
775     @download;
776    
777     Zuerst werden alle URIs auf eine entsprechende Transaktion gemapped, und
778     dann von allen Transaktionen das Resultat angefordert.
779    
780     Natürlich brauchen manche Zugriffe länger als andere. Wenn Transaktionen
781     früher beendet werden, sollte man gleich eine neue starten, um Freenet
782     am Laufen zu halten.
783    
784     Dazu benötigt man ein Signal, daß eine Transaktion beendet ist. Dies
785     macht C<Net::FCP> mit Hilfe eines Callbacks:
786    
787     my $txn = $fcp->txn_client_get (...)
788     ->cb (\&callback);
789    
790     Diesen setzt man mit Hilfe der C<cb>-Methode. Alle Methoden, die ein
791     Transaktionsobjekt konfigurieren, liefern das Transaktionsobjekt zurück,
792     man kann also Aufrufketten wie im Beispiel bilden.
793    
794     Der Callback wird aufgerufen, wenn der Request (erfolgreich oder nicht)
795     beendet wurde.
796    
797     Nun braucht man allerdings die Hauptschleife des jeweiligen Event-Systems,
798     da man ja nicht mehr auf ein bestimmtes Ergebis wartet, sondern auf ein
799     beliebiges.
800    
801     Dazu sollte man C<Net::FCP> auf ein bestimmtes Event-System zwingen. Das
802     folgende Beispiel tut dies und lädt die Kommandozeileargumente parallel
803     herunter:
804    
805     use Net::FCP qw(event=Event); # oder event=Glib, oder...
806    
807     my $fcp = new Net::FCP;
808     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
809    
810     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
811    
812     Die Transaktionsobjekte werden übrigens nicht gespeichert, denn sie
813     werden dem Callback übergeben, und nur dort werden sie gebraucht. Der
814     Callback könnte so aussehen:
815    
816     sub finished {
817     my ($txn) = @_;
818     my ($meta, $data) = @{ $txn->result };
819     # tue etwas
820     }
821    
822     Oder, mit Fehlerprüfung:
823    
824     sub finished {
825     my ($txn) = @_;
826     my ($meta, $data) = eval { @{ $txn->result } };
827     if ($@) {
828     warn "fehler: $@, wird einfach ignoriert :)"
829     } else {
830     warn "Lo and Behold! We got soemthing!";
831     # tue etwas
832     }
833     }
834    
835     =head3 Fortschritt ist nicht aufzuhalten
836    
837     Da Zugriffe recht lange dauern können, schickt Freenet regelmäig
838     Fortschrittsreports. Man kann sich das vorstellen wie C<warn> und
839     C<die>: C<warn> liefert wichtige Informationen, C<die> einen Fehler.
840    
841     Die Fortschrittsanzeigen werden normalerweise nicht von C<Net::FCP>
842     ausgegeben (wäre irgendwie kontraproduktiv in einem GUI-Programm), man
843     kann aber einen Callback installieren:
844    
845     my $fcp = Net::FCP progress => \&progress;
846    
847     Ein solcher Progress-Callback könnte so aussehen:
848    
849     sub progress_cb {
850     my ($self, $txn, $type, $attr) = @_;
851    
852     warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
853     }
854    
855     Möchte man Informationen für den Callback bereitstellen, so bietet sich
856     die C<userdata>-Methode von Transaktionsobjekten an.
857    
858     Hier ist ein kompletter parallelisierender Client mit Fortschrittsanzeige:
859    
860     my $fcp = Net::FCP progress => \&progress;
861    
862     Ein solcher Progress-Callback könnte so aussehen:
863    
864     use Net::FCP qw(event=Event); # oder event=Glib, oder...
865    
866     my $fcp = new Net::FCP progress => \&progress_cb;
867    
868     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
869    
870     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
871    
872     sub finished {
873     my ($txn) = @_;
874     my ($meta, $data) = eval { @{ $txn->result } };
875     if ($@) {
876     warn "$txn: fehler: $@, wird einfach ignoriert :)\n"
877     } else {
878     warn "$txn: Lo and Behold! We got something!\n";
879     # tue etwas
880     }
881     }
882    
883     sub progress_cb {
884     my ($self, $txn, $type, $attr) = @_;
885    
886     warn "$txn: progress<$txn,$type," . (join ":", %$attr) . ">\n";
887     }
888    
889     Startet man ihn mit einigen gültigen und ungültigen Freenet-URIs,
890     bekommt man vieleicht folgende Ausgabe:
891    
892     Net::FCP::Txn::ClientGet=HASH(0x81e0f64): ...
893     ... fehler: Net::FCP::Exception<<uri_error, ...
894     ... reason:Unspecified document name>>, wird einfach ignoriert :)
895     Net::FCP::Txn::ClientGet=HASH(0x81e7254): ...
896     ... fehler: Net::FCP::Exception<<uri_error,reason: ...
897     ... Unknown keytype>>, wird einfach ignoriert :)
898     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
899     ... progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c), ...
900     ... data_found,data_length:74:metadata_length:74>
901     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
902     ... progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c), ...
903     ... data,chunk:116:total:116:received:116>
904     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
905     ... Lo and Behold! We got something!
906     ... usw.
907    
908     Benutzt man das C<Coro>-Modul, so kann man Callback-basierte und Thread-
909     (bzw. Coroutinen-) basierte Programmierung mischen:
910    
911     use Net::FCP qw(event=Coro);
912    
913     my $fcp = new Net::FCP;
914    
915     # Achtung. Pseudocode :)
916     for my $url (@urls) {
917     async {
918     my ($meta, $data) = @{ $fcp->client_get ($url) };
919    
920     if ($meta ist ein splitfile) {
921     for (@alle_splitfile_blöcke) {
922     $fcp->txn_client_get (...)
923     ->cb (\&save_splitfile_block);
924     }
925     } else {
926     # direkt speichern
927     }
928     };
929     }
930    
931     =head3 Splitfiles
932    
933     Das Them Splitfiles möchte ich hier noch erwähnen, aber nicht
934     ausführlich besprechen.
935    
936     Da die Datenblöcke im Freenet (zur Zeit) nicht größer als 1MB sein
937     können, müssen größere Dateien (Filme z.B.) aufgeteilt werden. Wenn
938     man viele Datenblöcke für eine Datei braucht, erhöht sich die
939     Wahrscheinlichkeit dafür, daß ein Block mal fehlt oder nicht gefundne
940     werden kann, stark auf einen Wert, der das Herunterladen großer Dateien
941     unmöglich macht.
942    
943     Daher wird mit Fehlerkorrekturcodes die Anzahl der Blöcke um 50%
944     erhöht. Metadaten für ein Splitfile sehen so aus:
945    
946     'version' => { 'revision' => '1' },
947     'document' => [ {
948     'info' => {
949     'checksum' => '3bab309132f812cb2a281357b0dc1036920672e2',
950     'format' => 'video/mpeg',
951     'description' => 'Onion FEC v1.2 file inserted by Fuqid'
952     },
953     'split_file' => {
954     'algo_name' => 'OnionFEC_a_1_2',
955     'check_block_count' => '3f',
956     'block_count' => '7f',
957     'check_block' => {
958     '33' => 'freenet:CHK@eTNQ...3CBzTSsI~oPn6EUAwI,I4X...nyGoIRZBLg',
959     '32' => 'freenet:CHK@Ee1h...Kg3WZ90KXqwQZMUAwI,ppn...k1z3Sempqw',
960     '1e' => 'freenet:CHK@5gBB...fjO27kOtU0hZgkUAwI,Okf...wvtX~wfVNA',
961     [viele Zeilen fehlen hier :]
962     '3d' => 'freenet:CHK@iIT1...yOToFsyyytV-roUAwI,9u3...-eY7Onag1g',
963     '5' => 'freenet:CHK@EeVsn...CNBNVzvTTQ1l0UAwI,q6e...Oh-cc79WBA'
964     },
965     'block' => {
966     '33' => 'freenet:CHK@j522...h2ZK1D1q4UDnFgUAwI,Xwo...XFwI5f9iyw',
967     '32' => 'freenet:CHK@VU5v...y-Dnk8SCt9ZKbYUAwI,iX~...3Yk0rmpM9w',
968     '5d' => 'freenet:CHK@-wbX...uotK~uZbJp14zsUAwI,-bg...YvbwQeYtyA',
969     [viele Zeilen fehlen hier :]
970     '43' => 'freenet:CHK@zMXc...ee1cftvdK4~QukUAwI,o99...adXUXGjrlg',
971     '5' => 'freenet:CHK@1ZDiv...YTKQbDq1yCPWAUAwI,B9f...fOx~PTZkAA',
972     '3d' => 'freenet:CHK@ZGxa...qQIf6rbr-g8btMUAwI,f0x...mmxoJRzaLw'
973     },
974     'size' => '7e8e804'
975     }
976     } ],
977     }
978    
979     Die Datenblöcke und die zusätzlichen Korrekturblöcke sind getrennt
980     aufgeführt. Man braucht nur so viele Blöcke insgesamt, wie die Datei
981     groß ist. Den Rest kann man aus den heruntergeladenen erzeugen.
982    
983     Wie genau die Daten auf die Blöcker verteilt werden unterliegt einem
984     Algorithmus und ist nicht in den Metadaten gespeichert. Der Grund ist,
985     daß jedes Programm denselben Algorithmus benutzen muss, und Dateien daher
986     immer gleich auf die Blöcke verteilt werden, so daß Mehrfacheinfügen ins
987     Freenet keine Duplikate erstellt.
988    
989     Den genauen Algorithmus kann man in der Datei C<bin/fmd>, dem I<Freenet
990     Mass Downloader> in der Funktion C<state_splitfile> nachlesen. Der Code
991     ist weder besonders klar, noch besonders kurz, noch besonders hübsch. Auch
992     dieser Teil wird irgendwann einmal in ein Modul fließen, damit er anderen
993     zugänglicher wird.
994    
995     Interessant ist noch, daß man CHK-Inhalte, die aus dem Freenet
996     heruntergeladen werden, einfach auf Konsistenz prüfen kann: sie enthalten
997     schließlich den SHA1-Hash der Daten:
998    
999     use Net::FCP::Util;
1000    
1001     my ($meta, $data) = @{ $fcp->client_get ($uri) };
1002    
1003     # Extrahiere den Hash aus der URI
1004     my $k1 = Net::FCP::Util::extract_chk_hash $uri;
1005     # Berechne den Hash aus den Daten
1006     my $k2 = Net::FCP::Util::generate_chk_hask "$meta->{raw}$data";
1007    
1008     # Idealerweie stimmen beide überein...
1009     $k1 eq $k2 or die "Key/Content mismatch!";
1010    
1011     Zwar ist der Freenet-Daemon angeblich perfekt, aber früher passierte
1012     es tatsächlich oft, daß man korrupte Daten geliefert bekam. Retryen
1013     half. Man hat es eben nicht so leicht...
1014    
1015     Heute ist das zwar nicht mehr der Fall (...), aber prüfen kostet fast
1016     nichts.
1017    
1018     =head2 Publizieren
1019    
1020     Das Freenet lebt von vielen Dingen: Den Entwicklern, den Benutzern die
1021     ihr Interesse bekunden, den Regierungen, die die Meinungsäußerung
1022     einschränken - aber vor allem dadurch, daß es sinnvolle Inhalte darin gibt.
1023    
1024     Ich bin mir nicht so sicher, I<ob> es so viele sinnvolle Inhalte gibt,
1025     aber noch gab es ja auch keine 1.0-Release...
1026    
1027     Nun ja, solange man nichts ins Netz stellt, darf man auch nicht klagen.
1028    
1029     Das kann man ändern, indem man so langweilige Dinge wie den I<Freenet
1030     Insertion Wizard> oder das beliebte I<Fuqid> benutzt (was ich noch nie
1031     getan habe). Aber die benutzen kein Perl und sind damit langweilig.
1032    
1033     Mit C<Net::FCP> geht es erstmal sehr einfach:
1034    
1035     $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
1036    
1037     (Auch hier gibt es natürlich wieder die entsprechende C<txn_>-Variante).
1038    
1039     Einfügen von Inhalten geht also fast geanuso wie Auslesen. C<$uri> ist
1040     eine Freenet-``URIC'' (mit oder ohne C<freenet:>), wie man zu dieser kommt
1041     gleich mehr.
1042    
1043     C<$metadata> kann entweder ein Freenet-Metadata-String sein oder ein Hash
1044     der gleichen Form wie man ihn zurückgeliefert bekommt. C<$data> sind
1045     natürlich die eigentlichen Daten und C<$htl> sind die Hops-To-Live, die
1046     der Key-Insert haben soll. Höhere Werte (bis hin zu 25) speichern die
1047     Daten dauerhafter, brauchen aber auch entsprechend länger.
1048    
1049     C<$removelocal>, auf C<1> gesetzt, sorgt dafür, daß der lokale
1050     Netzknoten die Daten I<nicht> cached. Warum man das tun sollte, ist
1051     einfach erklärt: wenn Freenet beim Einfügen die Daten schon im Netz
1052     vorfindet, wird der Einfügevorgang abgebrochen. Möchte man die Daten
1053     regelmäßig ``auffrischen'' dann sorgt diese Option dafür, daß die
1054     Daten den Knoten auf jeden Fall verlassen und erhöhen so die Chance auf
1055     Verbreitung.
1056    
1057     Nun die Frage wie man zu einer URI kommt. Möchte man einen CHK-Schlüssel
1058     einfügen, so setzt man die URI einfach auf den String C<CHK@>. Nach dem
1059     Einfügen erhält man die eigentliche URI als Resultat:
1060    
1061     my $attr = $fcp->client_put ('CHK@', "", "Daten\n", 20);
1062     print "eingefügte URI: $attr->{uri}\n";
1063    
1064     Als Ergebnis dieses C<client_put>-Requests erhielt ich:
1065    
1066     freenet:CHK@MT~LuxKHH8fugJehkcgp239h7C4KAwI,378rJzHVZbsQbd7VGMHxSg
1067    
1068     Beim Einfügen ins Freenet kann es auch einen I<Key Collision>-Fehler
1069     geben, und zwar genau dann, wenn Freenet die Daten schon findet, die man
1070     einfügen wollte.
1071    
1072     C<Net::FCP> behandelt diesen Fehler allerdings genauso wie ein
1073     erfolgreicher Einfügevorgang, lediglich im Hash, den es zurückliefert,
1074     wird das C<key_collision>-Element auf C<1> gesetzt. Wenn die Daten schon
1075     vorhanden sind, ist das Ziel schließlich auch erreicht.
1076    
1077     Häufig möchte man den C<CHK>-Schlüssel aber schon im voraus
1078     Wissen. Dazu kann man entweder seinen freundlichen Freenet-Daemon fragen:
1079    
1080     my $key = $fcp->generate_chk ($metadata, $data);
1081    
1082     Oder man verwendet das C<Net::FCP::Key::CHK>-Modul, das schneller ist und
1083     keinen Serverzugang benötigt:
1084    
1085     use Net::FCP::Key;
1086    
1087     my $key = (new_from_data Net::FCP::Key::CHK $metadata, $data)->chk;
1088    
1089     =head3 SSK-Schlüssel generieren
1090    
1091     SSK-Schlüssel bestehen immer aus zwei Teilen, dem öffentlichen
1092     (C<public_key>) und dem privaten (C<private_key>), die zusammen ein Paar
1093     bilden. Neuerdings noch aus einem dritten, dem C<crypto_key>, der es
1094     Brute-Force-Attacken noch schwieriger machen soll.
1095    
1096     Solch ein Schlüsselpaar kann man mit einem Aufruf von
1097     C<generate_svk_pair> (auch hierfür gibt es eine C<txn_>-Variante)
1098     erzeugen. Das ``svk'' ist übrigens kein Schreibfehler: SSK-Schlüssel sind
1099     ein Spezialfall von SVK-Schlüsseln, die man aber als normalsterblicher
1100     nirgendwo direkt antrifft.
1101    
1102     my ($public, $private, $crypto) = @{ $fcp->generate_svk_pair };
1103    
1104     Daraus kann man beliebig viele SSK-Schlüssel erzeugen, indem man die drei
1105     Teile wie folgt zusammensetzt:
1106    
1107     # Öffentlicher Schlüssel:
1108    
1109     my $name = "..."; # beliebig
1110    
1111     my $get = "SSK\@${public}PAgM,$crypto/$name";
1112     my $put = "SSK\@$private,$crypto/$name";
1113    
1114     Den mit C<$put> bezeichneten Schlüssel muss man zum Einfügen
1115     verwenden. Mit dem mit C<$get> bezeichneten Schlüssel kann man diese
1116     Daten wieder holen. Letzterer kann bedenkenlos veröffentlich werden.
1117    
1118     Das SSK-Management kann vom C<Net::FCP::Key::SSK>-Modul übernommen
1119     werden.
1120    
1121     =head3 Eine Freesite erzeugen
1122    
1123     Um eine Freesite zu erzeugen, sollte man sich zuerst einen dauerhaften
1124     SSK-Schlüssel besorgen:
1125    
1126     # perl -MNet::FCP -MNet::FCP::Key::SSK \
1127     -e 'Net::FCP::Key::SSK->new_from_fcp (Net::FCP->new) \
1128     ->save ($ARGV[0])' mykey
1129    
1130     Das erzeugt einen neuen SSK-Schlüssel und speichert ihn gleich in der
1131     Datei C<mykey>.
1132    
1133     Dieser SSK-Schlüssel wird die Basis der Freesite.
1134    
1135     Für das Einfügen schreibt man sich am besten ein Skript, da man ides
1136     regelmäßig wiederholen sollte. Zuerst ein paar Definitionen:
1137    
1138     my $edition = 1;
1139     my $keyfile = "mykey";
1140     my $htl = 10;
1141    
1142     Das Skript legt eine editionenbasierte Freesite an und benutzt als Basis
1143     den eben erzeugten SSK-Schlüssel. Der Hops-To-Live-Wert von 10 ist etwas
1144     klein, aber so dauert das Einfügen vielleicht nur ein paar Minuten... In
1145     der Praxis sollte man 20-25 vorziehen, oder das Einfügen regelmäig
1146     wiederholen.
1147    
1148     Als nächstes benötigen wir ein paar Module:
1149    
1150     use Net::FCP qw(event=Event);
1151     use Net::FCP::Metadata;
1152     use Net::FCP::Key::SSK;
1153    
1154     Generell sollte man in einem Perl-Programm den verwendeten
1155     Event-Mechanismus fest angeben. In Freenet-Modulen sollte man dies dagegen
1156     nicht tun, um dem Benutzer die Wahl zu überlassen. Stattdessen sollte
1157     man in Modulen nur Transaktionen benutzen und diese dem Benutzer zur
1158     Verfügung stellen.
1159    
1160     Als nächstes werden ein paar notwendige Objekte erzeugt:
1161    
1162     my $fcp = new Net::FCP;
1163     my $ssk = new_from_file Net::FCP::Key::SSK $keyfile;
1164     my $manifest = new Net::FCP::Metadata;
1165    
1166     Die Metadaten für die Hauptseite (C<$manifest>) werden auch
1167     ``Manifest'' genannt, da man dort häufig nur Verweise auf die
1168     jeweiligen Unterseiten ablegt, also sowas wie der Grundstein einer
1169     Freesite. Das C<Net::FCP::Metadata>-Modul biette zur Zeit nur spärliche
1170     Funktionalität, für unsere Zwecke reicht das jedoch aus.
1171    
1172     Nun noch zwei Hilfsfuntionen. Zuerst C<add_key>:
1173    
1174     sub add_key {
1175     my ($name, $key, $meta, $data) = @_;
1176    
1177     # Asynchrones Einfügen
1178     $fcp->txn_client_put ($chk, $meta, $data, $htl, 1)
1179     ->cb (sub {
1180     eval {
1181     my $attr = $_[0]->result;
1182     printf "Insert of '%s' (%d bytes) ok.\n", $name, length $data;
1183     } or warn "Error while inserting '$name': $@";
1184     });
1185     }
1186    
1187     C<add_key> startet eine C<client_put>-Transaktion, ohne auf das Ende zu
1188     warten. Im Callback wird das Resultat angezeigt, oder, wenn ein Fehler
1189     Auftrat, eben dieser. Das Skript sollte so angelegt sein, das man es
1190     einfach nochmal starten kann, wenn einige Schlüssel nicht eingefügt
1191     werden konnten.
1192    
1193     Nun noch C<add_file>:
1194    
1195     # Hilfsfunktion zum Einfügen von Dateien
1196     sub add_file {
1197     my ($name, $path, $contenttype) = @_;
1198    
1199     # lies die Datei ein
1200     my $data = do {
1201     local $/;
1202     open my $fh, "<", $path
1203     or die "$path: $!";
1204     <$fh>
1205     };
1206    
1207     my $chk = $fcp->generate_chk ("", $data); # keine metadaten
1208     $manifest->add_redirect ($name => $chk, format => $contenttype);
1209     add_key $name, $chk, "", $data;
1210     }
1211    
1212     C<add_file> lädt eine externe Datei (< 1MB) und berechnet deren
1213     CHK-Schlüssel. Diesen Schlüssel fügt es als Verweis unter dem Namen
1214     C<$name> in das Manifest ein. Als letztes benutzt es C<add_key>, um die
1215     Datei einzufügen.
1216    
1217     Das Vorausberechnen des CHK-Schlüssels macht es möglich, das Manifest
1218     paralell zu den anderen Schlüsseln einzufügen. Würde man auf den
1219     Einfügevorgang warten, müsste man das Manifest am Ende getrennt
1220     hochladen.
1221    
1222     Nun ans Zusammenbauen. Zuerst werden (unsichtbare) Verweise auf die
1223     vorherige bzw. die nächste Edition eingefügt:
1224    
1225     $manifest->add_redirect (".prev" => $ssk->gen_pub ($edition - 1))
1226     if $edition > 1;
1227     $manifest->add_redirect (".next" => $ssk->gen_pub ($edition + 1));
1228    
1229     Dies nutzt zum einen die Eigenschaft von SSKs, einfach vorausberechnet
1230     werden zu können, als auch die Methode C<gen_pub>, die den öffentlichen
1231     SSK-Schlüssel mit optionalem Suffix generiert.
1232    
1233     Dadurch können Benutzer auch ``per Hand'' eine neuere oder ältere Edition
1234     suchen, indem sie einfach die Zahl (C<.../2//index.html>) vor dem C<//>
1235     ändern.
1236    
1237     Als nächstes folgt der eigentliche Inhalt der Freesite, eine HTML-Seite,
1238     ein Bild und der Quellcode des Skriptes, der sie generiert hat:
1239    
1240     add_file "activelink.png" => "logo.png", "image/png";
1241     add_file "" => "index.html", "text/html";
1242     add_file "index.html" => "index.html", "text/html";
1243     add_file "freesite" => "freesite", "text/plain";
1244    
1245     Die Datei C<index.html> wird zweimal hinzugefügt, einmal unter dem Namen
1246     ``'' und ein zweitesmal unter dem Namen ``C<index.html>''. Dadurch kann man
1247     erreichen, was viele HTTP-Server ebenfalls machen, nämlich eine URI, die
1248     auf ein Verzeichnis zeigt, auf eine Datei darin zu verweisen. Das die
1249     Datei zweimal eingefügt wird ist kein Problem, da der CHK-Schlüssel
1250     derselbe ist (und Mehrfacheinfügen kein Fehler).
1251    
1252     Nachdem die Dateien hinzugefügt (und vor allem ins Manifest eingetragen)
1253     wurden, kann man das Manifest einfügen:
1254    
1255     my $site = $ssk->gen_pub ($edition);
1256     add_key "manifest", $site, $manifest, "";
1257    
1258     Das Manifest wird. Damit man hinterher auch weiss, was man da eingefügt
1259     hat, sollte man den Hauptschlüssel auch ausgeben:
1260    
1261     print "site generated as $site//\n";
1262    
1263     Und da die ganzen Einfügevorgange ja nur gestartet wurden, muss man auf
1264     deren Ende warten:
1265    
1266     Event::loop;
1267    
1268     Solange C<Event> noch aktive Watcher sieht, bleibt es in der
1269     Event-Schleife. Wenn es keinen aktiven Watcher mehr gibt, kehrt C<loop>
1270     automatisch zurück und das Programm ist beendet.
1271    
1272     Die Ausgabe sieht dann z.B. so aus:
1273    
1274     site generated as SSK@wg...TBRylfnDlUBfjETgPAgM,Jr...UBDPfw1UO1g/1//
1275     Insert of 'freesite' (1688 bytes) ok.
1276     Insert of 'activelink.png' (1346 bytes) ok.
1277     Insert of 'index.html' (200 bytes) ok.
1278     Insert of '' (200 bytes) ok.
1279     Insert of 'manifest' (0 bytes) ok.
1280    
1281     Wenn man einen Fehler gemacht hat, kann man einfach einen neuen
1282     SSK-Schlüssel generieren. Das heißt, solange man seine Site nicht schon
1283     veröffentlicht hat.
1284    
1285     Die Alternative wäre z.B. ein täglicher Date-Based-Redirect, d.h.
1286     tägliches komplettes Neueinfügen. Da bügeln sich Fehler schnell von
1287     selbst aus, man muss sein Skript dann natürlich über Tage oder Monate
1288     regelmäßig starten.
1289    
1290     Eine offensichtliche Verbesserung wäre es, HTML-Dateien abzuändern, so
1291     daß man Links auf die nächste Revision nicht hardcodieren muss sondern
1292     etwa als C<%%NEXT%%//activelink.png> schreiben könnte.
1293    
1294     Nun ja, ich hoffe, es machen sich nun einige Leser auf, um eigene
1295     Freesites zu erstellen oder andere Anwendungen für das Freenet zu
1296     erstellen.
1297    
1298     =head2 Verweise
1299    
1300     =over 4
1301    
1302     =item L<http://www.freenetproject.org/>
1303    
1304     Die Hauptseite des Freenet-Projekts. Hier gibt es auch White-Papers, etwas
1305     Dokumentation und Mailinglisten(-archive).
1306    
1307     =item L<http://www.freenethelp.org/>
1308    
1309     Hier findet der gestresste Freenetter Hilfe beim Einrichten und Betrieb
1310     seines Knotens. Vielleicht.
1311    
1312     =back
1313    
1314    
1315    
1316