Ihr kennt vielleicht die Havag (Hallesche Verkehrs-AG)? Das ist das Verkehrsunternehmen in Halle, befördert uns zu maximalen Preisen bei dürftiger Qualität. Wie auch immer.

Das Unternehmen bietet seinen Kunden ein Widget an, mit dem man vom Wohnzimmer das Eintreffen der Bahnen an der nächstgelegenen Haltestelle monitoren kann. Das ganze nennt sich mobile@home (siehe Bild 1) und ist in Java geschrieben. Ist eigentlich eine feine Sache, man muss nicht mehr nach Bauchgefühl das Haus verlassen, wenn ja doch gerade keine Bahn kommt. Das Fenster nervt jedoch tierisch. Ich hab seit langen auf meinem Schedule einen Eintrag, der mich daran erinnert eine bessere Lösung zu finden. Heute Nacht hab ich mich nun endlich an die Arbeit gemacht.

Das Problem ist wie üblich eine geschlossene Software, keine Dokumentation für jemanden wie mich. Ja es ist Java, also wäre es ein Leichtes die Binaries zu dekompilieren, deren Projekt besteht jedoch aus >1000 Klassen (öhm overhead!) und über den rechtlichen Aspekt bin ich mir da auch nicht sicher. Aber was wird das Tool wohl machen? Es wird einen zentralen Server nach den nächsten Stopps an einer von mir gewählten Haltestelle fragen. Der Server wird dann sicherlich mit den jeweiligen Bahnen antworten. Soweit Client und Server kein kryptisches Protokoll sprechen kann ich also einfach den Netzwerkverkehr der über meine Interfaces geht mitschneiden und nachbauen. Also fix den Sniffer meines Vertrauens und das tolle Tool gestartet. Schnell wurde klar um welchen Server es sich handelt: 83.221.237.42 auf Port 20010 . Und die Sache war auch schon so gut wie gegessen als ich folgende Pakete an mir vorbei rauschen sah:

