ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/pws2004/freenet.mgp
Revision: 1.2
Committed: Sun Jun 20 20:31:05 2004 UTC (22 years, 4 months ago) by root
Branch: MAIN
Changes since 1.1: +172 -413 lines
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 root 1.2 Freenet Live
117    
118     %system "/opt/bin/links -g http://129.13.162.73:8888/"
119    
120     %page
121    
122     Abrufen eines Freenet-Keys (1)
123    
124     Beispiel: ein Bild der Seite "Thought-Crime":
125    
126     %font "t3"
127     CHK@pfAv9IejYPLQwaTLXDguEkUhiNUMAwI,blzDbhN~8Q28esq4JrfRWw
128     %font "s"
129    
130     1. Suche nach CHK@pfAv9IejYPLQwaTLXDguEkUhiNUMAwI
131     %pause
132     (a5f02ff487a360f2d0c1a4cb5c382e12452188d50c0302)
133     %pause
134     0c == 4k, 0302 == CHK + Twofish
135     %pause
136     Skalierbarkeit durch lineare Suche
137     %pause
138     2. Entschlüsseln der Daten mit blzDbhN~8Q28esq4JrfRWw
139     (6e5cc36e137ef10dbc7acab826b7d15b)
140    
141     %page
142    
143     Abrufen eines Freenet-Keys (2)
144    
145     Abgerufen Daten betsehen aus Metadaten + Daten
146    
147     Metadaten:
148    
149     %font "t3"
150     Revision=1
151     EndPart
152     Document
153     Info.Format=image/png
154     End
155     %font "s"
156    
157     Daten:
158    
159     %font "t3"
160     PNG^Z...
161     %font "s"
162    
163     %page
164    
165 root 1.1 Signed Subspace Key
166    
167     %font "t3"
168     SSK@Sc6qV~D6iFhaYord6HtbjJ8MaEYPAgM,Jrd6HtbjJfw1UO1g/YoYo
169     ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
170     Typ Public Key PAgM Entropie + Daten
171     %font "s"
172    
173     %pause
174     Daten werden mit einem private Key signiert
175     %pause
176     maximale Größe 32K
177     %pause
178     mit einem Schüsselpaar können viele Keys eingefügt werden
179     Unterscheidung über angehängte Daten
180    
181     %page
182    
183 root 1.2 Und nun: Perl
184 root 1.1
185    
186 root 1.2 Net::Freenet::FCP
187     http://www.sf.net/projects/perlfcp
188     %pause
189     stark bei Metadaten
190     %pause
191 root 1.1
192 root 1.2 Net::FCP
193     CPAN
194     %pause
195     stark beim asynchronen Einfügen bzw. Abrufen
196     %pause
197     ist von mir, daher wird nur dieses Modul angesprochen :)
198 root 1.1
199 root 1.2 %page
200 root 1.1
201 root 1.2 Einfache Abfragen
202 root 1.1
203 root 1.2 Abfragen benutzen das Freenet Client Protocol (FCP)
204     %pause
205     ein einziger Horror
206     %pause
207     zum Glück gibt's Perl :)
208     %pause
209 root 1.1
210 root 1.2 %font "t5"
211     use Net::FCP;
212     my $fcp = new Net::FCP;
213     %pause
214     my ($meta, $data)
215     = @{ $fcp->client_get (
216     "freenet:SSK@0O....PAgM/fx/3//"
217     15
218     ) };
219 root 1.1
220 root 1.2 %font "s"
221 root 1.1
222 root 1.2 %page
223 root 1.1
224 root 1.2 Fehlerbehandlung
225 root 1.1
226 root 1.2 Bei Nichtfinden: die
227     %pause
228     Net::FCP::Exception<<data_not_found,>>
229     %pause
230     einfach nochmal probieren
231     %pause
232     Bei Netzwerkfehlern: die
233     %pause
234     Net::FCP::Exception<<short_data,reason:unexpected eof or internal node error>>
235     %pause
236     einfach nochmal probieren
237     %pause
238     Bei Schluckauf: die
239     %pause
240     einfach nochmal probieren
241     %pause
242 root 1.1
243 root 1.2 Daher: eval { } benutzen.
244 root 1.1
245 root 1.2 %page
246 root 1.1
247 root 1.2 Metadaten in Perl
248 root 1.1
249 root 1.2 %pause
250     Metadaten in Perl: Net::FCP::Metadata-Objekte
251     %pause
252 root 1.1
253 root 1.2 Am besten einmal ausgeben lassen:
254 root 1.1
255 root 1.2 %font "t5"
256     use Data::Dumper;
257     print STDERR Dumper $meta;
258     print $data;
259     %font "s"
260 root 1.1
261 root 1.2 %pause
262     z.B. mit eg/fetch1:
263 root 1.1
264 root 1.2 %font "t4"
265     eg/fetch1 SSK@0OhV.......oPAgM/fx/3// >data
266     %font "s"
267 root 1.1
268 root 1.2 %page
269 root 1.1
270 root 1.2 Metadaten einer Freesite
271 root 1.1
272 root 1.2 Stark gekürzt:
273     %pause
274     %font "t4"
275     version => { 'revision' => '1' },
276     document => [ {
277     info =>{format => 'image/png'},
278     redirect=>{target => 'freenet:CHK@...'},
279     name =>'activelink.png'
280     }, ],
281     raw => 'Version
282     Revision=1
283     ...
284     End
285     '
286     };
287     %font "s"
288 root 1.1
289 root 1.2 %page
290 root 1.1
291 root 1.2 Manifest-Metadaten
292 root 1.1
293    
294 root 1.2 Beispiele für Keys aus den Metadaten der Freesite:
295 root 1.1
296 root 1.2 %pause
297     %font "t3"
298     Hauptseite
299     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//
300     %pause
301     Icon
302     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/3//activelink.png
303     %font "s"
304 root 1.1
305    
306 root 1.2 %pause
307     client_get liefert immer dieselben Daten
308 root 1.1
309 root 1.2 %pause
310     Freenet ignoriert alles hinter //
311 root 1.1
312 root 1.2 %page
313 root 1.1
314 root 1.2 Manifest-Metadaten
315 root 1.1
316 root 1.2 Selbst nachschauen durch Lookup in Metadaten:
317     %pause
318 root 1.1
319 root 1.2 %font "t4"
320     $link = grep $_->{name} eq "activelink.png",
321     @{ $metadata->{document} };
322     %pause
323     print $link->{redirect};
324     %font "s"
325 root 1.1
326 root 1.2 %pause
327     kein Redirect => Daten sind angehängt
328 root 1.1
329 root 1.2 %page
330     %page
331     %page
332     %page
333     %page
334     %page
335     %page
336     %page
337     %page
338     %page
339     %page
340     %page
341     %page
342 root 1.1
343    
344     =head3 Methoden des Freesite-Managements
345    
346     Oder: ``Wenn man Inhalte nicht mehr verändern kann, wie verändert man
347     sie?''
348    
349     Die offensichtliche Methode ist: ``Man macht sie veränderbar''. In
350     der Freenet-Gemeinde geistert seit langer Zeit der Mythos des
351     I<TUK>-Schlüssels, des I<Time Updatable Keys>. Diese Schlüssel tauchen
352     immer auf, wenn jemand über veränderbare Schlüssel spricht. Leider
353     weiss niemand, wie man sie implementieren soll, und ganz persönlich
354     fürchte ich, es wird niemals veränderbare Keys geben.
355    
356     =head4 ``Editions''
357    
358     Die nächstliegende Methode nutzt die Eigenschaft von SSKs (genauer:
359     redirects) aus, das man den Namen sofort generieren kann, den Inhalt aber
360     erst später einfügt.
361    
362     Dies nutzt man aus, indem man I<Editionen> herausgibt und
363     durchnummeriert. Jede Edition enthält einen Link auf die nächste. Bis
364     man die nächste Edition einfügt, wird der Link-Inhalt nicht gefunden.
365    
366     Üblicherweise benutzt man in HTML ein C<IMG>-Element mit Link auf ein
367     Bild aus der nächsten Edition. Sieht man das Bild, weiss man, die
368     nächste Edition existiert und kann sich hinklicken.
369    
370     I<Freenet Explained> benutzt diese Methode. Der Link auf Edition 4 lautet:
371    
372     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4// # Link-Ziel
373     SSK@0OhVDWutibbBMbXmbxNXW0M6YFoPAgM/fx/4//activelink.png # IMG-SRC
374    
375     Beides existierte nicht, als ich diesen Artikel schrieb (bzw. Freenet
376     konnte es nicht finden :). Wenn eine neue Edition herausgegeben werden
377     soll, fügt der Autor einfach einen neuen Datenblock mit vielen Redirects
378     unter obigem SSK-Key ein.
379    
380     Ein C<.next>-Eintrag ist ebenfalls vorhanden: manche Spider folgen diesem
381     und können auf diese Weise immer die aktuelle Edition anzeigen, sofern
382     sie häufig genug scannen.
383    
384     Editionen sind einfach für den Autor einer Freesite und erfordern
385     keine spezielle Unterstützung. In der Benutzung sind sie allerdings
386     umständlich und häufige Updates sind auch nicht effizient.
387    
388     Daher hat sich eine zweite Methode entwickelt, die gerade bei häufig
389     geänderten Inhalten besser funktioniert:
390    
391     =head4 ``Date Based Redirects''
392    
393     Freesites, die ``Date Based Redirects'' benutzen (kurz ``DBR-Sites'')
394     funktionieren ähnlich wie Editionen-basierte Freesites, nur wird statt
395     einer fortlaufenden Nummer die aktuelle Zeit benutzt.
396    
397     =for latex
398     \begin{sloppypar}
399    
400     Die Freesite I<The Freenet Help Index>
401     (C<SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/ FreenetHelp//>) benutzt diese
402     Methode. Folgende Metadaten wurden dazu hinterlegt:
403    
404     =for latex
405     \end{sloppypar}
406    
407     'version' => { 'revision' => '1' },
408     'document' => [
409     { 'date_redirect' => {
410     'increment' => '15180',
411     'target' => 'SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/FreenetHelp'
412     }
413     }
414     ],
415    
416     Statt einem einfachen C<redirect> gibt es jetzt einen
417     C<date_redirect>-Eintrag, und darin ein C<target> (dürfte bekannt sein)
418     und den Key C<increment>. Letzterer darf wie üblich fehlen, man sollte
419     dann C<86400> annehmen (ein Tag hat 86400 Sekunden). In diesem Beispiel
420     wird 86400 genommen (Hexadezimal 15180).
421    
422     Habe ich eigentlich schon erwähnt, daß jede Zahl im FCP-Protokoll oder
423     im Freenet hexadezimal kodiert wird? Also, praktisch immer, außer wenn
424     es mal nicht so ist. Und wozu man einen Offset addiert, ist mir auch
425     schleierhaft. In der freien Wildbahn habe ich sowieso noch keinen gesehen.
426    
427     Der Link in C<target> ist nicht vollständig (unter anderem fehlt
428     hinten ein C<//>). Der komplette Link wird generiert, ``indem die
429     aktuelle Zeit (POSIX-Zeit, üblicherweise das, was C<time> liefert),
430     auf C<increment>-Schritte gerundet und C<offset> addiert, als
431     Big-Endian-Hexadezimalzahl nach dem ersten Slash mit folgendem
432     Minuszeichen eingefügt wird.''
433    
434     Und jetzt in Perl - zum Verstehen:
435    
436     my $doc = $metadata->{document][0]; # Oder [1] oder ...
437    
438     my $increment = (hex $doc->{increment}) || 86400;
439     my $offset = (hex $doc->{offset}) || 0;
440     my $target = $doc->{target} || die;
441    
442     my ($head, $tail) = split /\//, $target, 2;
443    
444     my $NOW = time;
445     my $time = $NOW - $NOW % $increment + $offset;
446    
447     my $result = sprintf "%s/%x-%s", $head, $time, $tail;
448    
449     Für ``jetzt'' (C<time() == 1084388445>) liefert der Algorithmus folgenden Link:
450    
451     SSK@rjYFfgPHfolmcStiaoxESFfBXz8PAgM/40a16900-FreenetHelp
452    
453     Und tatsächlich, unter diesem Key findet man wieder ein Dokument mit
454     vielen Redirects.
455    
456     DBRs haben den Vorteil, ``automatisch'' aktuell zu sein. Solange man nur
457     Redirects einfügt, ist die Belastung durch neue das Einfügen vieler
458     neuer Daten gering, Freenet kommt damit zurecht und löscht alte DBRs
459     bei Bedarf automatisch (ohne natürlich zu wissen, das sich in den
460     Datenblöcken DBRs befinden, denn die kann ja niemand dekodieren, der
461     nicht den Schlüssel besitzt).
462    
463     Der Nachteil ist, das man eine aktuelle Uhrzeit braucht um die Site zu
464     finden. Schlimmer noch: die Freesite muss regelmäßig neu eingefügt
465     werden, eben alle C<increment> Sekunden.
466    
467     DBR-Freesites, die nicht mehr maintained werden, verschwinden deshalb fast
468     sofort, solange man nicht einen Zeitpunkt kennt, zu dem sie noch (bzw.
469     schon!) existierte.
470    
471     All dieses I<sollte> eigentlich in einem Metadata-Modul stattfinden. So
472     weit bin ich aber nicht, da mein Hauptziel das effiziente Herunterladen
473     großer Dateien ist.
474    
475     Womit ich beim Thema wäre.
476    
477     =head2 Effizient herunterladen
478    
479     Transfers im Freenet zeichnen sich durch zwei Eigenschaften aus: hohe
480     Latenz und vergleichsweise hoher Durchsatz. Dies ist ungewöhnlich, ergibt
481     sich aber aus dem Suchverfahren: die Suche nach einem Datenblock kann sehr
482     lange dauern (sogar einige Male fehlschlagen bevor die Daten eintreffen),
483     sie sind jedoch einfach parallelisierbar.
484    
485     Wie in Java üblich, erreichte man dies früher durch massive Benutzung
486     von Threads. Der Freenet-Referenz-Daemon ist davon inzwischen abgekommen
487     (zu langsam, zu viel Speicher und zu hohe Komplexität), doch viele
488     Clients haben auch heute noch Voreinstellungen wie `Anzahl der Threads pro
489     Download'' und viele sind auch so implementiert.
490    
491     Da Perls aktuelle Thread-Implementierung ein Prozessmodell simuliert
492     ist der Overhead ebenfalls entsprechend hoch, und man erhält bei
493     Thread-Benutzung viele ihrer Nachteile, jedoch keine nennenswerten
494     Vorteil, außer das alles etwas langsamer läuft und man daher besser
495     zuschauen kann :)
496    
497     Für C<Net::FCP> (oder besser: für mich) kam es deshalb nicht in Frage,
498     Parallelisierung auf Thread-Basis zu implementieren. Das Problem auf
499     den Modul-Benutzer abzuschieben erschien mir auch nicht fair, daher
500     unterstützt C<Net::FCP> die Module L<Coro>, L<Event>, L<Glib> oder L<Tk>,
501     bzw. deren Event-Steuerung. Man hat also die Wahl, auf welcher Basis man sein
502     Programm aufbaut, und es geht auch ganz ohne Event-System (wie die obigen
503     einfachen Beispiele zeigen).
504    
505     =head3 Transaktionen
506    
507     C<Net::FCP> benutzt für jeden Request eine I<Transaktion>. Das ist ein
508     Objekt, das den Zustand und eintreffende Ergebnisse speichert.
509    
510     Jede Anfrage, (z.B. C<client_get>) wird intern in ein solches
511     Transaktionsobjekt verwandelt. Für jede ``einfache'' Methode wie
512     C<client_get> oder C<insert_private_key> gibt es eine entsprechende
513     Methode mit C<txn_>-Präfix, die statt des Resultats ein solches Objekt
514     liefert.
515    
516     Transaktionsobjekte besitzen einige Methoden, mit denen sie konfiguriert
517     werden können oder Ergebnisse abgefragt werden können. Die wichtigste
518     ist die C<result>-Methode, die auf das Ergebnis wartet und es
519     zurückliefert:
520    
521     # $fcp->client_get wird intern so implementiert:
522     my $txn = $fcp->txn_client_get (...);
523     return $tdn->result;
524    
525     Es ist übrigens immer gefahrlos, Transaktionsobjekte zu erzeugen. Etwaige
526     Fehler bei der Durchführung werden immer erst beim Aufruf von C<result>
527     und immer als Perl-Exceptions gemeldet.
528    
529     Um also alle Freenet-Objekte im Array C<@download> gleichzeitig
530     anzufordern, reicht folgender Code:
531    
532     map $_->result,
533     map $_->txn_client_get ($_),
534     @download;
535    
536     Zuerst werden alle URIs auf eine entsprechende Transaktion gemapped, und
537     dann von allen Transaktionen das Resultat angefordert.
538    
539     Natürlich brauchen manche Zugriffe länger als andere. Wenn Transaktionen
540     früher beendet werden, sollte man gleich eine neue starten, um Freenet
541     am Laufen zu halten.
542    
543     Dazu benötigt man ein Signal, daß eine Transaktion beendet ist. Dies
544     macht C<Net::FCP> mit Hilfe eines Callbacks:
545    
546     my $txn = $fcp->txn_client_get (...)
547     ->cb (\&callback);
548    
549     Diesen setzt man mit Hilfe der C<cb>-Methode. Alle Methoden, die ein
550     Transaktionsobjekt konfigurieren, liefern das Transaktionsobjekt zurück,
551     man kann also Aufrufketten wie im Beispiel bilden.
552    
553     Der Callback wird aufgerufen, wenn der Request (erfolgreich oder nicht)
554     beendet wurde.
555    
556     Nun braucht man allerdings die Hauptschleife des jeweiligen Event-Systems,
557     da man ja nicht mehr auf ein bestimmtes Ergebis wartet, sondern auf ein
558     beliebiges.
559    
560     Dazu sollte man C<Net::FCP> auf ein bestimmtes Event-System zwingen. Das
561     folgende Beispiel tut dies und lädt die Kommandozeileargumente parallel
562     herunter:
563    
564     use Net::FCP qw(event=Event); # oder event=Glib, oder...
565    
566     my $fcp = new Net::FCP;
567     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
568    
569     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
570    
571     Die Transaktionsobjekte werden übrigens nicht gespeichert, denn sie
572     werden dem Callback übergeben, und nur dort werden sie gebraucht. Der
573     Callback könnte so aussehen:
574    
575     sub finished {
576     my ($txn) = @_;
577     my ($meta, $data) = @{ $txn->result };
578     # tue etwas
579     }
580    
581     Oder, mit Fehlerprüfung:
582    
583     sub finished {
584     my ($txn) = @_;
585     my ($meta, $data) = eval { @{ $txn->result } };
586     if ($@) {
587     warn "fehler: $@, wird einfach ignoriert :)"
588     } else {
589     warn "Lo and Behold! We got soemthing!";
590     # tue etwas
591     }
592     }
593    
594     =head3 Fortschritt ist nicht aufzuhalten
595    
596     Da Zugriffe recht lange dauern können, schickt Freenet regelmäig
597     Fortschrittsreports. Man kann sich das vorstellen wie C<warn> und
598     C<die>: C<warn> liefert wichtige Informationen, C<die> einen Fehler.
599    
600     Die Fortschrittsanzeigen werden normalerweise nicht von C<Net::FCP>
601     ausgegeben (wäre irgendwie kontraproduktiv in einem GUI-Programm), man
602     kann aber einen Callback installieren:
603    
604     my $fcp = Net::FCP progress => \&progress;
605    
606     Ein solcher Progress-Callback könnte so aussehen:
607    
608     sub progress_cb {
609     my ($self, $txn, $type, $attr) = @_;
610    
611     warn "progress<$txn,$type," . (join ":", %$attr) . ">\n";
612     }
613    
614     Möchte man Informationen für den Callback bereitstellen, so bietet sich
615     die C<userdata>-Methode von Transaktionsobjekten an.
616    
617     Hier ist ein kompletter parallelisierender Client mit Fortschrittsanzeige:
618    
619     my $fcp = Net::FCP progress => \&progress;
620    
621     Ein solcher Progress-Callback könnte so aussehen:
622    
623     use Net::FCP qw(event=Event); # oder event=Glib, oder...
624    
625     my $fcp = new Net::FCP progress => \&progress_cb;
626    
627     $fcp->txn_client_get ($_)->cb (\&finished) for @ARGV;
628    
629     Event::loop; # hier passierts, loop ist die Hauptschleife von Event
630    
631     sub finished {
632     my ($txn) = @_;
633     my ($meta, $data) = eval { @{ $txn->result } };
634     if ($@) {
635     warn "$txn: fehler: $@, wird einfach ignoriert :)\n"
636     } else {
637     warn "$txn: Lo and Behold! We got something!\n";
638     # tue etwas
639     }
640     }
641    
642     sub progress_cb {
643     my ($self, $txn, $type, $attr) = @_;
644    
645     warn "$txn: progress<$txn,$type," . (join ":", %$attr) . ">\n";
646     }
647    
648     Startet man ihn mit einigen gültigen und ungültigen Freenet-URIs,
649     bekommt man vieleicht folgende Ausgabe:
650    
651     Net::FCP::Txn::ClientGet=HASH(0x81e0f64): ...
652     ... fehler: Net::FCP::Exception<<uri_error, ...
653     ... reason:Unspecified document name>>, wird einfach ignoriert :)
654     Net::FCP::Txn::ClientGet=HASH(0x81e7254): ...
655     ... fehler: Net::FCP::Exception<<uri_error,reason: ...
656     ... Unknown keytype>>, wird einfach ignoriert :)
657     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
658     ... progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c), ...
659     ... data_found,data_length:74:metadata_length:74>
660     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
661     ... progress<Net::FCP::Txn::ClientGet=HASH(0x81e0c4c), ...
662     ... data,chunk:116:total:116:received:116>
663     Net::FCP::Txn::ClientGet=HASH(0x81e0c4c): ...
664     ... Lo and Behold! We got something!
665     ... usw.
666    
667     Benutzt man das C<Coro>-Modul, so kann man Callback-basierte und Thread-
668     (bzw. Coroutinen-) basierte Programmierung mischen:
669    
670     use Net::FCP qw(event=Coro);
671    
672     my $fcp = new Net::FCP;
673    
674     # Achtung. Pseudocode :)
675     for my $url (@urls) {
676     async {
677     my ($meta, $data) = @{ $fcp->client_get ($url) };
678    
679     if ($meta ist ein splitfile) {
680     for (@alle_splitfile_blöcke) {
681     $fcp->txn_client_get (...)
682     ->cb (\&save_splitfile_block);
683     }
684     } else {
685     # direkt speichern
686     }
687     };
688     }
689    
690     =head3 Splitfiles
691    
692     Das Them Splitfiles möchte ich hier noch erwähnen, aber nicht
693     ausführlich besprechen.
694    
695     Da die Datenblöcke im Freenet (zur Zeit) nicht größer als 1MB sein
696     können, müssen größere Dateien (Filme z.B.) aufgeteilt werden. Wenn
697     man viele Datenblöcke für eine Datei braucht, erhöht sich die
698     Wahrscheinlichkeit dafür, daß ein Block mal fehlt oder nicht gefundne
699     werden kann, stark auf einen Wert, der das Herunterladen großer Dateien
700     unmöglich macht.
701    
702     Daher wird mit Fehlerkorrekturcodes die Anzahl der Blöcke um 50%
703     erhöht. Metadaten für ein Splitfile sehen so aus:
704    
705     'version' => { 'revision' => '1' },
706     'document' => [ {
707     'info' => {
708     'checksum' => '3bab309132f812cb2a281357b0dc1036920672e2',
709     'format' => 'video/mpeg',
710     'description' => 'Onion FEC v1.2 file inserted by Fuqid'
711     },
712     'split_file' => {
713     'algo_name' => 'OnionFEC_a_1_2',
714     'check_block_count' => '3f',
715     'block_count' => '7f',
716     'check_block' => {
717     '33' => 'freenet:CHK@eTNQ...3CBzTSsI~oPn6EUAwI,I4X...nyGoIRZBLg',
718     '32' => 'freenet:CHK@Ee1h...Kg3WZ90KXqwQZMUAwI,ppn...k1z3Sempqw',
719     '1e' => 'freenet:CHK@5gBB...fjO27kOtU0hZgkUAwI,Okf...wvtX~wfVNA',
720     [viele Zeilen fehlen hier :]
721     '3d' => 'freenet:CHK@iIT1...yOToFsyyytV-roUAwI,9u3...-eY7Onag1g',
722     '5' => 'freenet:CHK@EeVsn...CNBNVzvTTQ1l0UAwI,q6e...Oh-cc79WBA'
723     },
724     'block' => {
725     '33' => 'freenet:CHK@j522...h2ZK1D1q4UDnFgUAwI,Xwo...XFwI5f9iyw',
726     '32' => 'freenet:CHK@VU5v...y-Dnk8SCt9ZKbYUAwI,iX~...3Yk0rmpM9w',
727     '5d' => 'freenet:CHK@-wbX...uotK~uZbJp14zsUAwI,-bg...YvbwQeYtyA',
728     [viele Zeilen fehlen hier :]
729     '43' => 'freenet:CHK@zMXc...ee1cftvdK4~QukUAwI,o99...adXUXGjrlg',
730     '5' => 'freenet:CHK@1ZDiv...YTKQbDq1yCPWAUAwI,B9f...fOx~PTZkAA',
731     '3d' => 'freenet:CHK@ZGxa...qQIf6rbr-g8btMUAwI,f0x...mmxoJRzaLw'
732     },
733     'size' => '7e8e804'
734     }
735     } ],
736     }
737    
738     Die Datenblöcke und die zusätzlichen Korrekturblöcke sind getrennt
739     aufgeführt. Man braucht nur so viele Blöcke insgesamt, wie die Datei
740     groß ist. Den Rest kann man aus den heruntergeladenen erzeugen.
741    
742     Wie genau die Daten auf die Blöcker verteilt werden unterliegt einem
743     Algorithmus und ist nicht in den Metadaten gespeichert. Der Grund ist,
744     daß jedes Programm denselben Algorithmus benutzen muss, und Dateien daher
745     immer gleich auf die Blöcke verteilt werden, so daß Mehrfacheinfügen ins
746     Freenet keine Duplikate erstellt.
747    
748     Den genauen Algorithmus kann man in der Datei C<bin/fmd>, dem I<Freenet
749     Mass Downloader> in der Funktion C<state_splitfile> nachlesen. Der Code
750     ist weder besonders klar, noch besonders kurz, noch besonders hübsch. Auch
751     dieser Teil wird irgendwann einmal in ein Modul fließen, damit er anderen
752     zugänglicher wird.
753    
754     Interessant ist noch, daß man CHK-Inhalte, die aus dem Freenet
755     heruntergeladen werden, einfach auf Konsistenz prüfen kann: sie enthalten
756     schließlich den SHA1-Hash der Daten:
757    
758     use Net::FCP::Util;
759    
760     my ($meta, $data) = @{ $fcp->client_get ($uri) };
761    
762     # Extrahiere den Hash aus der URI
763     my $k1 = Net::FCP::Util::extract_chk_hash $uri;
764     # Berechne den Hash aus den Daten
765     my $k2 = Net::FCP::Util::generate_chk_hask "$meta->{raw}$data";
766    
767     # Idealerweie stimmen beide überein...
768     $k1 eq $k2 or die "Key/Content mismatch!";
769    
770     Zwar ist der Freenet-Daemon angeblich perfekt, aber früher passierte
771     es tatsächlich oft, daß man korrupte Daten geliefert bekam. Retryen
772     half. Man hat es eben nicht so leicht...
773    
774     Heute ist das zwar nicht mehr der Fall (...), aber prüfen kostet fast
775     nichts.
776    
777     =head2 Publizieren
778    
779     Das Freenet lebt von vielen Dingen: Den Entwicklern, den Benutzern die
780     ihr Interesse bekunden, den Regierungen, die die Meinungsäußerung
781     einschränken - aber vor allem dadurch, daß es sinnvolle Inhalte darin gibt.
782    
783     Ich bin mir nicht so sicher, I<ob> es so viele sinnvolle Inhalte gibt,
784     aber noch gab es ja auch keine 1.0-Release...
785    
786     Nun ja, solange man nichts ins Netz stellt, darf man auch nicht klagen.
787    
788     Das kann man ändern, indem man so langweilige Dinge wie den I<Freenet
789     Insertion Wizard> oder das beliebte I<Fuqid> benutzt (was ich noch nie
790     getan habe). Aber die benutzen kein Perl und sind damit langweilig.
791    
792     Mit C<Net::FCP> geht es erstmal sehr einfach:
793    
794     $fcp->client_put ($uri, $metadata, $data, $htl, $removelocal);
795    
796     (Auch hier gibt es natürlich wieder die entsprechende C<txn_>-Variante).
797    
798     Einfügen von Inhalten geht also fast geanuso wie Auslesen. C<$uri> ist
799     eine Freenet-``URIC'' (mit oder ohne C<freenet:>), wie man zu dieser kommt
800     gleich mehr.
801    
802     C<$metadata> kann entweder ein Freenet-Metadata-String sein oder ein Hash
803     der gleichen Form wie man ihn zurückgeliefert bekommt. C<$data> sind
804     natürlich die eigentlichen Daten und C<$htl> sind die Hops-To-Live, die
805     der Key-Insert haben soll. Höhere Werte (bis hin zu 25) speichern die
806     Daten dauerhafter, brauchen aber auch entsprechend länger.
807    
808     C<$removelocal>, auf C<1> gesetzt, sorgt dafür, daß der lokale
809     Netzknoten die Daten I<nicht> cached. Warum man das tun sollte, ist
810     einfach erklärt: wenn Freenet beim Einfügen die Daten schon im Netz
811     vorfindet, wird der Einfügevorgang abgebrochen. Möchte man die Daten
812     regelmäßig ``auffrischen'' dann sorgt diese Option dafür, daß die
813     Daten den Knoten auf jeden Fall verlassen und erhöhen so die Chance auf
814     Verbreitung.
815    
816     Nun die Frage wie man zu einer URI kommt. Möchte man einen CHK-Schlüssel
817     einfügen, so setzt man die URI einfach auf den String C<CHK@>. Nach dem
818     Einfügen erhält man die eigentliche URI als Resultat:
819    
820     my $attr = $fcp->client_put ('CHK@', "", "Daten\n", 20);
821     print "eingefügte URI: $attr->{uri}\n";
822    
823     Als Ergebnis dieses C<client_put>-Requests erhielt ich:
824    
825     freenet:CHK@MT~LuxKHH8fugJehkcgp239h7C4KAwI,378rJzHVZbsQbd7VGMHxSg
826    
827     Beim Einfügen ins Freenet kann es auch einen I<Key Collision>-Fehler
828     geben, und zwar genau dann, wenn Freenet die Daten schon findet, die man
829     einfügen wollte.
830    
831     C<Net::FCP> behandelt diesen Fehler allerdings genauso wie ein
832     erfolgreicher Einfügevorgang, lediglich im Hash, den es zurückliefert,
833     wird das C<key_collision>-Element auf C<1> gesetzt. Wenn die Daten schon
834     vorhanden sind, ist das Ziel schließlich auch erreicht.
835    
836     Häufig möchte man den C<CHK>-Schlüssel aber schon im voraus
837     Wissen. Dazu kann man entweder seinen freundlichen Freenet-Daemon fragen:
838    
839     my $key = $fcp->generate_chk ($metadata, $data);
840    
841     Oder man verwendet das C<Net::FCP::Key::CHK>-Modul, das schneller ist und
842     keinen Serverzugang benötigt:
843    
844     use Net::FCP::Key;
845    
846     my $key = (new_from_data Net::FCP::Key::CHK $metadata, $data)->chk;
847    
848     =head3 SSK-Schlüssel generieren
849    
850     SSK-Schlüssel bestehen immer aus zwei Teilen, dem öffentlichen
851     (C<public_key>) und dem privaten (C<private_key>), die zusammen ein Paar
852     bilden. Neuerdings noch aus einem dritten, dem C<crypto_key>, der es
853     Brute-Force-Attacken noch schwieriger machen soll.
854    
855     Solch ein Schlüsselpaar kann man mit einem Aufruf von
856     C<generate_svk_pair> (auch hierfür gibt es eine C<txn_>-Variante)
857     erzeugen. Das ``svk'' ist übrigens kein Schreibfehler: SSK-Schlüssel sind
858     ein Spezialfall von SVK-Schlüsseln, die man aber als normalsterblicher
859     nirgendwo direkt antrifft.
860    
861     my ($public, $private, $crypto) = @{ $fcp->generate_svk_pair };
862    
863     Daraus kann man beliebig viele SSK-Schlüssel erzeugen, indem man die drei
864     Teile wie folgt zusammensetzt:
865    
866     # Öffentlicher Schlüssel:
867    
868     my $name = "..."; # beliebig
869    
870     my $get = "SSK\@${public}PAgM,$crypto/$name";
871     my $put = "SSK\@$private,$crypto/$name";
872    
873     Den mit C<$put> bezeichneten Schlüssel muss man zum Einfügen
874     verwenden. Mit dem mit C<$get> bezeichneten Schlüssel kann man diese
875     Daten wieder holen. Letzterer kann bedenkenlos veröffentlich werden.
876    
877     Das SSK-Management kann vom C<Net::FCP::Key::SSK>-Modul übernommen
878     werden.
879    
880     =head3 Eine Freesite erzeugen
881    
882     Um eine Freesite zu erzeugen, sollte man sich zuerst einen dauerhaften
883     SSK-Schlüssel besorgen:
884    
885     # perl -MNet::FCP -MNet::FCP::Key::SSK \
886     -e 'Net::FCP::Key::SSK->new_from_fcp (Net::FCP->new) \
887     ->save ($ARGV[0])' mykey
888    
889     Das erzeugt einen neuen SSK-Schlüssel und speichert ihn gleich in der
890     Datei C<mykey>.
891    
892     Dieser SSK-Schlüssel wird die Basis der Freesite.
893    
894     Für das Einfügen schreibt man sich am besten ein Skript, da man ides
895     regelmäßig wiederholen sollte. Zuerst ein paar Definitionen:
896    
897     my $edition = 1;
898     my $keyfile = "mykey";
899     my $htl = 10;
900    
901     Das Skript legt eine editionenbasierte Freesite an und benutzt als Basis
902     den eben erzeugten SSK-Schlüssel. Der Hops-To-Live-Wert von 10 ist etwas
903     klein, aber so dauert das Einfügen vielleicht nur ein paar Minuten... In
904     der Praxis sollte man 20-25 vorziehen, oder das Einfügen regelmäig
905     wiederholen.
906    
907     Als nächstes benötigen wir ein paar Module:
908    
909     use Net::FCP qw(event=Event);
910     use Net::FCP::Metadata;
911     use Net::FCP::Key::SSK;
912    
913     Generell sollte man in einem Perl-Programm den verwendeten
914     Event-Mechanismus fest angeben. In Freenet-Modulen sollte man dies dagegen
915     nicht tun, um dem Benutzer die Wahl zu überlassen. Stattdessen sollte
916     man in Modulen nur Transaktionen benutzen und diese dem Benutzer zur
917     Verfügung stellen.
918    
919     Als nächstes werden ein paar notwendige Objekte erzeugt:
920    
921     my $fcp = new Net::FCP;
922     my $ssk = new_from_file Net::FCP::Key::SSK $keyfile;
923     my $manifest = new Net::FCP::Metadata;
924    
925     Die Metadaten für die Hauptseite (C<$manifest>) werden auch
926     ``Manifest'' genannt, da man dort häufig nur Verweise auf die
927     jeweiligen Unterseiten ablegt, also sowas wie der Grundstein einer
928     Freesite. Das C<Net::FCP::Metadata>-Modul biette zur Zeit nur spärliche
929     Funktionalität, für unsere Zwecke reicht das jedoch aus.
930    
931     Nun noch zwei Hilfsfuntionen. Zuerst C<add_key>:
932    
933     sub add_key {
934     my ($name, $key, $meta, $data) = @_;
935    
936     # Asynchrones Einfügen
937     $fcp->txn_client_put ($chk, $meta, $data, $htl, 1)
938     ->cb (sub {
939     eval {
940     my $attr = $_[0]->result;
941     printf "Insert of '%s' (%d bytes) ok.\n", $name, length $data;
942     } or warn "Error while inserting '$name': $@";
943     });
944     }
945    
946     C<add_key> startet eine C<client_put>-Transaktion, ohne auf das Ende zu
947     warten. Im Callback wird das Resultat angezeigt, oder, wenn ein Fehler
948     Auftrat, eben dieser. Das Skript sollte so angelegt sein, das man es
949     einfach nochmal starten kann, wenn einige Schlüssel nicht eingefügt
950     werden konnten.
951    
952     Nun noch C<add_file>:
953    
954     # Hilfsfunktion zum Einfügen von Dateien
955     sub add_file {
956     my ($name, $path, $contenttype) = @_;
957    
958     # lies die Datei ein
959     my $data = do {
960     local $/;
961     open my $fh, "<", $path
962     or die "$path: $!";
963     <$fh>
964     };
965    
966     my $chk = $fcp->generate_chk ("", $data); # keine metadaten
967     $manifest->add_redirect ($name => $chk, format => $contenttype);
968     add_key $name, $chk, "", $data;
969     }
970    
971     C<add_file> lädt eine externe Datei (< 1MB) und berechnet deren
972     CHK-Schlüssel. Diesen Schlüssel fügt es als Verweis unter dem Namen
973     C<$name> in das Manifest ein. Als letztes benutzt es C<add_key>, um die
974     Datei einzufügen.
975    
976     Das Vorausberechnen des CHK-Schlüssels macht es möglich, das Manifest
977     paralell zu den anderen Schlüsseln einzufügen. Würde man auf den
978     Einfügevorgang warten, müsste man das Manifest am Ende getrennt
979     hochladen.
980    
981     Nun ans Zusammenbauen. Zuerst werden (unsichtbare) Verweise auf die
982     vorherige bzw. die nächste Edition eingefügt:
983    
984     $manifest->add_redirect (".prev" => $ssk->gen_pub ($edition - 1))
985     if $edition > 1;
986     $manifest->add_redirect (".next" => $ssk->gen_pub ($edition + 1));
987    
988     Dies nutzt zum einen die Eigenschaft von SSKs, einfach vorausberechnet
989     werden zu können, als auch die Methode C<gen_pub>, die den öffentlichen
990     SSK-Schlüssel mit optionalem Suffix generiert.
991    
992     Dadurch können Benutzer auch ``per Hand'' eine neuere oder ältere Edition
993     suchen, indem sie einfach die Zahl (C<.../2//index.html>) vor dem C<//>
994     ändern.
995    
996     Als nächstes folgt der eigentliche Inhalt der Freesite, eine HTML-Seite,
997     ein Bild und der Quellcode des Skriptes, der sie generiert hat:
998    
999     add_file "activelink.png" => "logo.png", "image/png";
1000     add_file "" => "index.html", "text/html";
1001     add_file "index.html" => "index.html", "text/html";
1002     add_file "freesite" => "freesite", "text/plain";
1003    
1004     Die Datei C<index.html> wird zweimal hinzugefügt, einmal unter dem Namen
1005     ``'' und ein zweitesmal unter dem Namen ``C<index.html>''. Dadurch kann man
1006     erreichen, was viele HTTP-Server ebenfalls machen, nämlich eine URI, die
1007     auf ein Verzeichnis zeigt, auf eine Datei darin zu verweisen. Das die
1008     Datei zweimal eingefügt wird ist kein Problem, da der CHK-Schlüssel
1009     derselbe ist (und Mehrfacheinfügen kein Fehler).
1010    
1011     Nachdem die Dateien hinzugefügt (und vor allem ins Manifest eingetragen)
1012     wurden, kann man das Manifest einfügen:
1013    
1014     my $site = $ssk->gen_pub ($edition);
1015     add_key "manifest", $site, $manifest, "";
1016    
1017     Das Manifest wird. Damit man hinterher auch weiss, was man da eingefügt
1018     hat, sollte man den Hauptschlüssel auch ausgeben:
1019    
1020     print "site generated as $site//\n";
1021    
1022     Und da die ganzen Einfügevorgange ja nur gestartet wurden, muss man auf
1023     deren Ende warten:
1024    
1025     Event::loop;
1026    
1027     Solange C<Event> noch aktive Watcher sieht, bleibt es in der
1028     Event-Schleife. Wenn es keinen aktiven Watcher mehr gibt, kehrt C<loop>
1029     automatisch zurück und das Programm ist beendet.
1030    
1031     Die Ausgabe sieht dann z.B. so aus:
1032    
1033     site generated as SSK@wg...TBRylfnDlUBfjETgPAgM,Jr...UBDPfw1UO1g/1//
1034     Insert of 'freesite' (1688 bytes) ok.
1035     Insert of 'activelink.png' (1346 bytes) ok.
1036     Insert of 'index.html' (200 bytes) ok.
1037     Insert of '' (200 bytes) ok.
1038     Insert of 'manifest' (0 bytes) ok.
1039    
1040     Wenn man einen Fehler gemacht hat, kann man einfach einen neuen
1041     SSK-Schlüssel generieren. Das heißt, solange man seine Site nicht schon
1042     veröffentlicht hat.
1043    
1044     Die Alternative wäre z.B. ein täglicher Date-Based-Redirect, d.h.
1045     tägliches komplettes Neueinfügen. Da bügeln sich Fehler schnell von
1046     selbst aus, man muss sein Skript dann natürlich über Tage oder Monate
1047     regelmäßig starten.
1048    
1049     Eine offensichtliche Verbesserung wäre es, HTML-Dateien abzuändern, so
1050     daß man Links auf die nächste Revision nicht hardcodieren muss sondern
1051     etwa als C<%%NEXT%%//activelink.png> schreiben könnte.
1052    
1053     Nun ja, ich hoffe, es machen sich nun einige Leser auf, um eigene
1054     Freesites zu erstellen oder andere Anwendungen für das Freenet zu
1055     erstellen.
1056    
1057     =head2 Verweise
1058    
1059     =over 4
1060    
1061     =item L<http://www.freenetproject.org/>
1062    
1063     Die Hauptseite des Freenet-Projekts. Hier gibt es auch White-Papers, etwas
1064     Dokumentation und Mailinglisten(-archive).
1065    
1066     =item L<http://www.freenethelp.org/>
1067    
1068     Hier findet der gestresste Freenetter Hilfe beim Einrichten und Betrieb
1069     seines Knotens. Vielleicht.
1070    
1071     =back
1072    
1073    
1074    
1075