| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
1
|
|
|
1
|
|
7
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
31
|
|
|
2
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
41
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package App::HTTP_Proxy_IMP::Relay; |
|
5
|
|
|
|
|
|
|
use fields ( |
|
6
|
1
|
|
|
|
|
5
|
'fds', # file descriptors |
|
7
|
|
|
|
|
|
|
'conn', # App::HTTP_Proxy_IMP::HTTPConn object |
|
8
|
|
|
|
|
|
|
'acct', # collect accounting |
|
9
|
1
|
|
|
1
|
|
5
|
); |
|
|
1
|
|
|
|
|
3
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
69
|
use App::HTTP_Proxy_IMP::Debug; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
83
|
|
|
12
|
1
|
|
|
1
|
|
6
|
use Scalar::Util 'weaken'; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
40
|
|
|
13
|
1
|
|
|
1
|
|
844
|
use IO::Socket::SSL; |
|
|
1
|
|
|
|
|
78134
|
|
|
|
1
|
|
|
|
|
7
|
|
|
14
|
1
|
|
|
1
|
|
1234
|
use AnyEvent; |
|
|
1
|
|
|
|
|
5487
|
|
|
|
1
|
|
|
|
|
37
|
|
|
15
|
1
|
|
|
1
|
|
7
|
use POSIX '_exit'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
7
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# set if the child should destroy itself after last connection closed |
|
18
|
|
|
|
|
|
|
my $exit_if_no_relays; |
|
19
|
0
|
|
|
0
|
0
|
|
sub exit_if_no_relays { $exit_if_no_relays = pop; } |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# active relay, inserted in new, removed in $idlet timer |
|
22
|
|
|
|
|
|
|
my @relays; |
|
23
|
0
|
|
|
0
|
0
|
|
sub relays { return grep { $_ } @relays } |
|
|
0
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# creates new relay and puts it into @relays as weak reference |
|
26
|
|
|
|
|
|
|
sub new { |
|
27
|
0
|
|
|
0
|
0
|
|
my ($class,$cfd,$upstream,$conn) = @_; |
|
28
|
0
|
|
|
|
|
|
my $self = fields::new($class); |
|
29
|
0
|
|
|
|
|
|
debug("create relay $self"); |
|
30
|
|
|
|
|
|
|
|
|
31
|
0
|
0
|
0
|
|
|
|
if ( $upstream && ! ref($upstream)) { |
|
32
|
0
|
0
|
|
|
|
|
$upstream =~m{\A(?:\[([a-f\d:.]+)\]|([\da-z_\-.]+)):(\d+)\Z} or |
|
33
|
|
|
|
|
|
|
die "invalid upstream specification: $upstream"; |
|
34
|
0
|
|
0
|
|
|
|
$upstream = [ $1||$2, $3 ]; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
0
|
|
|
|
|
|
my $cobj = $conn->new_connection({ |
|
38
|
|
|
|
|
|
|
daddr => $cfd->sockhost, |
|
39
|
|
|
|
|
|
|
dport => $cfd->sockport, |
|
40
|
|
|
|
|
|
|
saddr => $cfd->peerhost, |
|
41
|
|
|
|
|
|
|
sport => $cfd->peerport, |
|
42
|
|
|
|
|
|
|
upstream => $upstream, |
|
43
|
|
|
|
|
|
|
},$self); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#debug("create connection $cobj"); |
|
46
|
0
|
|
|
|
|
|
$self->{conn} = $cobj; |
|
47
|
0
|
|
|
|
|
|
my $cfo = $self->{fds}[0] = App::HTTP_Proxy_IMP::Relay::FD->new(0,$cfd,$self,1); |
|
48
|
0
|
|
|
|
|
|
$cfo->mask( r => 1 ); # enable read |
|
49
|
|
|
|
|
|
|
|
|
50
|
0
|
|
|
|
|
|
push @relays, $self; |
|
51
|
0
|
|
|
|
|
|
weaken($relays[-1]); |
|
52
|
|
|
|
|
|
|
|
|
53
|
0
|
|
|
|
|
|
return $self; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub DESTROY { |
|
57
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
58
|
0
|
|
|
|
|
|
$self->account('destroy'); |
|
59
|
0
|
|
|
|
|
|
$self->xdebug("destroy relay $self"); |
|
60
|
0
|
0
|
0
|
|
|
|
if ( $exit_if_no_relays && ! $self->relays ) { |
|
61
|
|
|
|
|
|
|
# der letzte macht das Licht aus |
|
62
|
0
|
|
|
|
|
|
debug("exit child $$ after last connection"); |
|
63
|
0
|
|
|
|
|
|
_exit(0) |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub acctinfo { |
|
68
|
0
|
|
|
0
|
0
|
|
my ($self,$acct) = @_; |
|
69
|
0
|
|
|
|
|
|
$self->{acct} = $acct; |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
sub account { |
|
72
|
0
|
|
|
0
|
0
|
|
my ($self,$what,%args) = @_; |
|
73
|
0
|
|
|
|
|
|
my $acct = $self->{acct}; |
|
74
|
0
|
0
|
|
|
|
|
$acct = $acct ? { %$acct,%args } : \%args if %args; |
|
|
|
0
|
|
|
|
|
|
|
75
|
0
|
0
|
|
|
|
|
$acct or return; |
|
76
|
0
|
|
|
|
|
|
$self->{acct} = undef; |
|
77
|
0
|
0
|
|
|
|
|
if ( my $t = delete $acct->{start} ) { |
|
78
|
0
|
|
|
|
|
|
$acct->{duration} = AnyEvent->now - $t; |
|
79
|
|
|
|
|
|
|
} |
|
80
|
0
|
|
|
|
|
|
my @msg; |
|
81
|
0
|
|
|
|
|
|
for( sort keys %$acct ) { |
|
82
|
0
|
|
|
|
|
|
my $t; |
|
83
|
0
|
|
|
|
|
|
my $v = $acct->{$_}; |
|
84
|
0
|
0
|
|
|
|
|
if ( ! defined $v ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
85
|
0
|
|
|
|
|
|
next; |
|
86
|
|
|
|
|
|
|
} elsif ( ref($v) eq 'ARRAY') { |
|
87
|
0
|
|
|
|
|
|
$t = "$_=[".join(',',map { _quote($_) } @$v)."]"; |
|
|
0
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
} elsif ( defined $v ) { |
|
89
|
0
|
|
|
|
|
|
$t = "$_="._quote($v); |
|
90
|
|
|
|
|
|
|
} |
|
91
|
0
|
|
|
|
|
|
push @msg,$t; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
0
|
|
|
|
|
|
print STDERR "ACCT @msg\n"; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub _quote { |
|
97
|
0
|
|
|
0
|
|
|
my $text = shift; |
|
98
|
0
|
|
|
|
|
|
$text =~s{([\000-\037\\"\377-\777])}{ sprintf("\\%03o",ord($1)) }eg; |
|
|
0
|
|
|
|
|
|
|
|
99
|
0
|
0
|
|
|
|
|
return $text =~m{ } ? qq["$text"]:$text; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub xdebug { |
|
103
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
104
|
0
|
|
|
|
|
|
my $conn = $self->{conn}; |
|
105
|
0
|
0
|
|
|
|
|
if ( my $xdebug = UNIVERSAL::can($conn,'xdebug') ) { |
|
106
|
0
|
|
|
|
|
|
unshift @_,$conn; |
|
107
|
0
|
|
|
|
|
|
goto &$xdebug; |
|
108
|
|
|
|
|
|
|
} else { |
|
109
|
0
|
|
|
|
|
|
goto &debug; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# non-fatal problem |
|
115
|
|
|
|
|
|
|
sub error { |
|
116
|
0
|
|
|
0
|
0
|
|
my ($self,$reason) = @_; |
|
117
|
0
|
|
0
|
|
|
|
warn "[error] ".( $self->{conn} && $self->{conn}->id || 'noid')." $reason\n"; |
|
118
|
0
|
|
|
|
|
|
return 0; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# fatal problem - close connection |
|
122
|
|
|
|
|
|
|
sub fatal { |
|
123
|
0
|
|
|
0
|
0
|
|
my ($self,$reason) = @_; |
|
124
|
0
|
|
0
|
|
|
|
warn "[fatal] ".( $self->{conn} && $self->{conn}->id || 'noid')." $reason\n"; |
|
125
|
0
|
|
|
|
|
|
$self->close; |
|
126
|
0
|
|
|
|
|
|
return 0; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub connect:method { |
|
130
|
0
|
|
|
0
|
0
|
|
my ($self,$to,$host,$port,$callback,$reconnect) = @_; |
|
131
|
0
|
|
0
|
|
|
|
my $fo = $self->{fds}[$to] ||= App::HTTP_Proxy_IMP::Relay::FD->new($to,undef,$self); |
|
132
|
0
|
|
|
|
|
|
$fo->connect($host,$port,$callback,$reconnect); |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# masks/unmasks fd for dir, rw = r|w |
|
136
|
|
|
|
|
|
|
sub mask { |
|
137
|
0
|
|
|
0
|
0
|
|
my ($self,$dir,$rw,$v) = @_; |
|
138
|
0
|
0
|
|
|
|
|
my $fd = $self->{fds}[$dir] or do { |
|
139
|
0
|
|
|
|
|
|
warn "fd dir=$dir does not exists\n"; |
|
140
|
0
|
|
|
|
|
|
return; |
|
141
|
|
|
|
|
|
|
}; |
|
142
|
0
|
|
|
|
|
|
$fd->mask($rw,$v); |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub fd { |
|
146
|
0
|
|
|
0
|
0
|
|
my ($self,$dir) = @_; |
|
147
|
0
|
|
|
|
|
|
return $self->{fds}[$dir]; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# send some data via fd dir |
|
151
|
|
|
|
|
|
|
sub forward { |
|
152
|
0
|
|
|
0
|
0
|
|
my ($self,$from,$to,$data) = @_; |
|
153
|
0
|
0
|
|
|
|
|
my $fo = $self->{fds}[$to] or return |
|
154
|
|
|
|
|
|
|
$self->fatal("cannot write to $to - no such fo"); |
|
155
|
0
|
|
|
|
|
|
$self->xdebug("$from>$to - forward %d bytes",length($data)); |
|
156
|
0
|
|
|
|
|
|
$fo->write($data,$from); |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# ssl interception, e.g. upgrade both client and server to SSL sockets, |
|
160
|
|
|
|
|
|
|
# where I can read/write unencrypted data |
|
161
|
|
|
|
|
|
|
sub sslify { |
|
162
|
0
|
|
|
0
|
0
|
|
my ($self,$from,$to,$hostname,$callback) = @_; |
|
163
|
0
|
0
|
|
|
|
|
my $conn = $self->{conn} or return; |
|
164
|
0
|
0
|
|
|
|
|
my $mitm = $conn->{mitm} or return; # no MITM needed |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# destroy the current connection object and create a new obne |
|
167
|
0
|
|
|
|
|
|
$conn = $self->{conn} = $conn->clone; |
|
168
|
0
|
|
|
|
|
|
$conn->{intunnel} = 1; |
|
169
|
|
|
|
|
|
|
|
|
170
|
0
|
0
|
|
|
|
|
my $sfo = $self->{fds}[$from] or return |
|
171
|
|
|
|
|
|
|
$self->fatal("cannot startssl $from - no such fo"); |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# stop handling all data |
|
174
|
0
|
|
|
|
|
|
$self->mask($to,r=>0); |
|
175
|
0
|
|
|
|
|
|
$self->mask($from,r=>0); |
|
176
|
0
|
|
|
|
|
|
weaken( my $wself = $self ); |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
my %sslargs = ( |
|
179
|
|
|
|
|
|
|
SSL_verifycn_name => $hostname, |
|
180
|
|
|
|
|
|
|
SSL_verifycn_schema => 'http', |
|
181
|
|
|
|
|
|
|
SSL_hostname => $hostname, # SNI |
|
182
|
|
|
|
|
|
|
$conn->{capath} ? ( |
|
183
|
|
|
|
|
|
|
SSL_verify_mode => SSL_VERIFY_PEER, |
|
184
|
|
|
|
|
|
|
( -d $conn->{capath} ? 'SSL_ca_path' : 'SSL_ca_file' ), |
|
185
|
|
|
|
|
|
|
$conn->{capath} |
|
186
|
0
|
0
|
|
|
|
|
):( |
|
|
|
0
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
SSL_verify_mode => SSL_VERIFY_NONE |
|
188
|
|
|
|
|
|
|
) |
|
189
|
|
|
|
|
|
|
); |
|
190
|
|
|
|
|
|
|
$sfo->startssl( %sslargs, sub { |
|
191
|
0
|
|
|
0
|
|
|
my $sfo = shift; |
|
192
|
0
|
|
|
|
|
|
my ($cert,$key) = $mitm->clone_cert($sfo->{fd}->peer_certificate); |
|
193
|
0
|
0
|
|
|
|
|
my $cfo = $wself->{fds}[$to] or return |
|
194
|
|
|
|
|
|
|
$wself->fatal("cannot startssl $to - no such fo"); |
|
195
|
|
|
|
|
|
|
$cfo->startssl( |
|
196
|
|
|
|
|
|
|
SSL_server => 1, |
|
197
|
|
|
|
|
|
|
SSL_cert => $cert, |
|
198
|
|
|
|
|
|
|
SSL_key => $key, |
|
199
|
|
|
|
|
|
|
sub { |
|
200
|
|
|
|
|
|
|
# allow data again |
|
201
|
0
|
|
|
|
|
|
$self->mask($to,r=>1); |
|
202
|
0
|
|
|
|
|
|
$self->mask($from,r=>1); |
|
203
|
0
|
0
|
|
|
|
|
$callback->() if $callback; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
0
|
|
|
|
|
|
); |
|
206
|
0
|
|
|
|
|
|
}); |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# closes relay |
|
210
|
|
|
|
|
|
|
sub close:method { |
|
211
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
212
|
|
|
|
|
|
|
#debug("close $self"); |
|
213
|
0
|
|
|
|
|
|
undef $self->{conn}; |
|
214
|
0
|
0
|
|
|
|
|
@relays = grep { !$_ or $_ != $self } @relays; |
|
|
0
|
|
|
|
|
|
|
|
215
|
0
|
|
0
|
|
|
|
$_ && $_->close for @{$self->{fds}}; |
|
|
0
|
|
|
|
|
|
|
|
216
|
0
|
|
|
|
|
|
@{$self->{fds}} = (); |
|
|
0
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# shutdown part of relay |
|
220
|
|
|
|
|
|
|
sub shutdown:method { |
|
221
|
0
|
|
|
0
|
0
|
|
my ($self,$dir,$rw,$force) = @_; |
|
222
|
0
|
0
|
|
|
|
|
my $fo = $self->{fds}[$dir] or return; |
|
223
|
0
|
|
|
|
|
|
$fo->shutdown($rw,$force); |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# check for condition, where we cannot transfer anymore data: |
|
227
|
|
|
|
|
|
|
# - nowhere to read and no open requests |
|
228
|
|
|
|
|
|
|
# - nowhere to write too |
|
229
|
|
|
|
|
|
|
sub closeIfDone { |
|
230
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
231
|
0
|
|
|
|
|
|
my $sink = my $drain = ''; |
|
232
|
0
|
|
|
|
|
|
for my $fo (@{$self->{fds}}) { |
|
|
0
|
|
|
|
|
|
|
|
233
|
0
|
0
|
0
|
|
|
|
$fo && $fo->{fd} or next; |
|
234
|
0
|
0
|
|
|
|
|
return if $fo->{rbuf} ne ''; # has unprocessed data |
|
235
|
0
|
0
|
|
|
|
|
return if $fo->{wbuf} ne ''; # has unwritten data |
|
236
|
0
|
0
|
|
|
|
|
$drain .= $fo->{dir} if not $fo->{status} & 0b100; # not read-closed |
|
237
|
0
|
0
|
|
|
|
|
$sink .= $fo->{dir} if not $fo->{status} & 0b010; # not write-closed |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
0
|
0
|
|
|
|
|
if ( $sink eq '' ) { # nowhere to write |
|
241
|
0
|
0
|
|
|
|
|
$DEBUG && $self->xdebug( "close relay because all fd done sink='$sink' "); |
|
242
|
|
|
|
|
|
|
# close relay |
|
243
|
0
|
|
|
|
|
|
return $self->close; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
|
|
246
|
0
|
0
|
|
|
|
|
if ( $drain ne '01' ) { # no reading from both sides |
|
247
|
0
|
|
|
|
|
|
my $conn = $self->{conn}; |
|
248
|
0
|
0
|
0
|
|
|
|
if ( ! $conn or ! $conn->open_requests ) { |
|
249
|
|
|
|
|
|
|
# close relay |
|
250
|
0
|
0
|
|
|
|
|
$DEBUG && $self->xdebug( "close relay because nothing to read and all done"); |
|
251
|
0
|
|
|
|
|
|
return $self->close; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
0
|
0
|
|
|
|
|
$DEBUG && $self->xdebug("drain=$drain sink=$sink rq=".$self->{conn}->open_requests." - keeping open"); |
|
256
|
0
|
|
|
|
|
|
return; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# dump state to debug |
|
261
|
|
|
|
|
|
|
sub dump_state { |
|
262
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
263
|
0
|
|
|
|
|
|
my $conn = $self->{conn}; |
|
264
|
0
|
|
|
|
|
|
my $msg = ''; |
|
265
|
0
|
0
|
|
|
|
|
if ( my $fds = $self->{fds} ) { |
|
266
|
0
|
|
|
|
|
|
my @st; |
|
267
|
0
|
|
|
|
|
|
for( my $i=0;$i<@$fds;$i++) { |
|
268
|
0
|
|
0
|
|
|
|
push @st, sprintf("%d=%03b",$i,$fds->[$i]{status} || 0); |
|
269
|
|
|
|
|
|
|
} |
|
270
|
0
|
|
|
|
|
|
$msg .= " fd:".join(',',@st); |
|
271
|
|
|
|
|
|
|
} |
|
272
|
0
|
|
|
|
|
|
$msg = $conn->dump_state().$msg; |
|
273
|
0
|
0
|
|
|
|
|
return $msg if defined wantarray; |
|
274
|
0
|
|
|
|
|
|
debug($msg); |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
my $idlet = AnyEvent->timer( |
|
279
|
|
|
|
|
|
|
after => 5, |
|
280
|
|
|
|
|
|
|
interval => 5, cb => sub { |
|
281
|
|
|
|
|
|
|
@relays = grep { $_ } @relays or return; |
|
282
|
|
|
|
|
|
|
#debug("check timeouts for %d conn",+@relays); |
|
283
|
|
|
|
|
|
|
my $now = AnyEvent->now; |
|
284
|
|
|
|
|
|
|
RELAY: for my $r (@relays) { |
|
285
|
|
|
|
|
|
|
# timeout depends on the state of the relay and child |
|
286
|
|
|
|
|
|
|
# if there are active requests set it to 60, if not (e.g. |
|
287
|
|
|
|
|
|
|
# idle keep-alive connections) to 30. If this is a forked |
|
288
|
|
|
|
|
|
|
# child with no listener which should close after all |
|
289
|
|
|
|
|
|
|
# requests are done close idle keep-alive connections faster, |
|
290
|
|
|
|
|
|
|
# e.g. set timeout to 1 |
|
291
|
|
|
|
|
|
|
my $idle = ! $r->{conn}->open_requests; |
|
292
|
|
|
|
|
|
|
my $timeout = |
|
293
|
|
|
|
|
|
|
! $idle ? 60 : |
|
294
|
|
|
|
|
|
|
$exit_if_no_relays ? 1 : |
|
295
|
|
|
|
|
|
|
30; |
|
296
|
|
|
|
|
|
|
for my $fo (@{$r->{fds}}) { |
|
297
|
|
|
|
|
|
|
next RELAY if $_->{didit} + $timeout > $now; |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
$r->xdebug("close because of timeout"); |
|
300
|
|
|
|
|
|
|
$r->close |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
); |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
############################################################################ |
|
306
|
|
|
|
|
|
|
# Filehandle |
|
307
|
|
|
|
|
|
|
############################################################################ |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
package App::HTTP_Proxy_IMP::Relay::FD; |
|
310
|
1
|
|
|
1
|
|
2381
|
use Carp 'croak'; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
72
|
|
|
311
|
1
|
|
|
1
|
|
7
|
use Scalar::Util 'weaken'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
81
|
|
|
312
|
1
|
|
|
1
|
|
7
|
use App::HTTP_Proxy_IMP::Debug; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
93
|
|
|
313
|
1
|
|
|
1
|
|
613
|
use AnyEvent::Socket qw(tcp_connect format_address); |
|
|
1
|
|
|
|
|
21565
|
|
|
|
1
|
|
|
|
|
67
|
|
|
314
|
1
|
|
|
1
|
|
8
|
use IO::Socket::SSL; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
10
|
|
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
use fields ( |
|
317
|
1
|
|
|
|
|
7
|
'dir', # direction 0,1 |
|
318
|
|
|
|
|
|
|
'fd', # file descriptor |
|
319
|
|
|
|
|
|
|
'host', # destination hostname |
|
320
|
|
|
|
|
|
|
'status', # bitmap of read_shutdown|write_shutdown|connected |
|
321
|
|
|
|
|
|
|
'relay', # weak link to relay |
|
322
|
|
|
|
|
|
|
'didit', # time of last activity (read/write) |
|
323
|
|
|
|
|
|
|
'rbuf', # read buffer (read but not processed) |
|
324
|
|
|
|
|
|
|
'rsub', # read handler |
|
325
|
|
|
|
|
|
|
'rwatch', # AnyEvent watcher - undef if read is disabled |
|
326
|
|
|
|
|
|
|
'wbuf', # write buffer (not yet written to socket) |
|
327
|
|
|
|
|
|
|
'wsub', # write handler |
|
328
|
|
|
|
|
|
|
'wwatch', # AnyEvent watcher - undef if write is disabled |
|
329
|
|
|
|
|
|
|
'wsrc', # source of writes for stalled handling |
|
330
|
1
|
|
|
1
|
|
158
|
); |
|
|
1
|
|
|
|
|
2
|
|
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
sub new { |
|
333
|
0
|
|
|
0
|
|
|
my ($class,$dir,$fd,$relay,$connected) = @_; |
|
334
|
0
|
|
|
|
|
|
my $self = fields::new($class); |
|
335
|
0
|
|
|
|
|
|
$self->{dir} = $dir; |
|
336
|
0
|
|
|
|
|
|
$self->{fd} = $fd; |
|
337
|
0
|
0
|
|
|
|
|
$self->{status} = $connected ? 0b001 : 0; |
|
338
|
|
|
|
|
|
|
#weaken( $self->{relay} = $relay ); |
|
339
|
0
|
|
|
|
|
|
$self->{relay} = $relay; |
|
340
|
0
|
|
|
|
|
|
$self->{rbuf} = $self->{wbuf} = ''; |
|
341
|
0
|
|
|
|
|
|
return $self; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
sub xdebug { |
|
345
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
346
|
0
|
|
|
|
|
|
my $conn = $self->{relay}{conn}; |
|
347
|
0
|
0
|
|
|
|
|
if ( my $xdebug = UNIVERSAL::can($conn,'xdebug') ) { |
|
348
|
0
|
|
|
|
|
|
my $msg = "[$self->{dir}] ".shift(@_); |
|
349
|
0
|
|
|
|
|
|
unshift @_,$conn,$msg; |
|
350
|
0
|
|
|
|
|
|
goto &$xdebug; |
|
351
|
|
|
|
|
|
|
} else { |
|
352
|
0
|
|
|
|
|
|
goto &debug; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub close:method { |
|
357
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
358
|
0
|
|
|
|
|
|
$self->xdebug("close"); |
|
359
|
0
|
0
|
|
|
|
|
if ( $self->{fd} ) { |
|
360
|
0
|
|
|
|
|
|
$self->{fd} = undef; |
|
361
|
0
|
|
|
|
|
|
delete $self->{relay}{fds}[$self->{dir}]; |
|
362
|
0
|
|
|
|
|
|
$self->{relay}->closeIfDone; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
0
|
|
|
|
|
|
%$self = (); |
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub reset { |
|
368
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
369
|
0
|
|
|
|
|
|
$self->xdebug("reset"); |
|
370
|
0
|
0
|
|
|
|
|
close($self->{fd}) if $self->{fd}; |
|
371
|
|
|
|
|
|
|
$self->{fd} = |
|
372
|
|
|
|
|
|
|
$self->{rwatch} = $self->{rsub} = |
|
373
|
|
|
|
|
|
|
$self->{wwatch} = $self->{wsub} = |
|
374
|
|
|
|
|
|
|
$self->{host} = |
|
375
|
|
|
|
|
|
|
$self->{wsrc} = |
|
376
|
0
|
|
|
|
|
|
undef; |
|
377
|
0
|
|
|
|
|
|
$self->{status} = $self->{didit} = 0; |
|
378
|
0
|
|
|
|
|
|
$self->{rbuf} = $self->{wbuf} = ''; |
|
379
|
0
|
|
|
|
|
|
return 1; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# attempt to shutdown fd. |
|
383
|
|
|
|
|
|
|
# don't shutdown(1) if wbuf ne '' && ! $force |
|
384
|
|
|
|
|
|
|
sub shutdown:method { |
|
385
|
0
|
|
|
0
|
|
|
my ($self,$rw,$force) = @_; |
|
386
|
0
|
0
|
|
|
|
|
my $write = $rw eq 'r' ? 0 : $rw eq 'w' ? 1 : $rw; |
|
|
|
0
|
|
|
|
|
|
|
387
|
0
|
0
|
|
|
|
|
my $stat = $write ? 0b010 : 0b100; |
|
388
|
0
|
0
|
0
|
|
|
|
return if $self->{status} & $stat && ! $force; # no change |
|
389
|
|
|
|
|
|
|
|
|
390
|
0
|
|
|
|
|
|
$self->{status} |= $stat; |
|
391
|
0
|
0
|
0
|
|
|
|
if ( $write && $self->{wbuf} ne '' ) { |
|
392
|
|
|
|
|
|
|
$self->xdebug("called shutdown $rw fn=".fileno($self->{fd}). |
|
393
|
0
|
|
|
|
|
|
" wbuf.len=".length($self->{wbuf})); |
|
394
|
0
|
0
|
|
|
|
|
return if ! $force; # will shutdown once all is written |
|
395
|
0
|
|
|
|
|
|
$self->{wbuf} = ''; # drop rest |
|
396
|
0
|
|
|
|
|
|
undef $self->{wsrc}; # don't re-enable, unclear state |
|
397
|
0
|
|
|
|
|
|
undef $self->{wwatch}; |
|
398
|
|
|
|
|
|
|
} |
|
399
|
|
|
|
|
|
|
|
|
400
|
0
|
|
|
|
|
|
$self->xdebug("shutdown $rw fn=".fileno($self->{fd})); |
|
401
|
0
|
|
|
|
|
|
shutdown($self->{fd},$write); |
|
402
|
|
|
|
|
|
|
# shutdown on both sides -> close |
|
403
|
0
|
0
|
|
|
|
|
if (( $self->{status} & 0b110 ) == 0b110 ) { |
|
|
|
0
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
|
$self->xdebug( "close fn=".fileno($self->{fd})." because status $self->{status} done"); |
|
405
|
0
|
|
|
|
|
|
$self->close; |
|
406
|
|
|
|
|
|
|
} elsif ( $write ) { |
|
407
|
0
|
|
|
|
|
|
undef $self->{wwatch}; |
|
408
|
|
|
|
|
|
|
} else { |
|
409
|
0
|
|
|
|
|
|
undef $self->{rwatch}; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# if all fd are closed, close the relay too |
|
413
|
0
|
|
|
|
|
|
$self->{relay}->closeIfDone; |
|
414
|
|
|
|
|
|
|
|
|
415
|
0
|
|
|
|
|
|
return 1; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub mask { |
|
420
|
0
|
|
|
0
|
|
|
my ($self,$rw,$val) = @_; |
|
421
|
|
|
|
|
|
|
#debug("$self->{dir} $self->{fd} fn=".fileno($self->{fd})." $rw=>$val"); |
|
422
|
0
|
0
|
|
|
|
|
if ( $rw eq 'r' ) { |
|
|
|
0
|
|
|
|
|
|
|
423
|
0
|
0
|
|
|
|
|
if ( ! $val ) { |
|
424
|
|
|
|
|
|
|
# disable read |
|
425
|
0
|
|
|
|
|
|
undef $self->{rwatch}; |
|
426
|
|
|
|
|
|
|
} else { |
|
427
|
0
|
0
|
|
|
|
|
$self->{status} & 0b100 and return 0; # read shutdown already |
|
428
|
0
|
|
0
|
0
|
|
|
$self->{rsub} ||= sub { _read($self) }; |
|
|
0
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
$self->{rwatch} = AnyEvent->io( |
|
430
|
|
|
|
|
|
|
fh => $self->{fd}, |
|
431
|
|
|
|
|
|
|
poll => 'r', |
|
432
|
|
|
|
|
|
|
cb => ref($val) ? $val : $self->{rsub} |
|
433
|
0
|
0
|
|
|
|
|
); |
|
434
|
|
|
|
|
|
|
} |
|
435
|
|
|
|
|
|
|
} elsif ( $rw eq 'w' ) { |
|
436
|
0
|
0
|
|
|
|
|
if ( ! $val ) { |
|
437
|
|
|
|
|
|
|
# disable write |
|
438
|
0
|
|
|
|
|
|
undef $self->{wwatch}; |
|
439
|
|
|
|
|
|
|
} else { |
|
440
|
0
|
0
|
|
|
|
|
$self->{status} & 0b010 and return 0; # write shutdown already |
|
441
|
0
|
|
0
|
0
|
|
|
$self->{wsub} ||= sub { _writebuf($self) }; |
|
|
0
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
$self->{wwatch} = AnyEvent->io( |
|
443
|
|
|
|
|
|
|
fh => $self->{fd}, |
|
444
|
|
|
|
|
|
|
poll => 'w', |
|
445
|
|
|
|
|
|
|
cb => ref($val) ? $val : $self->{wsub} |
|
446
|
0
|
0
|
|
|
|
|
); |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
} else { |
|
449
|
0
|
|
|
|
|
|
croak("cannot set mask for $rw"); |
|
450
|
|
|
|
|
|
|
} |
|
451
|
0
|
|
|
|
|
|
return 1; |
|
452
|
|
|
|
|
|
|
} |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
# write data, gets written from relay->send |
|
455
|
|
|
|
|
|
|
sub write:method { |
|
456
|
0
|
|
|
0
|
|
|
my ($self,$data,$from) = @_; |
|
457
|
0
|
|
|
|
|
|
my $n = 0; |
|
458
|
0
|
0
|
|
|
|
|
if ( $self->{wbuf} eq '' ) { |
|
459
|
|
|
|
|
|
|
# no buffered data, set as buffer and try to write immediately |
|
460
|
0
|
|
|
|
|
|
$self->{wbuf} = $data; |
|
461
|
0
|
|
0
|
|
|
|
$n = _writebuf($self,$from) // return; # fatal? |
|
462
|
|
|
|
|
|
|
} else { |
|
463
|
|
|
|
|
|
|
# only append to buffer, will be written on write ready |
|
464
|
0
|
|
|
|
|
|
$self->{wbuf} .= $data; |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
|
|
467
|
0
|
0
|
0
|
|
|
|
if ( $self->{wbuf} ne '' |
|
468
|
|
|
|
|
|
|
&& ! $self->{wsrc}{$from}++ ) { |
|
469
|
|
|
|
|
|
|
# newly stalled, disable reads on $from for now |
|
470
|
0
|
|
|
|
|
|
$self->{relay}->mask($from, r=>0); |
|
471
|
|
|
|
|
|
|
} |
|
472
|
0
|
|
|
|
|
|
return $n; |
|
473
|
|
|
|
|
|
|
} |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
# gets called if wbuf is not empty, either from write or from callback |
|
476
|
|
|
|
|
|
|
# when fd is writable again |
|
477
|
|
|
|
|
|
|
sub _writebuf { |
|
478
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
479
|
|
|
|
|
|
|
#debug("write $self fn=".fileno($self->{fd})); |
|
480
|
0
|
|
|
|
|
|
my $n = syswrite($self->{fd},$self->{wbuf}); |
|
481
|
|
|
|
|
|
|
#debug("write(%s,%d) -> %s", $self->{dir},length($self->{wbuf}), (defined $n ? $n : $!)); |
|
482
|
0
|
0
|
|
|
|
|
if ( ! defined $n ) { |
|
483
|
|
|
|
|
|
|
$self->{relay}->fatal("write($self->{dir}) failed: $!") |
|
484
|
0
|
0
|
0
|
|
|
|
unless $!{EINTR} or $!{EAGAIN}; |
|
485
|
0
|
|
|
|
|
|
return; |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
0
|
|
|
|
|
|
substr($self->{wbuf},0,$n,''); |
|
489
|
0
|
|
|
|
|
|
$self->{didit} = AnyEvent->now; |
|
490
|
|
|
|
|
|
|
|
|
491
|
0
|
0
|
|
|
|
|
if ( $self->{wbuf} eq '' ) { |
|
492
|
|
|
|
|
|
|
# wrote everything |
|
493
|
|
|
|
|
|
|
#debug("all written to $self->{dir}"); |
|
494
|
0
|
|
|
|
|
|
undef $self->{wwatch}; |
|
495
|
|
|
|
|
|
|
|
|
496
|
0
|
0
|
|
|
|
|
if ( $self->{status} & 0b100 ) { |
|
497
|
|
|
|
|
|
|
# was marked for shutdown |
|
498
|
0
|
|
|
|
|
|
shutdown($self->{fd},1); |
|
499
|
|
|
|
|
|
|
# if all fd are closed, close the relay too |
|
500
|
0
|
|
|
|
|
|
$self->{relay}->closeIfDone; |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
# enable read again on stalled fd |
|
503
|
0
|
0
|
|
|
|
|
if ( my $src = $self->{wsrc} ) { |
|
504
|
0
|
|
|
|
|
|
$self->{relay}->mask($_, r=>1) for (keys %$src); |
|
505
|
|
|
|
|
|
|
} |
|
506
|
|
|
|
|
|
|
} else { |
|
507
|
|
|
|
|
|
|
# need to write more later |
|
508
|
|
|
|
|
|
|
#debug("need to write more"); |
|
509
|
0
|
|
|
|
|
|
mask($self,w=>1); |
|
510
|
|
|
|
|
|
|
} |
|
511
|
0
|
|
|
|
|
|
return $n; |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# gets called if data are available on the socket |
|
515
|
|
|
|
|
|
|
# but only, if we don't have unsent data in wbuf |
|
516
|
|
|
|
|
|
|
# reads data into rbuf and calls connection->in |
|
517
|
|
|
|
|
|
|
sub _read:method { |
|
518
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
519
|
|
|
|
|
|
|
#debug("read $self fn=".fileno($self->{fd})); |
|
520
|
0
|
|
|
|
|
|
my $n = sysread($self->{fd},$self->{rbuf},2**15,length($self->{rbuf})); |
|
521
|
|
|
|
|
|
|
#debug("read done: ". (defined $n ? $n : $!)); |
|
522
|
0
|
0
|
|
|
|
|
if ( ! defined $n ) { |
|
523
|
0
|
0
|
0
|
|
|
|
if ( ! $!{EINTR} and ! $!{EAGAIN} ) { |
|
524
|
|
|
|
|
|
|
# complain only if we are inside a request |
|
525
|
|
|
|
|
|
|
# timeouts after inactivity are normal |
|
526
|
|
|
|
|
|
|
return $self->{relay}->fatal("read($self->{dir}) failed: $!") |
|
527
|
0
|
0
|
|
|
|
|
if $self->{relay}{conn}->open_requests; |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# close connection |
|
530
|
0
|
|
|
|
|
|
$self->xdebug("closing relay because of read error on $self->{dir}"); |
|
531
|
0
|
|
|
|
|
|
return $self->{relay}->close; |
|
532
|
|
|
|
|
|
|
} |
|
533
|
0
|
|
|
|
|
|
return; |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
0
|
|
|
|
|
|
$self->{didit} = AnyEvent->now; |
|
537
|
|
|
|
|
|
|
my $bytes = $self->{relay}{conn} |
|
538
|
0
|
|
|
|
|
|
->in($self->{dir},$self->{rbuf},!$n,$self->{didit}); |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
# fd/relay closed from within in() ? |
|
541
|
0
|
0
|
|
|
|
|
defined $self->{fd} or return; |
|
542
|
|
|
|
|
|
|
|
|
543
|
0
|
0
|
|
|
|
|
if ( $bytes ) { |
|
544
|
|
|
|
|
|
|
# connection accepted $bytes |
|
545
|
0
|
|
|
|
|
|
substr($self->{rbuf},0,$bytes,''); |
|
546
|
|
|
|
|
|
|
} |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
return $self->{relay}->fatal( |
|
549
|
|
|
|
|
|
|
"connection should have taken all remaining bytes on eof") |
|
550
|
0
|
0
|
0
|
|
|
|
if !$n && $self->{rbuf} ne ''; |
|
551
|
|
|
|
|
|
|
|
|
552
|
0
|
0
|
|
|
|
|
$self->shutdown('r') if ! $n; |
|
553
|
|
|
|
|
|
|
} |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
sub connect:method { |
|
556
|
0
|
|
|
0
|
|
|
my ($self,$host,$port,$callback,$reconnect) = @_; |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# down existing connection if we should connect to another host |
|
559
|
|
|
|
|
|
|
$self->reset if $self->{fd} and |
|
560
|
0
|
0
|
0
|
|
|
|
( $reconnect or $self->{host}||'' ne "$host.$port" ); |
|
|
|
|
0
|
|
|
|
|
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
# if we have a connection already, keep it |
|
563
|
0
|
0
|
|
|
|
|
if ( $self->{status} & 0b001 ) { # already connected |
|
564
|
0
|
|
|
|
|
|
$callback->(); |
|
565
|
0
|
|
|
|
|
|
return 1; |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# (re)connect |
|
569
|
0
|
|
|
|
|
|
$self->xdebug("connecting to $host.$port"); |
|
570
|
|
|
|
|
|
|
# async dns lookup + connect |
|
571
|
|
|
|
|
|
|
App::HTTP_Proxy_IMP::Relay::DNS::lookup($host, sub { |
|
572
|
0
|
0
|
|
0
|
|
|
$self->{relay} or return; # relay already closed |
|
573
|
0
|
0
|
|
|
|
|
if ( my $addr = shift ) { |
|
574
|
|
|
|
|
|
|
tcp_connect($addr,$port, sub { |
|
575
|
0
|
0
|
|
|
|
|
if ( my $fd = shift ) { |
|
576
|
0
|
0
|
|
|
|
|
$self->{relay} or return; # relay already closed |
|
577
|
0
|
|
|
|
|
|
$self->{fd} = $fd; |
|
578
|
0
|
|
|
|
|
|
$self->{status} = 0b001; |
|
579
|
0
|
|
|
|
|
|
$self->{host} = "$host.$port"; |
|
580
|
0
|
|
|
|
|
|
$self->xdebug("connect done"); |
|
581
|
0
|
|
|
|
|
|
$self->mask( r => 1 ); |
|
582
|
0
|
|
|
|
|
|
$callback->(); |
|
583
|
|
|
|
|
|
|
} else { |
|
584
|
0
|
|
|
|
|
|
App::HTTP_Proxy_IMP::Relay::DNS::uncache($host,$addr); |
|
585
|
0
|
0
|
|
|
|
|
$self->{relay} or return; # relay already closed |
|
586
|
0
|
|
|
|
|
|
$self->{relay}->fatal("connect to $host.$port failed: $!"); |
|
587
|
|
|
|
|
|
|
} |
|
588
|
0
|
|
|
|
|
|
}); |
|
589
|
|
|
|
|
|
|
} else { |
|
590
|
|
|
|
|
|
|
$self->{relay}->fatal( |
|
591
|
0
|
|
|
|
|
|
"connect to $host.$port failed: no such host (DNS)"); |
|
592
|
|
|
|
|
|
|
} |
|
593
|
0
|
|
|
|
|
|
}); |
|
594
|
0
|
|
|
|
|
|
return -1; |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
sub startssl { |
|
598
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
599
|
|
|
|
|
|
|
$self->{rbuf} eq '' or return |
|
600
|
0
|
0
|
|
|
|
|
$self->{relay}->fatal("read buf $self->{dir} not empty before starting SSL: '$self->{rbuf}'"); |
|
601
|
|
|
|
|
|
|
$self->{wbuf} eq '' or return |
|
602
|
0
|
0
|
|
|
|
|
$self->{relay}->fatal("write buf $self->{dir} not empty before starting SSL: '$self->{wbuf}'"); |
|
603
|
|
|
|
|
|
|
|
|
604
|
0
|
0
|
|
|
|
|
my $callback = @_%2 ? pop(@_):undef; |
|
605
|
0
|
|
|
|
|
|
my %sslargs = @_; |
|
606
|
|
|
|
|
|
|
IO::Socket::SSL->start_SSL( $self->{fd}, |
|
607
|
0
|
0
|
|
|
|
|
%sslargs, |
|
608
|
|
|
|
|
|
|
SSL_startHandshake => 0, |
|
609
|
|
|
|
|
|
|
) or die "failed to upgrade socket to SSL"; |
|
610
|
|
|
|
|
|
|
my $sub = $sslargs{SSL_server} |
|
611
|
0
|
0
|
|
|
|
|
? \&IO::Socket::SSL::accept_SSL |
|
612
|
|
|
|
|
|
|
: \&IO::Socket::SSL::connect_SSL; |
|
613
|
0
|
|
|
|
|
|
_ssl($self,$sub,$callback,\%sslargs); |
|
614
|
|
|
|
|
|
|
} |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
sub _ssl { |
|
617
|
0
|
|
|
0
|
|
|
my ($self,$sub,$cb,$sslargs) = @_; |
|
618
|
0
|
0
|
|
|
|
|
if ( $sub->($self->{fd}) ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
619
|
0
|
|
|
|
|
|
$self->xdebug("ssl handshake success"); |
|
620
|
0
|
0
|
|
|
|
|
$cb->($self) if $cb; |
|
621
|
|
|
|
|
|
|
} elsif ( $!{EAGAIN} ) { |
|
622
|
|
|
|
|
|
|
# retry |
|
623
|
|
|
|
|
|
|
my $dir = |
|
624
|
|
|
|
|
|
|
$SSL_ERROR == SSL_WANT_READ ? 'r' : |
|
625
|
|
|
|
|
|
|
$SSL_ERROR == SSL_WANT_WRITE ? 'w' : |
|
626
|
0
|
0
|
|
|
|
|
return $self->{relay}->fatal( "unhandled $SSL_ERROR on EAGAIN" ); |
|
|
|
0
|
|
|
|
|
|
|
627
|
0
|
|
|
0
|
|
|
$self->mask( $dir => sub { _ssl($self,$sub,$cb,$sslargs) }); |
|
|
0
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
} elsif ( $sslargs->{SSL_server} ) { |
|
629
|
0
|
|
|
|
|
|
return $self->{relay}->fatal( "error on accept_SSL: $SSL_ERROR|$!" ); |
|
630
|
|
|
|
|
|
|
} else { |
|
631
|
|
|
|
|
|
|
return $self->{relay}->fatal( |
|
632
|
0
|
|
|
|
|
|
"error on connect_SSL to $sslargs->{SSL_verifycn_name}: $SSL_ERROR|$!" ); |
|
633
|
|
|
|
|
|
|
} |
|
634
|
|
|
|
|
|
|
} |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
############################################################################ |
|
638
|
|
|
|
|
|
|
# DNS cache |
|
639
|
|
|
|
|
|
|
############################################################################ |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
package App::HTTP_Proxy_IMP::Relay::DNS; |
|
642
|
1
|
|
|
1
|
|
2285
|
use AnyEvent::DNS; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
28
|
|
|
643
|
1
|
|
|
1
|
|
5
|
use Socket qw(AF_INET AF_INET6 inet_pton); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
327
|
|
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
my %cache; |
|
646
|
|
|
|
|
|
|
sub uncache { |
|
647
|
0
|
|
|
0
|
|
|
my ($host,$addr) = @_; |
|
648
|
0
|
0
|
|
|
|
|
my $e = $cache{lc($host)} or return; |
|
649
|
0
|
|
|
|
|
|
@$e = grep { $_ ne $addr } @$e; |
|
|
0
|
|
|
|
|
|
|
|
650
|
0
|
0
|
|
|
|
|
delete $cache{lc($host)} if !@$e; |
|
651
|
|
|
|
|
|
|
} |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub lookup { |
|
654
|
0
|
|
|
0
|
|
|
my ($host,$cb) = @_; |
|
655
|
0
|
|
|
|
|
|
$host = lc($host); |
|
656
|
|
|
|
|
|
|
|
|
657
|
0
|
0
|
0
|
|
|
|
if ( my $e = $cache{$host} ) { |
|
|
|
0
|
|
|
|
|
|
|
658
|
0
|
|
|
|
|
|
return $cb->(@$e); |
|
659
|
|
|
|
|
|
|
} elsif ( inet_pton(AF_INET,$host) || inet_pton(AF_INET6,$host) ) { |
|
660
|
0
|
|
|
|
|
|
return $cb->($host); |
|
661
|
|
|
|
|
|
|
} |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
AnyEvent::DNS::a($host,sub { |
|
664
|
0
|
0
|
|
0
|
|
|
if ( @_ ) { |
|
665
|
0
|
|
|
|
|
|
$cache{$host} = [ @_ ]; |
|
666
|
0
|
|
|
|
|
|
return $cb->(@_); |
|
667
|
|
|
|
|
|
|
} |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
# try AAAA |
|
670
|
|
|
|
|
|
|
AnyEvent::DNS::aaaa($host,sub { |
|
671
|
0
|
0
|
|
|
|
|
$cache{$host} = [ @_ ] if @_; |
|
672
|
0
|
|
|
|
|
|
return $cb->(@_); |
|
673
|
0
|
|
|
|
|
|
}); |
|
674
|
0
|
|
|
|
|
|
}); |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
1; |