-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhashcash-proxy.pl
executable file
·186 lines (159 loc) · 4.83 KB
/
hashcash-proxy.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/perl
use warnings;
use strict;
use JSON;
use Digest::SHA qw(sha256_hex);
use Getopt::Long;
use String::Random qw(random_regex);
use Socket qw(PF_INET PF_INET6 IN6ADDR_ANY inet_ntop);
use IO::Socket;
use IO::Socket::IP -register;
use IO::Select;
my $json = JSON->new->canonical;
my $usage = "Usage: $0 --listen_port=1080 --target_host=localhost --target_port=50001 [--listen_ipv6] [--target_ipv6]";
my $listen_port=0;
my $target_host="";
my $target_port=0;
my $listen_ipv6=0;
my $target_ipv6=0;
GetOptions (
"listen_port=i" => \$listen_port,
"target_port=i" => \$target_port,
"target_host=s" => \$target_host,
"listen_ipv6" => \$listen_ipv6,
"target_ipv6" => \$target_ipv6,
)
or die $usage;
#Mandatory options
$listen_port and $target_port and ($target_host ne "")
or die $usage;
my $listen_domain = ($listen_ipv6)? PF_INET6 : PF_INET;
my $target_domain = ($target_ipv6)? PF_INET6 : PF_INET;
my $serverside = ($0 =~ /server/);
my @allowed_ips = ('1.2.3.4', '5.6.7.8', '127.0.0.1', '192.168.1.2');
my $ioset = IO::Select->new;
my %socket_map;
my $debug = 1;
sub new_conn {
my ($host, $port) = @_;
return IO::Socket::IP->new(
Domain => $target_domain,
PeerAddr => $host,
PeerPort => $port
) || die "Unable to connect to $host:$port: $!";
}
sub new_server {
my ($host, $port) = @_;
my $server = IO::Socket::IP->new(
Domain => $listen_domain,
LocalAddr => $host,
LocalPort => $port,
ReuseAddr => 1,
Listen => 100
) || die "Unable to listen on $host:$port: $!";
}
sub new_connection {
my $server = shift;
my $client = $server->accept;
my $client_ip = client_ip($client);
unless (client_allowed($client)) {
print "Connection from $client_ip denied.\n" if $debug;
$client->close;
return;
}
print "Connection from $client_ip accepted.\n" if $debug;
my $remote = new_conn($target_host, $target_port);
$ioset->add($client);
$ioset->add($remote);
$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};
$ioset->remove($client);
$ioset->remove($remote);
delete $socket_map{$client};
delete $socket_map{$remote};
$client->close;
$remote->close;
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 } @allowed_ips;
}
sub check_pow {
my $h = shift;
not exists $h->{'pow'} and return 0;
my $pow = $h->{'pow'};
delete $h->{'pow'};
my $sha256 = sha256_hex($json->encode($h));
($pow ne $sha256) and return 0;
delete $h->{'nonce'};
$sha256 =~ /^0000/ and return 1;
return 0;
}
sub set_pow {
my $h = shift;
my $nonce = "";
my $sha256 = "zz";
while ($sha256 !~ /^0000/) {
$nonce = random_regex('[A-Za-z0-9]' x 10);
$h->{'nonce'} = $nonce;
$sha256 = sha256_hex($json->encode($h));
}
$h->{pow} = $sha256;
return $json->encode($h);
}
my $server;
if ($listen_ipv6) {
print "Starting a server on :::$listen_port\n";
$server = new_server(inet_ntop(PF_INET6,IN6ADDR_ANY), $listen_port);
} else {
print "Starting a server on 0.0.0.0:$listen_port\n";
$server = new_server('0.0.0.0', $listen_port);
}
$ioset->add($server);
while (1) {
for my $socket ($ioset->can_read) {
if ($socket == $server) {
new_connection($server);
} else {
next unless exists $socket_map{$socket};
my $remote = $socket_map{$socket};
my $buffer;
my $read = $socket->sysread($buffer, 4096);
if ($read) {
my $h = $json->decode($buffer);
if (exists $h->{'method'}) {
#request from client
if ($serverside) {
#we are on the server
if(check_pow($h)) {
#pow ok => send to server
$remote->syswrite($buffer);
} else {
#pow not ok => do nothing
}
} else {
#We are on the client side
my $new_buffer = set_pow($h);
$remote->syswrite($new_buffer."\n");
}
} else {
#answer from server to client => no pow
$remote->syswrite($buffer);
}
} else {
close_connection($socket);
}
}
}
}