tunnel is a semi-functional program to establish and negotiate a tcp connection via an intermediate telnet connection. It is a console based utility.

Audience: linux networking hackers
Requires: gcc
Language: C
Licence: Don't blame me

I wrote the program in order to connect to a system behind a Cisco 1600 router which was only running ssh and not telnet. Unfortunately, this particular router strips characters 0x00 and 0xFF, so the program does not work for this purpose.

We hope you can use it for some other purpose:

  • Logging into your favourite telnet server without entering the password each time. Instead you get to store it in clear text on your hard disk drive.
  • Getting past a stupid sign-on process - e.g. logging into your AS/400 and then running qsh and then setting your path.
  • Things that you would use expect for (I expect) if you knew how expect worked, or had it installed.

What it does
The program does this:

  • Connect: Opens a connection to the specified host and port
  • Negotiate: Wait for a prompt, then send a response. The prompt and response are stored in a file. The program reads prompts and responses from the file until it reaches the end of the file.
  • Passthrough: After the connection has been negotiated, the system switches to passthrough mode. Any available input is sent to the remote system, and any available input is read from the remote system.


+------------+        +------------+        +------------+
|            |        |            |        |            |
|            |        | Non-routing|        | Target     |
| My machine |--------|   router   |--------|            |
|            |        |            |        |            |
|            |        |            |        |            |
+------------+        +------------+        +------------+

This chat script logs in to the router, tries really hard to make it 8 bit clean without echo, and requests a further connection to the otherwise inaccessible server: " . le_code(le_file("chatscript")) . " The only other thing is to get ssh to run it: " . le_code(" ssh -v -o 'proxycommand ./tunnel 23 chatscript' killall tunnel ") . " Instead of negotiating a connection itself, ssh uses stdin and stdout of the tunnel command. Note that ssh does not kill the program -- if someone can suggest why, mail me. I do not claim to have debugged it.


  • tunnel.c - utility to negotiate tricky connections
  • allchars.c - print out characters 0 to 255. You can use this, together with netcat -l -p 55555 | hex on the remote machine to see whether the connection is 8 bit clean (in the one direction, at least). Guess what I discovered when I sent this to the cisco router I wrote the program for? It's not 8 bit clean!
  • Makefile - Make life simpler
  • Bugs:

    • The code has not been even remotely checked for bugs. It discards error results everywhere.
    • When running under ssh, the program does not exit when the connection is terminated. This is probably bad. In fact, I noticed it was doing this because my laptop started to get hot from the CPU loop.
    • The program does not adequately handle interrupted system calls.
    • The program does not really work
    • You are reading this


    • Changes to ssh - If anyone wants to convince the openssh people to support escaping certain characters in the raw ssh stream, I would tolerate that. It should probably be a client side request -- you will need to decide how this could be fitted into the ssh protocol before the key exchange.
    • Cisco - If anyone can suggest a way to convince a Cisco 1600 router to relay all eight bit values I will give it a try.
    • Real proxy - It would be nice to have it listen on a local port and forward connections to a remote port (and go through the chat sequence, of course). That's not what I want to do right at the moment.

    Author: andrew at ledge dot co dot za