Ein Perl-Skript erspart dem eiligen Nutzer das Herumklicken auf Splash-Pages von Wifi-Providern, indem es Geschäftsbedingungen automatisch abnickt und den Zugriff freischaltet.
Bevor Hotel-, Internetcafé- oder Flughafen-WLANs neue User ins Netz lassen, leiten sie deren Browser-Requests um (Abbildung 1). Der Surfwillige muss sich dann erst durch eine Splash-Page (Abbildung 2) quälen. Dort erwarten ihn entweder ellenlange Nutzungsbedingungen, die sowieso niemand liest, Checkboxen, die zu aktivieren sind, oder Werbung oder alle drei Ärgernisse zugleich.
Abbildung 1: Obwohl der User Google.com wünscht, leitet der WLAN-Provider ihn zunächst unfreiwillig zu seiner eigenen Splash-Page um.

Abbildung 2: Solche Splash-Pages muss das Perl-Skript überwinden, um seinem Anwender den Weg zum Zugang zu ebnen.
Wifi-Provider wollen so die MAC-Adresse der Clients im Wifi-Netz zentral erfassen und die User an die Spielregeln erinnern. Erst nachdem das geklärt ist, hebt der Accesspoint die Schranke und beliebige Verbindungen stehen offen.
Manchmal möchte der User aber gar keinen Webzugang, sondern nur einen SSH-Tunnel öffnen, und wundert sich dann bei offener Shell und geschlossenem Browser, warum er zwar eine IP-Adresse zugewiesen bekommt, aber offensichtlich eine Firewall den Zugriff aufs Internet blockiert.
Flexible Plugin-Taktik
Das Perl-Skript »splash« hilft in solchen Fällen aus der Patsche. Es wühlt sich blind durch die Webformulare, klickt auf alle Links, checkt die Checkboxen, sammelt die Cookies ein und schickt sie brav an den Wifi-Provider zurück, dem es auf diese Weise vorgaukelt, mit einem Browser samt lebensechtem Bediener zu kommunizieren.
Doch wie kann ein einfaches Skript mit potenziell Tausenden verschiedenen Splash-Seiten zurechtkommen? Wifi-Provider könnten nicht nur beliebig komplexe Formulare erzeugen, sondern auch schwere Geschütze auffahren, etwa mit Javascript tricksen oder gar Flash-Animationen einsetzen, um automatische Skripte abzuwehren.
Deshalb fährt das Skript eine Plugin-Strategie, mit der es sich an neue Verfahren anpassen lässt. Jedes Plugin probiert nach seiner eigene Methode die Splash-Seite zu überlisten: Das Plugin »ClickAllLinks.pm« klickt alle Links auf der Seite durch, während »CheckBoxFill.pm« alle dargestellten Checkboxen des ersten Webformulars aktiviert und anschließend einen eventuell vorhandenen Submit-Button drückt. Neu entwickelte Plugins fügt der User ins Plugin-Verzeichnis ein, aus dem sie das Skript ohne weitere Konfiguration automatisch aufschnappt und als neue Taktik ausprobiert.
Nach jedem Plugin-Lauf prüft »splash« erneut, ob ein Request für [http://google.com] auch wirklich die Webseite des Suchmaschinenriesen erreicht oder ob stattdessen noch immer die Splash-Page erscheint. Falls sich immer noch kein Erfolg eingestellt hat, kommt das nächste Plugin mit neuer Methode zum Zug, andernfalls bricht das Skript mit einer Erfolgsmeldung ab.
Bis die Mauer fällt
Abbildung 3 zeigt, wie das Skript reagiert, falls einfach noch keine Netzwerkverbindung existiert, wohl weil der User noch keines der verfügbaren Wifi-Netzwerke selektiert oder noch nicht das erforderliche WPA-Passwort eingegeben hat. Solange die Google-Anfrage nur Fehler liefert oder länger als 5 Sekunden blockiert, probiert es das Skript nach kurzer Verschnaufpause einfach noch mal. Sobald der Client eine IP zugewiesen bekommt und sich bei Webanfragen wenigstens die Splash-Page einstellt, schickt es ein Plugin nach dem anderen in die Schlacht (Abbildung 4), bis eines schließlich die Barriere überwindet.

Abbildung 3: Falls keine Netzwerkverbindung besteht, versucht das Skript in Abständen von 5 Sekunden den Google-Server zu erreichen.

Abbildung 4: Trifft das Skript auf eine Splash-Page, versucht es zunächst nach dem Click-all-Links-Verfahren alle Links durchzuklicken.
Listing 1 zeigt die Basisklasse »SplashJumper« aller Plugins, die lediglich einen Konstruktor definiert. Sie zieht das CPAN-Modul Module::Pluggable hinzu und übergibt ihm die Parameter »require => 1«. Das Modul durchforstet daraufhin das Unterverzeichnis »SplashJumper/Plugin« nach ».pm«-Dateien und lädt sie wegen der »require«-Anweisung alle in das gerade laufende Skript.
|
Listing 1: |
|---|
01 ###########################################
02 package SplashJumper;
03 ###########################################
04 use strict;
05 use warnings;
06 use Module::Pluggable require => 1;
07
08 sub new {
09 bless {}, shift;
10 }
11
12 1;
|
Die Besten zuerst
Ein typisches Plugin zeigt Listing 2 mit dem Modul »ClickAllLinks«. Wenn das Hauptskript das Plugin lädt, ruft es dessen »register()«-Methode auf, die den Namen der implementierten Taktik und eine Prioritätszahl zurückliefert. Das Hauptskript sortiert die Plugins numerisch nach den von ihnen mitgeteilten Prioritätszahlen, ruft also Plugins mit niedrigen Werten zuerst auf. So können die Plugins untereinander auskarteln, wer wann an die Reihe kommt. Üblicherweise sollte das mit den besten Erfolgsaussichten den Anfang machen, das verkürzt den Skriptlauf. Die beiden hier vorgestellten Plugins definieren die Prioritäten 10 beziehungsweise 50, sodass das Skript immer zuerst »ClickAllLinks« versucht, bevor das weiter unten gezeigte »CheckBoxFill«-Verfahren zum Zug kommt.
|
Listing 2: |
|---|
01 ###########################################
02 package
03 SplashJumper::Plugin::ClickAllLinks;
04 ###########################################
05 use Log::Log4perl qw(:easy);
06
07 ###########################################
08 sub register {
09 ###########################################
10 return "click-all-links", 10;
11 }
12
13 ###########################################
14 sub process {
15 ###########################################
16 my($self, $mech) = @_;
17
18 for my $link ( $mech->links() ) {
19
20 INFO "Clicking on ", $link->url();
21 my $resp = $mech->get( $link );
22
23 INFO "Got ",
24 length( $resp->content() ),
25 " bytes back";
26
27 $mech->back();
28 }
29 }
30
31 1;
|
Kommt ein Plugin zum Überwinden der Splash-Page zum Einsatz, ruft das Skript dessen »process()«-Methode auf und übergibt ihr den Browser-Simulator »$mech«, ein Objekt der Klasse WWW::Mechanize. Dieses CPAN-Modul eignet sich hervorragend dazu, Webseiten einzuholen, deren Inhalt zu analysieren und zu tiefer liegenden Links vorzudringen. Es kommt häufig bei der Implementierung von Screenscrapern zum Einsatz, da es beinahe alle Browser-Funktionen beherrscht (abgesehen von Javascript-Code oder Flash-Plugins) und Cookies automatisch empfängt und korrekt wieder an den Server zurückschickt.
Im Plugin »ClickAllLinks« findet zunächst die Methode »links()« alle auf der eingeholten Splash-Seite vorhandenen Links in Form von WWW::Mechanize::Link-Objekten und iteriert über die Liste mittels einer For-Schleife. Die Funktion »INFO()« aus dem Log4perl-Fundus zeigt dann dem neugierigen User an, welchen Link das Plugin gerade anklickt. Die Methode »get()« des Browser-Simulators führt den Webrequest anschließend durch.
Mechanize mit Autocheck
Eine explizite Fehlerprüfung findet dabei nicht statt, da WWW::Mechanize sich standardmäßig im »autocheck«-Modus befindet, in dem es sowieso bei jedem auftretenden Fehler eine Exception wirft, die ein im Hauptskript um den Plugin-Aufruf gewickelter Eval-Block abfängt. Anschließend behandelt das Hauptprogramm den Fehler.
In Zeile 23 zeigt das Plugin dann mit einer weiteren Info-Anweisung an, wie viele Bytes über den geklickten Link zurückkamen, und die Methode »back()« in Zeile 27 drückt den Back-Button des virtuellen Browsers, damit dieser wieder zur Splash-Page zurück rudert.
Eine weitere Taktik zeigt das Plugin »CheckBoxFill« in Listing 3. Es sucht im HTML der Splash-Seite das erste Webformular und setzt dieses mit der Anweisung »form_number(1)« als »current_form«. Die Methode »find_input()« extrahiert daraus alle Eingabefelder vom Typ »checkbox« und die For-Schleife ab Zeile 22 klickt sie alle an, indem sie deren »check()«-Methode aufruft.
|
Listing 3: |
|---|
01 ###########################################
02 package SplashJumper::Plugin::CheckBoxFill;
03 ###########################################
04 use Log::Log4perl qw(:easy);
05
06 ###########################################
07 sub register {
08 ###########################################
09 return "checkbox-fill", 50;
10 }
11
12 ###########################################
13 sub process {
14 ###########################################
15 my($self, $mech) = @_;
16
17 $mech->form_number(1);
18
19 my @inputs = $mech->current_form->
20 find_input( undef, "checkbox" );
21
22 for my $input ( @inputs ) {
23 $input->check();
24 }
25
26 INFO "Submitting form 1";
27 $mech->submit_form( form_number => 1 );
28 $mech->back();
29 }
30
31 1;
|
Zeile 27 schickt das Webformular dann mit »submit_form()« an den Server zurück, bevor Zeile 28 wieder auf die Splash-Seite zurückkehrt, damit das eventuell gleich folgende Plugin wieder normale Ausgangsbedingungen vorfindet. Mit dieser Methode lassen sich Splash-Pages wie in den Abbildungen 5 und 6 knacken, die beide dem User abverlangen, eine Checkbox zu aktivieren und dann den Submit-Button anzuklicken.

Abbildung 5: Das kostenlose Wifi am Flughafen von San Diego verlangt eine Checkbox anzuklicken und das Webformular abzuschicken.
Splash
Listing 4 zeigt schließlich das Hauptprogramm »splash«. Es definiert zunächst in Zeile 7 die Test-URL, die das Skript einholt, um festzustellen, ob der Internetzugang bereits offen ist. Die Google-Seite eignet sich dazu hervorragend, da sie leichtgewichtig und mit hoher Wahrscheinlichkeit in einem funktionierenden Internet verfügbar ist.
|
Listing 4: |
|---|
01 #!/usr/local/bin/perl -w
02 use strict;
03 use SplashJumper;
04 use WWW::Mechanize;
05 use Log::Log4perl qw(:easy);
06
07 my $url = "http://www.google.com";
08
09 Log::Log4perl->easy_init($DEBUG);
10
11 my $sj = SplashJumper->new();
12
13 my @ways = ();
14
15 for my $plugin ( $sj->plugins() ) {
16
17 if( ! $plugin->can("register") ) {
18 ERROR "$plugin can't do register()";
19 next;
20 }
21
22 my($algo, $order) = $plugin->register();
23
24 push @ways, [$algo, $plugin, $order];
25 }
26
27 # sort by plugin priority
28 @ways = sort { $a->[2] <=> $b->[2] } @ways;
29
30 my $mech = WWW::Mechanize->new();
31 $mech->timeout(5);
32
33 # wait until network is up
34 {
35 INFO "Trying $url";
36 eval { $mech->get( $url ); };
37 if($@) {
38 INFO "Connection down, retrying";
39 sleep 5;
40 redo;
41 }
42 }
43
44 # try to get past splash page
45 for my $ways ( @ways ) {
46
47 my $current_url =
48 $mech->response->request->uri;
49
50 if( $current_url eq $url ) {
51 INFO "Link is up.";
52 last;
53 } else {
54 INFO "Link still down";
55 }
56
57 my($algo, $plugin, $order) = @$ways;
58
59 eval {
60 INFO "Processing splash page ",
61 "$current_url with algo $algo";
62 $plugin->process( $mech );
63 };
64
65 if($@) {
66 ERROR "Algo $algo failed ($@)";
67 } else {
68 INFO "Plugin $algo succeeded";
69 }
70 }
|
Zeile 9 initialisiert das Log4perl-Framework mit dem Level »DEBUG«, damit der User detaillierte Informationen darüber erhält, welches Plugin gerade läuft und auf welche Links er klickt. Ein neu angelegtes Objekt der Basisklasse »SplashJumper« verfügt dank des dort eingebundenen Moduls Module::Pluggable über eine Methode »plugins()«, die eine Liste aller im Unterverzeichnis »Plugin« eingestellten Plugins zurückliefert.
Zeile 17 prüft, ob jedes Plugin tatsächlich vorschriftsgemäß eine Methode namens »register()« anbietet, und überspringt mangelhaft implementierte Plugins mit einer Fehlermeldung. Ordnungsgemäße Plugins geben in Zeile 22 ihre Taktik in »$algo« und die gewünschte numerische Priorität in der Variablen »$order« zurück. Das Skript verpackt die gefundenen Daten in einem Array und schiebt eine Referenz darauf an das Ende des Array »@ways«. Zeile 28 sortiert dessen Elemente dann numerisch nach dem »order«-Feld, sodass ein Plugin mit Priorität 10 auch bestimmt vor einem Plugin mit der Priorität 50 läuft.
Der Browser-Simulator vom Typ WWW:: Mechanize setzt seinen Timeout in Zeile 31 auf 5 Sekunden, sodass die Schleife ab Zeile 33 jeweils nur 5 Sekunden in der »get()«-Methode hängt, bevor sie aufgibt und 5 Sekunden schläft, bevor sie erneut probiert den Google-Server zu kontaktieren. Am Ende des Blocks, den Perl mit »redo« wiederholt, funktioniert zumindest das lokale Wifi-Netz und der Client bekam eine gültige IP-Adresse zugewiesen. Doch der Wifi-Provider leitet Requests an »www.google.com« eventuell noch zu einem internen Server weiter, der die Splash-Page produziert.
Unermüdlich
Diese Seite versucht die For-Schleife ab Zeile 45 mit den verschiedenen Plugins zu überwinden. Sobald die URL des letzten Request gleich der Test-URL ist (also kein Redirect zur Splash-Page mehr stattfindet), erkennt Zeile 51, dass die Splash-Page überwunden und die Internetverbindung offen ist.
Ist dies noch nicht der Fall, ruft Zeile 62 die Methode »process()« des nächsten Plugin auf, gemäß den vorher definierten Prioritäten. Der Eval-Block ab Zeile 59 fängt im Plugin auftretende Fehler ab, Zeile 65 prüft die Variable »$@«, um festzustellen, ob etwas vorgefallen ist.
Installation
Damit das Skript das Modul »SplashJumper« findet, muss es im Suchpfad »%INC« des Skripts installiert sein, am einfachsten geht das, indem beide im selben Verzeichnis landen. Die Plugins wandern ins neu angelegte Unterverzeichnis »SplashJumper/Plugin«, sodass das Datei-Layout wie folgt aussieht:
splash SplashJumper.pm SplashJumper/Plug-in/ClickAllLinks.pm SplashJumper/Plug-in/CheckBoxFill.pm
Weitere Plugins, die eventuell mehrere Webformulare verarbeiten oder gar Javascript-Tricks überlisten, legt der User ebenfalls im Verzeichnis »Plugin« ab und weist ihnen in der Methode »register()« einen Taktik-Namen und eine Priorität zu, um die Reihenfolge ihres Einsatzes festzulegen.
Nach dem Aufklappen des Laptops am Flughafen startet der Anwender auf seinem Hindernislauf ins WLAN dann zuerst einfach »splash«. Die Ausgaben des Skripts zeigen anschließend, wie sich der kleine Krieger in der neuen Umgebung schlägt und ob er auch dort die in den Weg gestellten Hürden erfolgreich umzuschubsen vermag. (jcb)
|
Infos |
|---|
|
[1] Listings zu diesem Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2010/11/Perl] |







