Startseite > informatik, livehacking, oss > TCPdump auf Android und TTRSS-Debugging

TCPdump auf Android und TTRSS-Debugging

Das ist ein Artikel aus der Kategorie Live-Hacking. Diese Artikel entstehen während ich programmiere, sind daher voller technischer Details, unnötig lang und womöglich voller Fail.

Wie aufregend, CyanogenMOD 7 hat tcpdump an Bord! Das ist gut, denn ich debugge gerade den TT-RSS-Reader bzw dessen Fork.

Das Problem: Heise.de liefert in ihrem RSS-Feed (ATOM) keinen content mit, sondern nur den Titel. Nun ist die app zwar so eingestellt, dass sie automatisch die Website zu Artikel aufruft, wenn kein Content vorhanden ist. Funktioniert aber nicht! Blick in den Code:

if (!linkAutoOpened && content.length() < 3) {
if (Controller.getInstance().isOpenUrlEmptyArticle()) {
Log.i(Utils.TAG, „Article-Content is empty, opening URL in browser“);
linkAutoOpened = true;
openLink();
}
}

Müsste ja eigentlich gehen. Dann gucken wir erstmal mit TCPdump, was da vom Server kommt. Vielleicht sollte ich noch erwähnen, dass die App nicht das RSS vom Heise-Server liest. Stattdessen spricht die APP per JSON-API über HTTP mit der TT-RSS Instanz auf meinem Webserver. Das RSS von Heise sieht übrigens so aus:

<entry>
<title>Bericht: Sky zahlt 14,5 Millionen an institutionelle Anleger</title>
<link href=“http://www.heise.de/newsticker/meldung/Bericht-Sky-zahlt-14-5-Millionen-an-institutionelle-Anleger-1180130.html/from/atom10&#8243; />
<id>http://www.heise.de/newsticker/meldung/Bericht-Sky-zahlt-14-5-Millionen-an-institutionelle-Anleger-1180130.html/from/atom10</id&gt;
<published>2011-01-30T11:48:00+01:00</published>
<updated>2011-01-30T11:49:46+01:00</updated>

</entry>

Also gut, ich will wissen, ob da vielleicht was von meiner TT-RSS-Instanz kommt, weswegen womöglich obige Logik in der App nicht greift. Ich will mit tcpdump also mal zwei Dinge: allen Traffic auf einem bestimmten Interface mithören und den in eine Datei schreiben lassen. Schnell in die man-page geguckt:

-i     Listen  on  interface.   If unspecified, tcpdump searches the system interface list for the
lowest numbered, configured up interface (excluding loopback).  Ties are broken by choosing
the earliest match.

On Linux systems with 2.2 or later kernels, an interface argument of „any“ can be used to
capture packets from all interfaces.  Note that captures on the „any“ device will not  be
done in promiscuous mode.

-w     Write the raw packets to file rather than parsing and printing them out.  They can later be
printed with the -r option.  Standard output is used if  file  is  „-“.   See  pcap-save‐
file(5) for a description of the file format.

Davon abgesehen muss ich noch wissen, auf welchem Interface ich nun lauschen möchte. Da hätten wir normale ifconfig -a, nur leider kriege ich damit keine Liste der Schnittstellen. Offenbar kommt dieses ifconfig aus busybox und kann dann nicht allzu viel. Schade! Aber wieder eilt tcpdump zur Rettung:

-D     Print  the  list of the network interfaces available on the system and on which tcpdump can
capture packets.  For each network interface, a number and an interface name, possibly fol‐
lowed by a text description of the interface, is printed.  The interface name or the number
can be supplied to the -i flag to specify an interface on which to capture.

This can be useful on systems that don’t have a command to list them  (e.g.,  Windows  sys‐
tems,  or  UNIX  systems lacking ifconfig -a); the number can be useful on Windows 2000 and
later systems, where the interface name is a somewhat complex string.

Wir hätten also folgende Schnittstellen:

# tcpdump -D
1.rmnet0
2.any (Pseudo-device that captures on all interfaces)
3.lo

Da nehme ich doch mal rmnet0!

# tcpdump -w ttrss.dmp -i rmnet0
tcpdump: ttrss.dmp: Read-only file system
#

Eh. Schnell noch adb remount ausgeführt auf dem Host und schon geht es. Der Kram bisher gehört übrigens per adb shell ausgeführt, oder eben im Terminal Emulator auf dem Gerät. Aber eigentlich ist es viel geschickter, den Dump auf der SD-Karte zu speichern.

