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