usr@srv % tcpdump -A -n src 83.221.237.42 or dst 83.221.237.42 and tcp port 20010
[...]
03:19:29.177630 IP 192.168.9.55.39453 > 83.221.237.42.20010: Flags [P.], seq 231:443, ack 108, win 35, options [nop,nop,TS val 28628068 ecr 367433884], length 212
E....2@.@.....  7S..*..N*..["..:~...#.......
...d....POST /init/rtpi HTTP/1.1
Content-Type: text/xml
User-Agent: Java/1.6.0_26
Host: 83.221.237.42:20010
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 47


03:19:29.177676 IP 192.168.9.55.39453 > 83.221.237.42.20010: Flags [P.], seq 443:490, ack 108, win 35, options [nop,nop,TS val 28628068 ecr 367433884], length 47
E..c.3@.@..z..  7S..*..N*..[...:~...#.=.....
...d....c..m..getDeparturesForStopS..An der Feuerwachez
03:19:29.229795 IP 83.221.237.42.20010 > 192.168.9.55.39453: Flags [.], ack 490, win 65488, options [nop,nop,TS val 367433990 ecr 28628068], length 0
E..4..@.r.}.S..*..      7N*....:~..\\%....s......
.......d
03:19:29.234736 IP 83.221.237.42.20010 > 192.168.9.55.39453: Flags [.], seq 108:1556, ack 490, win 65535, options [nop,nop,TS val 367433992 ecr 28628068], length 1448
E.....@.r.x1S..*..      7N*....:~..\\%....h......
.......dHTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 1869
Date: Tue, 09 Aug 2011 01:19:29 GMT

S..9S..Soltauer Stra..eS..2011.08.09.03:46:00S..2011.08.09.03:46:00S..trueS..38150S..651S..falseS..falseS..38150:12840S..A FeuerwacheS..falseS..falsezVt..[stringl...l...ingl...
[...]

An der Feuerwache ist die Haltestelle, für die meine Anfrage raus ging, und ein POST -connect mit den Daten ...d....c..m..getDeparturesForStopS..An der Feuerwachez sieht stark nach dem aus, was ich gesucht habe. Kurz darauf kommt auch gleich die Antwort S..9S..Soltauer Stra..eS..2011.08.09.03:46:00S..2011.08.09.03:46:00S..true[..] , und tatsächlich fährt eine Linie 9 in kürze zur Soltauer Straße . Dedüm!! Der Rest ist simpel. Nur noch mit ‘nem Hexeditor (ich empfehle hexcurse) in die Pakete hinein geschaut um zu sehen was genau übermittelt werden muss (die . zwischen den Daten stehen für Zeichen, die in meinem Terminal nicht für mich sichtbar dargestellt werden können. Deren Hexadezimal-Code gibt aber Aufschluss über ihren Wert). Mit ein wenig Trial&Error ist die Geschichte vom Tisch.

Natürlich ist meine Alternative zu dem Widget für jedermann verfügbar:

Als Argument einfach die gewünschte Haltestelle angeben:

usr@srv % ./havag.pl Straßburger Weg
Frage http://83.221.237.42:20010/init/rtpi nach geplanten Stops für 'Straßburger Weg'...

 Zeit   Linie   Richtung
15:33       4   Kröllwitz
15:36       5   Bad Dürrenberg
15:42       5   Kröllwitz
15:44       S   Sonderfahrt
15:45       4   Hauptbahnhof
15:48       4   Kröllwitz
15:51       5   Ammendorf
15:57       5   Kröllwitz
16:00       4   Hauptbahnhof
16:03       4   Kröllwitz
16:06       5   Bad Dürrenberg
16:12       5   Kröllwitz
16:15       4   Hauptbahnhof
16:18       4   Kröllwitz
16:21       5   Ammendorf
16:27       5   Kröllwitz
16:30       4   Hauptbahnhof

Das Protokoll

Vielleicht hat ja jemand Lust weitere Tools zu schreiben, die die “Serverschnittstelle” der Havag benutzen. Daher hier eine kurze Dokumentation des inoffiziellen Protokolls, das mit dem Server gesprochen werden kann.

Eine Anfrage an den Server muss via HTTP-POST an 83.221.237.42:20010/init/rtpi geschehen und die folgenden Inhalte liefern:

[COMMAND]x00[LEN][FUNCTION]x00[LEN][FUNCTIONNAME]x53x00[LEN][ARGUMENT]x7A

Hierbei ist xAB das Zeichen für den Hexwert AB , LEN ist die Länge der darauf folgenden Zeichenkette. Um den Server nach den nächsten Stopps am Rennbahnkreuz zu fragen sendet man als Kommando ein c , als Funktion ein m , und die Funktion heißt getDeparturesForStop . Als Argument wird dann die Haltestelle erwartet. Die Anfrage in HalbHex sieht also wie folgt aus:

cx00x01mx00x14getDeparturesForStopx53x00x0dRennbahnkreuzx7a

und komplett Hexadezimal-kodiert:

x63x00x01x6dx00x14x67x65x74x44x65x70x61x72x74x75x72x65x73x46x6fx72x53x74x6fx70x53x00x0dx52x65x6ex6ex62x61x68x6ex6bx72x65x75x7ax7a

Als Antwort bekommt man die Abfahrtszeiten in den nächsten 60 min. Die Antwort beginnt mit einer Zeichenkette die ich nicht verstehe und vorerst nur verwerfe. Ein Eintrag für eine Bahn startet mit den Hexadezimalwerten x56x74x00x07x5Cx5Bx73x74x72x69x6Ex67x6Cx00x00x00x0D . Darauf folgen die einzelnen Informationen zu dieser Bahn. Eine einzelne Information ist wie folgt kodiert:

x53x00[LEN][INFO]

LEN gibt hier wieder die Länge der folgenden Information an, die Information selbst ist ASCII Klartext. Ein Bahneintrag wird abgeschlossen mit einem x7a (z). Folgende Daten werden übermittelt:

Linie   Endstation      Ankunft                 ???                     N       ???     ???     ???     ???     ???             Angefragte Haltestelle  ???     ???  z
91      Am Steintor     2011.08.09.02:42:35     2011.08.09.01:13:00     false   18824   0       false   false   18824:90120     An der Feuerwache       false   falsez

Das boolsche N gibt an, ob es sich um eine Niederflur-Straßenbahn handelt. Die mit ??? gekennzeichneten Felder verstehe ich (noch) nicht. Vielleicht etwas wie Wagennummer/Typ, ob es eine Verspätung gibt etc. Kann man sicher auch herausbekommen, da sie aber für mich vorerst nicht wichtig sind habe ich mich nicht weiter darum gekümmert.

Meine Beschreibungen sind natürlich alles andere als komplett, für die Korrektheit kann ich auch nicht garantieren. Vielleicht gehört das x00 vor der Längen-Kodierung auch mit zur Längenangabe und ist bei den kurzen Zeichenketten nur immer 0? Wer weiß, in der Praxis funktioniert mein Gehacktes aber (mindestens für mich) ;-)

Update: Wird getDeparturesForStop ohne Argumente gesendet, bekommt man eine Antwort mit allen Verfügbaren Stopps. (via @michas)

Damit ist also der Weg für coole neue Apps geebnet. Vielleicht hat ja jemand von euch eines dieser Smartphones (oder es findet sich ein Sponsor der mir eines zur Verfügung stellt)?

Übrigens flogen auch hin und wieder Pakete mit Inhalten wie diesem vorbei: Apache Tomcat/5.5.17 - Error report . Spricht nicht gerade für das Widget. :-P

Download: Perl: havag.pl (Please take a look at the man-page. Browse bugs and feature requests.)

Martin Scharm

stuff. just for the records.


10 Comments

Hacking Havag | Permalink | 2011-08-10 03:36:20

[…] das Eintreffen der Bahnen a …

Bookmark: Zum Originalbeitrag: Hacking Havag […]

Der Emil | Permalink | 2011-08-10 13:40:01

Klasse!

Genau sowas fehlte mir wirklich – weil ich von Perl und Java keine Ahnung habe (als LINUXer!).

promoter4web | Permalink | 2011-08-10 14:56:18

Genial. Die Befreiung einer Quelle > “openHavag”.

Weiter so und Gruss.

Internet-Citizen | Permalink | 2011-08-11 02:20:24

Soweit ich informiert bin, ist das Widget keine HAVAG-Eigenentwicklung und das Protokoll teil des “MOBILE-STOPinfo” von der INIT AG. Mehr Infos dazu gibt es u.U. auf deren Webseite: www.initag.de (Direktlink auf die Referenz der HAVAG: http://www.initag.de/share/projects/de/Halle.pdf)…

Vielleicht findet man so auch andere “MOBILE-STOPinfo-Hacker” oder kann über die Firma eine Protokollbeschreibung erhalten, obwohl zweiteres doch eher unwahrscheinlich ist - schließlich werden die ihre Lösungen für viel Geld verticken!

Wauzl | Permalink | 2011-08-12 15:27:53

Hallo, super töölchen, sowas hat mit noch gefehlt in ~/bin/ :D

Vllt. schreibst du noch dazu – für den perl-noob, dass libwww-perl benötigt wird :)

Gruß

Martin Scharm | Permalink | 2011-08-12 15:34:51

Achso, ja, natürlich: aptitude install libwww-perl

Tim Friedrich | Permalink | 2013-01-19 01:13:11

Gestern hat es mich mal gepackt und ich habe ein wenig mit deinen Script rumprobiert. Die Abfahrtszeiten herauszubekommen war recht schnell erledigt. Doch wie die Haltestellen intern codiert sind…das war gar nicht so leicht herauszubekommen. Es ging trotzdem:

Anstatt getDeparturesForStop z ohne Argumente zu schicken, muss man getStopCodes z ohne Argumente schicken. Hex Code: 63:00:01:6d:00:0c:67:65:74:53:74:6f:70:43:6f:64:65:73:7a

Das fertig angepasste Perl Skript findet man hier: http://www.jimtim.de/download/public/index.php?dir=Havag%2F . In den denselben Ordner befindet sich ein Skript mit allen Haltestellencodes.

Das Skript ist nicht wirklich gut programmiert…tut aber seinen Dienst. :-D

Danke nochmal an den Verfasser für den Denkanstoß

michas | Permalink | 2013-04-08 15:46:50

Habe es nun endlich mal geschafft, eine erste Beta einer App für iOS, die auf diesem Protokoll-Reengineering basiert, umzusetzen. Man kann sich alle (aktuellen) Haltestellen listen lassen, in der Liste nach einer bestimmten Haltestelle suchen und sich dann die derzeitigen Fahrten dieser Haltestelle im Detail anzeigen lassen.

Jmd. Interesse an einem Beta-Test? :)

Stefan | Permalink | 2013-07-26 21:02:54

Ahoi.

Saubere Arbeit! Schicke Analyse! Wir haben da mal eben eine Implementierung in Ruby gestrickt. Das Ganze findet ihr hier: https://bitbucket.org/terminal21/ruby_on_havag

Wenn alles funzt wird das ein schönes Dashing-Widget (http://shopify.github.io/dashing/) für den Hackspace.

Stefan | Permalink | 2017-01-03 23:39:00

Wir haben da mal noch ein wenig rumgeforscht. Das Protokoll unter der ganzen Nummer nennt sich “Hessian” (https://de.wikipedia.org/wiki/Hessian_(Webprotokoll)). Es gibt ein paar angegammelte Bibliotheken, z.B. HessianPy f&uumlr Python. Damit wird der ganze Eiertanz deutlich trivialer:

from hessian.client import HessianProxy
hp = HessianProxy('http://83.221.237.42:20010/init/rtpi')
hp.getDeparturesForStop(u'An der Feuerwache')

Post a comment

read more about submitting comments