Several weeks ago my friend Madars was in an airport in the Netherlands and he wanted to login into his server via ssh. It turned out that their public internet had only ports 80 and 443 open so he couldn't do that. He asked me if I could proxy either port 80 or 443 to his server. Surely, I had a solution. I modified the tcp proxy server that I had written for my Turn any Linux computer into SOCKS5 proxy in one command article and did:

sudo ./ 443

This proxied the port 443 on my server to ssh port. Now Madars could do

ssh -p 443

and he got connected to his server. Mission accomplished.

Here is the code of,

use warnings;
use strict;

use IO::Socket::INET;
use IO::Select;

my @allowed_ips = ('all', '');
my $ioset = IO::Select->new;
my %socket_map;

my $debug = 1;

sub new_conn {
    my ($host, $port) = @_;
    return IO::Socket::INET->new(
        PeerAddr => $host,
        PeerPort => $port
    ) || die "Unable to connect to $host:$port: $!";

sub new_server {
    my ($host, $port) = @_;
    my $server = IO::Socket::INET->new(
        LocalAddr => $host,
        LocalPort => $port,
        ReuseAddr => 1,
        Listen    => 100
    ) || die "Unable to listen on $host:$port: $!";

sub new_connection {
    my $server = shift;
    my $remote_host = shift;
    my $remote_port = shift;

    my $client = $server->accept;
    my $client_ip = client_ip($client);

    unless (client_allowed($client)) {
        print "Connection from $client_ip denied.\n" if $debug;
    print "Connection from $client_ip accepted.\n" if $debug;

    my $remote = new_conn($remote_host, $remote_port);

    $socket_map{$client} = $remote;
    $socket_map{$remote} = $client;

sub close_connection {
    my $client = shift;
    my $client_ip = client_ip($client);
    my $remote = $socket_map{$client};

    delete $socket_map{$client};
    delete $socket_map{$remote};


    print "Connection from $client_ip closed.\n" if $debug;

sub client_ip {
    my $client = shift;
    return inet_ntoa($client->sockaddr);

sub client_allowed {
    my $client = shift;
    my $client_ip = client_ip($client);
    return grep { $_ eq $client_ip || $_ eq 'all' } @allowed_ips;

die "Usage: $0 <local port> <remote_host:remote_port>" unless @ARGV == 2;

my $local_port = shift;
my ($remote_host, $remote_port) = split ':', shift();

print "Starting a server on$local_port\n";
my $server = new_server('', $local_port);

while (1) {
    for my $socket ($ioset->can_read) {
        if ($socket == $server) {
            new_connection($server, $remote_host, $remote_port);
        else {
            next unless exists $socket_map{$socket};
            my $remote = $socket_map{$socket};
            my $buffer;
            my $read = $socket->sysread($buffer, 4096);
            if ($read) {
            else {


Download link: tcp proxy 2 (
Download URL:
Downloaded: 9186 times

I also pushed the to a new GitHub repository: on GitHub.



March 31, 2011, 16:45

Simple and elegant!

I wrote a TCP proxy in Python a few weeks ago. It was on a whiteboard for an interview though, assuming that there are already functions for establishing new connections and close them later. May be I can write a complete program, and see how it really performs.

March 31, 2011, 16:49

F*#@!g firewalls!!! :-D

I encountered the same problem, but my 443 port was already in use by my HTTP server, so I did a similar proxy with a simple dispatching technique to detect HTTP from SSH on a single port...

Have a look at my solution, I just posted it on our blog.

Best regards

Dirk Permalink
April 01, 2011, 06:07


September 06, 2012, 12:00

I would like to see this code. Where I can found it?

April 02, 2011, 01:42

Have you heard of SSLH? Not something your friend could have done at the airport, but it's a great way to get access.

What's even more fun is tossing OpenVPN into the mix. Since OpenVPN is smart enough to proxy port 443 already, you can combo SSLH & OpenVPN. Have sslh sitting on, and let it redirect to ssh & openvpn. Then OpenVPN will sniff the traffic, and if it's really HTTPS traffic, send it along to the web server. Bit more complex, and does add some latency, but really really fun & silly to do. :-)

user Permalink
April 04, 2011, 13:19

Such proxy also can be done with great program named "socat". It can connect almost everything-to-everything.
Also, as I understand You proxy uses only non-blocking read, but write is blocking. This can cause problems on some applications. I have seen them when using rsync over nc (netcat seems to use blocking write too). socat have no this problem.

April 06, 2011, 02:41

kit kit

mindoula Permalink
September 25, 2012, 11:32

Hi! Proxy very useful and very convenient, but it does not transfer header for http - HTTP_X_REAL_IP, HTTP_X_FORWARDED_FOR.
I did not programing on perl. And please help correct this problem. Thanks a lot.

Adam vonNieda Permalink
October 09, 2012, 14:02

This proxy works great, and really helped me out in a pinch. Thanks!


November 01, 2012, 03:02

Hi! Thanks for sharing your Perl TCP. I forked you on the GitHub. This seed script also inspired me to write an asynchronous Python TCP proxy tool with injection capability. You may view & fork at:

ryne Permalink
February 01, 2013, 15:33

why note > ssh user@ -L 80: -g > ssh -p 80

Bastien Permalink
August 22, 2018, 15:59


nice script. thanks for sharing!

I use it for analyzing a nsaty error in communication between my appserver and the backend.

If I want to see in and output from communication (like man in the middle reading) where does i have to print it? I tried here

else {
            next unless exists $socket_map{$socket};
            my $remote = $socket_map{$socket};
            my $buffer;
            my $read = $socket->sysread($buffer, 4096);
            if ($read) {
            print "$buffer\n"; #<----- here

but that seems to not show me all.

thanks a lot.

Leave a new comment

(why do I need your e-mail?)

(Your twitter handle, if you have one.)

Type the word "apple_254": (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.