# tcpdump -w /mnt/sdcard/ttrss.dmp -i rmnet0
tcpdump: listening on rmnet0, link-type EN10MB (Ethernet), capture size 96 bytes

Schon besser. Jetzt kann ich auf dem Schlaufernsprecher die App aufmachen und einen Heise-Artikel angucken, die Kommunikation wird mitgelesen. Dann wird tcpdump mit CTRL+C im Terminal gekillt und ich lade die Datei auf dem Desktop in wireshark.

# tcpdump -w /mnt/sdcard/ttrss.dmp -i rmnet0
tcpdump: listening on rmnet0, link-type EN10MB (Ethernet), capture size 96 bytes
^C114 packets captured
114 packets received by filter
0 packets dropped by kernel
#

In wireshark sehen wir dann unter anderem „packet size limited during capture“. Wir fluchen kurz, werfen die Meldung in Google und starten tcpdump nochmal mit -s 0:

# tcpdump -w /mnt/sdcard/ttrss.dmp -i rmnet0 -s 0
tcpdump: listening on rmnet0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C105 packets captured
105 packets received by filter
0 packets dropped by kernel

Ich stelle fest, dass Wireshark nun crasht, da ich offenbar einen korrupten Dump erzeugt habe. Also in eine *neue* Datei speichern:

# tcpdump -w /mnt/sdcard/ttrss-1.dmp -i rmnet0 -s 0
tcpdump: listening on rmnet0, link-type EN10MB (Ethernet), capture size 65535 bytes
^C162 packets captured
162 packets received by filter
0 packets dropped by kernel

Ich stelle weiterhin fest, dass wireshark immer noch crasht; es also nicht an der Wiederverwendung der Datei lag. Bei einem dritten Versuch mit der gleichen Datei überlegt Wireshark nun – vielleicht liegt es daran, dass die Datei nicht mehr auf usb-storage liegt, sondern ich sie auf /tmp/ kopiert habe? Egal, ich denke nicht darüber nach, sonst verzweifele ich wieder am Nicht-Determinismus dieser Welt.

Nun ist der Dump jedenfalls in Wireshark. Dort habe ich mir nun einen erfolgssversprechenden Eintrag aus der Liste ausgesucht – also irgendwas, das per HTTP mit meinem Server spricht, und per Rechtsklick „Follow TCP Stream“ ausgewählt. Neues Fenster, mit unter anderem folgenden Inhalt:

HTTP/1.1 200 OK
Date: Sun, 30 Jan 2011 12:20:01 GMT
Server: Apache/2.2.9 (Debian)
X-Powered-By: PHP/5.2.6-1+lenny9
Content-Language: auto
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: ttrss_sid_api=XXXXXX; path=/
Vary: Accept-Encoding
Content-Type: text/html; charset=
Transfer-Encoding: chunked

17d
{„seq“:0,“status“:0,“content“:[{„id“:“667″,“title“:“Lernen verlagert sich in virtuelle Welten“,“link“:“http:\/\/www.heise.de\/newsticker\/meldung\/Lernen-verlagert-sich-in-virtuelle-Welten-1180118.html\/from\/atom10″,“labels“:[],“unread“:true,“marked“:false,“published“:false,“comments“:““,“author“:“heise online“,“updated“:1296379500,“content“:““,“feed_id“:“7″,“attachments“:[]}]}
0

Wunderbar, wir sehen also, dass in der Antwort nichts von content oder sonstwas steht. Zum Vergleich mal ein Artikel der Tagesschau:

