3

I have a problem using TCPDump from my Android-Application. It is supposed to read the output from tcpdump line by line and process it within my Application.
The Problem is:
Sometimes the code works fine, it reads the captured packets immediately. But sometimes, ReadLine blocks until I kill the tcpdump process from the Linux-Console (killall tcpdump). After doing that, my loop is processed for each line (sometimes 10, sometimes 1 or 2) - which means, the readLine should have worked, but didn´t.
I read about similar problems, but did not find any solution for this problem...
THANKS!!

public class ListenActivity extends Activity {

static ArrayList<Packet> packetBuffer = new ArrayList<Packet>();
static Process tcpDumpProcess = null;
static ListenThread thread = null;
public static final String TCPDUMP_COMMAND = "tcpdump -A -s0 | grep -i -e 'Cookie'\n";

private InputStream  inputStream = null;
private OutputStream outputStream = null;

@Override
protected void onStart() {
    super.onStart();
    try {
        tcpDumpProcess = new ProcessBuilder().command("su").redirectErrorStream(true).start();
        inputStream = tcpDumpProcess.getInputStream();
        outputStream = tcpDumpProcess.getOutputStream();
        outputStream.write(TCPDUMP_COMMAND.getBytes("ASCII"));
    } catch (Exception e) {
        Log.e("FSE", "", e);
    }
    thread = new ListenThread(new BufferedReader(new InputStreamReader(inputStream)));
    thread.start();
}

private class ListenThread extends Thread {

    public ListenThread(BufferedReader reader) {
        this.reader = reader;
    }

    private BufferedReader reader = null;

    @Override
    public void run() {

        reader = new BufferedReader(new InputStreamReader(inputStream));
        while (true) {
            try {                   
                String received = reader.readLine();
                Log.d("FS", received);
                Packet pReceived = Packet.analyze(received);
                if (pReceived != null) {
                    packetBuffer.add(pReceived);
                }
            } catch (Exception e) {
                Log.e("FSE", "", e);
            }

        }

    }

}

}

andreas911
  • 31
  • 1
  • 2
  • Is there a reason why you're not using [jnetpcap](http://jnetpcap.com/) directly? Reading another process's output feels like the long way around to solve your problem. – sarnold Apr 19 '11 at 10:21
  • To be honest, I didn´t know about jnetpcap :-) This really looks like what I need... I´m having a deeper look at it and try to find out if it has been successfully compiled for Android-ARM. Thanks!! – andreas911 Apr 19 '11 at 12:46

2 Answers2

5

Because output sent to pipes is usually block buffered, both the tcpdump process and the grep process will be waiting until they've received enough data to bother sending it onto your program. You're very lucky though, both programs you have chosen to use are prepared to modify their buffer behavior (using the setvbuf(3) function internally, in case you're curious about the details):

For tcpdump(8):

   -l     Make stdout line buffered.  Useful if you want to see
          the data while capturing it.  E.g.,
          ``tcpdump  -l  |  tee dat'' or ``tcpdump  -l   >
          dat  &  tail  -f  dat''.

For grep(1):

   --line-buffered
          Use line buffering on output.  This can cause a
          performance penalty.

Try this:

"tcpdump -l -A -s0 | grep --line-buffered -i -e 'Cookie'\n";
sarnold
  • 102,305
  • 22
  • 181
  • 238
  • Well - this sounds like being exactly my problem (the block-buffering) - but unfortunately grep on Android does not have the --line-buffered option... *argh*. Is there any possibility to decrease the blocksize or disable buffering at all for this process? – andreas911 Apr 19 '11 at 12:39
  • 1
    @andreas911, if you were to run a program such as `pty` (from [Advanced Programming in the Unix Environment](http://www.apuebook.com/)) that creates a pseudo-terminal for running arbitrary programs, you could trick `grep` into thinking that its output is a terminal and thus should use line-buffered output. It'd probably be easier to just supply your own `grep` program, or perform the filtering in your Java program. Assuming jnetpcap isn't the better answer altogether. :) – sarnold Apr 19 '11 at 23:18
0

I don't understand why, but even with the -l option the buffer is too large if you read on the standard output of the process wherein you run tcpdump. I solve this problem by redirect TcpDump's output to a file and read this file in another thread. The TcpDump command should be something like :

tcpdump -l-A -s0 > /data/local/output.txt

The run method inside your thread have to be change to read in the output file :

File dumpedFile = new File("/data/local/output.txt");
//open a reader on the tcpdump output file
BufferedReader reader = new BufferedReader(new FileReader(dumpedFile));
String temp = new String();
//The while loop is broken if the thread is interrupted   
while (!Thread.interrupted()) {    
    temp = reader.readLine();
    if (temp!=null) {
        Log.e("READER",new String(temp));    
    }
}

I dont exactly know what you want to do with grep but I think it's possible do achieve the same actions with a regexp inside the Java code.

You should also be aware that the TcpDump's process will never end, so you have to kill it when your activity is paused or distroy. You can have a look here to my blog post, I explain my whole code to start/stop tcpdump.

a.b.d
  • 2,190
  • 3
  • 26
  • 26
  • 1
    Welcome to Stack Overflow. Please note that SO is intended to be a repository of excellent questions and answers, not a link exchange service. Please copy the relevant portions of your blog post into your answers in the future. – sarnold Jun 01 '11 at 23:30