Search code examples
javalinuxnetwork-programmingstreamtunnel

Rewriting a tcp stream on the fly: how difficult is it? How about taking a dump of said stream?


I'm trying to write a tcp stream 'tunnel' (similar to the ones SSH handles by default) but with one exception, I have to rewrite certain information as it flows through.

I'm certain there's something similar out there but I have not been able to find it. I have three main questions:

  • Is there an easy way to save a tcp stream for observation? (ie using netcat, or a ssh -r/-l/-D, or using some other utility alltogether)
  • how hard is it to rewrite the stream on the fly?

Edit: The information being rewritten would be just the initial authentication.


Solution

  • A straight pass-through tunnel with logging can be cobbled together from existing (or easily found) utilities.

    socat -v -x tcp-l:8080,fork,reuseaddr tcp:localhost:80 2>log
    

    In this example, connecting to http://localhost:8080/ will pass through to http://localhost:80/, and log data transferred to log.

    The tool TCPreen is specialized for this exact purpose.

    If you have root privileges, there are many analyzers such as tcpdump and tcpflow which can capture packets directly from the network, without having to redirect traffic.

    socat can also do some very basic stream modification with the ,cr and ,crnl options, which strip/add/replace \r characters.

    In any case, back to the original question… It's been ages since I've written any Java, and this is totally untested, but a tunnel that can modify traffic before retransmitting isn't difficult.

    public class ForwardAndChangeCaseThread extends Thread {
        private Socket in, out;
        public ForwardAndChangeCaseThread(Socket in, Socket out) {
            this.in = in; this.out = out;
        }
        public void run() {
            byte[] buf = new byte[4096];
            InputStream in = this.in.getInputStream();
            OutputStream out = this.out.getOutputStream();
            int count;
            while ((count = in.read(buf)) > 0) {
                for (int i = 0; i < count; i++)
                    if (buf[i] >= 0x40) buf[i] ^= 0x20;
                out.write(buf, 0, count);
            }
        }
    }
    public class TcpForwarder {
        public static void main(String[] args) {
            ServerSocket listen = new ServerSocket(8080, 1);
            for (;;) {
                Socket local = listen.accept();
                Socket remote = new Socket("localhost", 80);
                new ForwardAndChangeCaseThread(local, remote).start();
                new ForwardAndChangeCaseThread(remote, local).start();
            }
        }
    }