{„seq“:0,“status“:0,“content“:[{„id“:“649″,“title“:“\u00c4gypten: Erneut Tote bei Protesten gegen Mubarak“,“link“:“http:\/\/www.tagesschau.de\/ausland\/aegypten324.html“,“labels“:[],“unread“:true,“marked“:false,“published“:false,“comments“:““,“author“:““,“updated“:1296314880,“content“:“\n\n<p>\n<a href=\“http:\/\/www.tagesschau.de\/ausland\/aegypten324.html\“> BLABLA

Wir halten also nochmal fest: die Artikel von Heise haben im JSON kein „content“-Feld. Mal gucken, wie der Code das handhabt!

In org.ttrssreader.net.TTRSSJsonConnector.java:

private String parseMetadata(String str) {
// Cut string from content: to the end: /{„seq“:0,“status“:0,“content“:(.*)}/\1/
String pattern = „\“content\“:“;
int start = str.indexOf(pattern) + pattern.length();
int stop = str.length() – 2;
if (start >= pattern.length() && stop > start) {
return str.substring(start, stop);
}
return „“;
}

org.ttrssreader.model.pojos.ArticleItem scheint ebenfalls relevant:

public void setContent(String mContent) {
if (mContent == null || mContent.equals(„null“)) {
this.mContent = null;
} else {
this.mContent = mContent;
}
}

Sieht ja irgendwie alles gut aus bis jetzt! Ich tippe dann einfach mal darauf, dass die Abfrage, ob content eine Länge kleiner 3 hat,  der Knackpunkt ist. Zum Beispiel in ArticleActivity.java wird vielleicht was in content geschrieben, was wir gar nicht so wollen:

// Inject the specific code for attachments, <img> for images, http-link for Videos
content = injectAttachments(getApplicationContext(), mArticleItem.getContent(),
mArticleItem.getAttachments());
content = Utils.injectCachedImages(content);

// Load html from Raw-Ressources and insert content
String temp = getResources().getString(R.string.INJECT_HTML_HEAD);
String text = temp.replace(„MARKER“, content);

Es hilft also nichts, es wird Zeit für das gute alt printf-Debugging, in unserem Fall per System.out.println oder Log.d. Das bedeutet also, ich muss meine eigene Version der App kompilieren. Kein Problem, der Quelltext ist ja schon in meinem Eclipse. Dummerweise wird das resultierende Paket mit einem anderen Schlüssel signiert, nämlich mit meinem Debug-Schlüssel. Ich muss also erstmal die aktuell installierte App von meinem Handy entfernen und dann mit meiner Version der App alle Einstellungen nochmal vornehmen.

Mit der SVN-Version frisch auf dem Gerät finde ich nun als erstes Mal heraus, dass Artikel nicht angezeigt werden. Die App zeigt zwar, dass Artikel vorhanden sind, aber ich kriege keine Liste. Das ist Qualität. Das Verhalten kenne ich noch von meinen ersten Versuchen; offenbar muss ich so lange random rumdrücken, bis es tut. Der Traffic zum Server sieht einwandfrei aus, die Kommunikation klappt also, Artikel kommen an.

Nachdem ich nun einmal auf „Unkategorisierte Feeds“ geklickt habe, geht die App plötzlich wieder. Tatsächlich sehe ich nun nicht nur die Artikel, sondern auch auch die Artikel von Heise werden direkt wie gewünscht im Browser geöffnet. Also wurde in der Version im SVN offenbar genau dieser Fehler behoben. Argh! Zur Sicherheit installiere ich nochmal 0.8.1 aus dem Fdroid-Verzeichnis; im SVN ist Version 0.8.3.

Erkenntnisse mit 0.8.1:

  • auch hier das Verhalten, dass keine Artikel angezeigt werden. Erst, nachdem ich „Unkategorisierte Feeds“ angeklickt habe
  • tatsächlich ist in 0.8.1 die Funktion, Artikel mit leerem content automagisch im Browser zu öffnen, defekt

Damit wäre also der Teil der Arbeit gespart oder, je nach Betrachtungsweise, der vorige Teil der Arbeit meinerseits völlig unnötig. Jetzt interessiert mich aber doch, was denn in 0.8.1 der Bug war und wie er gefixt wurde! Natürlich sind die ganzen Code-Schnipsel oben aus der bereits reparierten Version, so dass sich auch erklärt, wieso das alles so aussieht, als müsste es funktionieren: es funktioniert ja auch!

Es wäre nun eigentlich interessant, herauszufinden, was genau der Bug war. Andererseits scheint die Sonne und ich habe keine Lust mehr 🙂

Ich habe also gelernt:

  • wie man tcpdump (auf Android) benutzt
  • dass man gefälligst die aktuellste Version testen sollte und nicht was älteres
  • dass die Google-Server recht langsam sind, wenn man sich Changesets mit svn diff -c anguckt
  • dass auch TT-RSS unter bestimmten Umständen meine IMEI (md5’ed) nach Hause telefoniert, wenn man an das Projekt gespendet hat. Fail.
Advertisements
Kategorien:informatik, livehacking, oss
  1. Es gibt noch keine Kommentare.
  1. No trackbacks yet.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: