added systemd service file
[safeq] / tcp-proxy.pl
1 #!/usr/bin/perl
2 #
3 # Peteris Krumins (peter@catonmat.net)
4 # http://www.catonmat.net  --  good coders code, great reuse
5 #
6 # A simple TCP proxy that implements IP-based access control
7 # Currently the ports are hard-coded, and it proxies
8 # 0.0.0.0:1080 to localhost:55555.
9 #
10 # Written for the article "Turn any Linux computer into SOCKS5
11 # proxy in one command," which can be read here:
12 #
13 # http://www.catonmat.net/blog/linux-socks5-proxy
14 # https://github.com/pkrumins/perl-tcp-proxy/raw/master/tcp-proxy.pl
15
16 use warnings;
17 use strict;
18
19 my ($from,$to) = @ARGV;
20 die "usage: $0 localhost:9335 10.60.3.35:9100\n" unless defined $from && defined $to && $from =~ m/:/ && $to =~ m/:/;
21
22 use IO::Socket;
23 use IO::Select;
24
25 my @allowed_ips; # = ('127.0.0.1'); FIXME -- disabled IP check
26 my $ioset = IO::Select->new;
27 my %socket_map;
28
29 my $debug = 1;
30
31 sub new_conn {
32     my ($host, $port) = @_;
33     return IO::Socket::INET->new(
34         PeerAddr => $host,
35         PeerPort => $port
36     ) || die "Unable to connect to $host:$port: $!";
37 }
38
39 sub new_server {
40     my ($host, $port) = @_;
41     my $server = IO::Socket::INET->new(
42         LocalAddr => $host,
43         LocalPort => $port,
44         ReuseAddr => 1,
45         Listen    => 100
46     ) || die "Unable to listen on $host:$port: $!";
47 }
48
49 sub new_connection {
50     my $server = shift;
51     my $client = $server->accept;
52     my $client_ip = client_ip($client);
53
54     unless (client_allowed($client)) {
55         print "Connection from $client_ip denied.\n" if $debug;
56         $client->close;
57         return;
58     }
59     print "Connection from $client_ip accepted.\n" if $debug;
60
61     my $remote = new_conn(split(/:/,$to));
62     $ioset->add($client);
63     $ioset->add($remote);
64
65     $socket_map{$client} = $remote;
66     $socket_map{$remote} = $client;
67 }
68
69 sub close_connection {
70     my $client = shift;
71     my $client_ip = client_ip($client);
72     my $remote = $socket_map{$client};
73     
74     $ioset->remove($client);
75     $ioset->remove($remote);
76
77     delete $socket_map{$client};
78     delete $socket_map{$remote};
79
80     $client->close;
81     $remote->close;
82
83     print "Connection from $client_ip closed.\n" if $debug;
84 }
85
86 sub client_ip {
87     my $client = shift;
88     return inet_ntoa($client->sockaddr);
89 }
90
91 sub client_allowed {
92         return 1 unless @allowed_ips;
93     my $client = shift;
94     my $client_ip = client_ip($client);
95     return grep { $_ eq $client_ip } @allowed_ips;
96 }
97
98 print "Starting a server on $from -> $to\n";
99 my $server = new_server(split(/:/,$from));
100 $ioset->add($server);
101
102 use Data::Dump qw(dump);
103
104 while (1) {
105     for my $socket ($ioset->can_read) {
106         if ($socket == $server) {
107             new_connection($server);
108         }
109         else {
110             next unless exists $socket_map{$socket};
111             my $remote = $socket_map{$socket};
112             my $buffer;
113             my $read = $socket->sysread($buffer, 4096);
114             if ($read) {
115                 $remote->syswrite($buffer);
116                                 warn "# ", inet_ntoa($socket->sockaddr), " buffer=", dump($buffer);
117             }
118             else {
119                 close_connection($socket);
120             }
121         }
122     }
123 }
124