line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl -w |
2
|
|
|
|
|
|
|
package Net::BitTorrent::DHT; |
3
|
|
|
|
|
|
|
{ |
4
|
11
|
|
|
11
|
|
202641
|
use strict; |
|
11
|
|
|
|
|
25
|
|
|
11
|
|
|
|
|
453
|
|
5
|
11
|
|
|
11
|
|
65
|
use warnings; |
|
11
|
|
|
|
|
1327
|
|
|
11
|
|
|
|
|
445
|
|
6
|
11
|
|
|
11
|
|
1160
|
use Digest::SHA qw[sha1_hex]; |
|
11
|
|
|
|
|
4778
|
|
|
11
|
|
|
|
|
795
|
|
7
|
11
|
|
|
11
|
|
76
|
use Scalar::Util qw[blessed weaken refaddr]; |
|
11
|
|
|
|
|
25
|
|
|
11
|
|
|
|
|
734
|
|
8
|
11
|
|
|
11
|
|
70
|
use Carp qw[carp]; |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
545
|
|
9
|
11
|
|
|
11
|
|
1252
|
use Socket qw[/inet_/ /pack_sockaddr_in/]; |
|
11
|
|
|
|
|
4646
|
|
|
11
|
|
|
|
|
5606
|
|
10
|
11
|
|
|
11
|
|
82
|
use lib q[../../../lib/]; |
|
11
|
|
|
|
|
26
|
|
|
11
|
|
|
|
|
100
|
|
11
|
11
|
|
|
11
|
|
2750
|
use Net::BitTorrent::Util qw[:bencode :compact]; |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
1612
|
|
12
|
11
|
|
|
11
|
|
839
|
use Net::BitTorrent::Protocol qw[:dht]; |
|
11
|
|
|
|
|
20
|
|
|
11
|
|
|
|
|
1784
|
|
13
|
11
|
|
|
11
|
|
835
|
use Net::BitTorrent::Version; |
|
11
|
|
|
|
|
24
|
|
|
11
|
|
|
|
|
316
|
|
14
|
11
|
|
|
11
|
|
67
|
use version qw[qv]; |
|
11
|
|
|
|
|
21
|
|
|
11
|
|
|
|
|
96
|
|
15
|
|
|
|
|
|
|
our $VERSION_BASE = 50; our $UNSTABLE_RELEASE = 0; our $VERSION = sprintf(($UNSTABLE_RELEASE ? q[%.3f_%03d] : q[%.3f]), (version->new(($VERSION_BASE))->numify / 1000), $UNSTABLE_RELEASE); |
16
|
|
|
|
|
|
|
my @CONTENTS |
17
|
|
|
|
|
|
|
= \my (%_client, %tid, %node_id, %outstanding_p, %nodes, %tracking); |
18
|
|
|
|
|
|
|
my %REGISTRY; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
sub new { |
21
|
47
|
|
|
47
|
1
|
151
|
my ($class, $args) = @_; |
22
|
47
|
|
|
|
|
103
|
my $self = undef; |
23
|
47
|
50
|
33
|
|
|
773
|
if (!$args or (ref($args) ne q[HASH])) { |
24
|
0
|
|
|
|
|
0
|
carp __PACKAGE__ |
25
|
|
|
|
|
|
|
. q[->new( { ... } ) requires parameters passed as a hashref]; |
26
|
0
|
|
|
|
|
0
|
return; |
27
|
|
|
|
|
|
|
} |
28
|
47
|
50
|
33
|
|
|
1246
|
if ( !$args->{q[Client]} |
|
|
|
33
|
|
|
|
|
29
|
|
|
|
|
|
|
or !blessed($args->{q[Client]}) |
30
|
|
|
|
|
|
|
or !$args->{q[Client]}->isa(q[Net::BitTorrent])) |
31
|
0
|
|
|
|
|
0
|
{ carp __PACKAGE__ |
32
|
|
|
|
|
|
|
. q[->new( { ... } ) requires parameters passed as a hashref]; |
33
|
0
|
|
|
|
|
0
|
return; |
34
|
|
|
|
|
|
|
} |
35
|
47
|
|
|
|
|
651
|
my $node_id = Net::BitTorrent::Version->gen_node_id(); |
36
|
47
|
|
|
|
|
406
|
$self = bless \$node_id, $class; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# Defaults |
39
|
47
|
|
|
|
|
364
|
$_client{refaddr $self} = $args->{q[Client]}; |
40
|
47
|
|
|
|
|
278
|
weaken $_client{refaddr $self}; |
41
|
47
|
|
|
|
|
258
|
$node_id{refaddr $self} = $node_id; |
42
|
47
|
|
|
|
|
215
|
$nodes{refaddr $self} = {}; |
43
|
47
|
|
|
|
|
220
|
$tid{refaddr $self} = q[aaaaa]; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Boot |
46
|
|
|
|
|
|
|
$_client{refaddr $self}->_schedule( |
47
|
22
|
|
|
22
|
|
200
|
{Code => sub { shift->_pulse() }, |
48
|
47
|
|
|
|
|
1435
|
Time => time + 3, |
49
|
|
|
|
|
|
|
Object => $self |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
$_client{refaddr $self}->_schedule( # boot up |
53
|
|
|
|
|
|
|
{Code => sub { |
54
|
22
|
|
|
22
|
|
68
|
my ($s) = @_; |
55
|
22
|
|
|
|
|
50
|
for my $node (values %{$nodes{refaddr $s}}) { |
|
22
|
|
|
|
|
210
|
|
56
|
8
|
|
|
|
|
67
|
$self->_ping_out($node); |
57
|
8
|
|
|
|
|
64
|
$self->_find_node_out($node, $node_id{refaddr $s}); |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
}, |
60
|
47
|
|
|
|
|
665
|
Time => time + 2, |
61
|
|
|
|
|
|
|
Object => $self |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
); |
64
|
47
|
|
|
|
|
287
|
weaken($REGISTRY{refaddr $self} = $self); |
65
|
47
|
|
|
|
|
249
|
return $self; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# Accessors | Private |
69
|
|
|
|
|
|
|
sub _client { |
70
|
0
|
0
|
|
0
|
|
0
|
return if defined $_[1]; |
71
|
0
|
|
|
|
|
0
|
return $_client{refaddr + $_[0]}; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _peers { |
75
|
133
|
|
|
133
|
|
281
|
my ($self, $info_hash) = @_; |
76
|
133
|
100
|
|
|
|
13012
|
return q[] if !$tracking{refaddr $self}{$info_hash}; |
77
|
18
|
|
|
|
|
122
|
$tracking{refaddr $self}{$info_hash}{q[touch]} = time; |
78
|
18
|
|
|
|
|
150
|
return $tracking{refaddr $self}{$info_hash}{q[peers]}; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# Accesors | Public |
82
|
|
|
|
|
|
|
sub node_id { |
83
|
3
|
50
|
|
3
|
1
|
14
|
return if defined $_[1]; |
84
|
3
|
|
|
|
|
64
|
return $node_id{refaddr + $_[0]}; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub nodes { |
88
|
2
|
50
|
|
2
|
1
|
13
|
return if defined $_[1]; |
89
|
1
|
|
|
|
|
33
|
return [map { {ip => $_->{q[ip]}, port => $_->{q[port]}} } |
|
2
|
|
|
|
|
25
|
|
90
|
2
|
|
|
|
|
4
|
values %{$nodes{refaddr + $_[0]}}]; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Setters | Private |
94
|
|
|
|
|
|
|
sub _set_node_id { |
95
|
0
|
0
|
|
0
|
|
0
|
return if not defined $_[1]; |
96
|
0
|
|
|
|
|
0
|
return $node_id{refaddr + $_[0]} = $_[1]; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Methods | Public |
100
|
|
|
|
|
|
|
sub add_node { |
101
|
358
|
|
|
358
|
1
|
745
|
my ($self, $args) = @_; |
102
|
358
|
50
|
|
|
|
1786
|
return if !$_client{refaddr $self}->_use_dht; |
103
|
358
|
50
|
|
|
|
558
|
return if scalar keys %{$nodes{refaddr $self}} >= 300; # max 300 nodes |
|
358
|
|
|
|
|
1660
|
|
104
|
358
|
50
|
|
|
|
1258
|
return if ref $args ne q[HASH]; |
105
|
358
|
50
|
|
|
|
919
|
return if !$args->{q[port]}; |
106
|
358
|
50
|
|
|
|
810
|
return if !$args->{q[ip]}; |
107
|
358
|
|
|
|
|
3606
|
my $ok = $_client{refaddr $self}->_event(q[ip_filter], |
108
|
|
|
|
|
|
|
{Address => sprintf q[%s:%d], $args->{q[ip]}, $args->{q[port]}}); |
109
|
358
|
50
|
33
|
|
|
2867
|
if (defined $ok and $ok == 0) { return; } |
|
0
|
|
|
|
|
0
|
|
110
|
358
|
|
|
|
|
2363
|
my $_resolved = inet_aton($args->{q[ip]}); |
111
|
358
|
50
|
|
|
|
814
|
return if !$_resolved; |
112
|
358
|
|
|
|
|
1254
|
my $paddr = pack_sockaddr_in($args->{q[port]}, $_resolved); |
113
|
358
|
100
|
|
|
|
2920
|
$nodes{refaddr $self}{$paddr} = { |
114
|
|
|
|
|
|
|
birth => time, |
115
|
|
|
|
|
|
|
fail => 0, |
116
|
|
|
|
|
|
|
id => undef, |
117
|
|
|
|
|
|
|
ip => $args->{q[ip]}, |
118
|
|
|
|
|
|
|
okay => 0, |
119
|
|
|
|
|
|
|
paddr => $paddr, |
120
|
|
|
|
|
|
|
ping => time - 61, # lies |
121
|
|
|
|
|
|
|
port => $args->{q[port]}, |
122
|
|
|
|
|
|
|
prev_find => 0, |
123
|
|
|
|
|
|
|
prev_get => 0, |
124
|
|
|
|
|
|
|
prev_ann => 0, |
125
|
|
|
|
|
|
|
seen => time - 60, # lies |
126
|
|
|
|
|
|
|
token_i => undef, |
127
|
|
|
|
|
|
|
token_o => undef |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
if !$nodes{refaddr $self}{$paddr}; |
130
|
358
|
|
|
|
|
1399
|
return $nodes{refaddr $self}{$paddr}; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub _pulse { |
134
|
37
|
|
|
37
|
|
119
|
my ($self) = @_; |
135
|
37
|
100
|
|
|
|
368
|
return if !$_client{refaddr $self}->_use_dht; |
136
|
31
|
|
|
|
|
124
|
for my $tid ( |
|
8
|
|
|
|
|
73
|
|
137
|
|
|
|
|
|
|
grep { |
138
|
31
|
|
|
|
|
306
|
$outstanding_p{refaddr $self}{$_}{q[sent]} < time - 20 |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
keys %{$outstanding_p{refaddr $self}} |
141
|
|
|
|
|
|
|
) |
142
|
|
|
|
|
|
|
{ # old packets |
143
|
0
|
|
|
|
|
0
|
$nodes{refaddr $self} |
144
|
|
|
|
|
|
|
{$outstanding_p{refaddr $self}{$tid}{q[paddr]}}{q[fail]}++; |
145
|
0
|
|
|
|
|
0
|
delete $outstanding_p{refaddr $self}{$tid}; |
146
|
|
|
|
|
|
|
} |
147
|
31
|
50
|
33
|
|
|
84
|
for my $paddr ( |
|
58
|
|
|
|
|
810
|
|
148
|
|
|
|
|
|
|
grep { |
149
|
31
|
|
|
|
|
247
|
(!defined $nodes{refaddr $self}{$_} |
150
|
|
|
|
|
|
|
{q[seen]}) # XXX - mystery bug |
151
|
|
|
|
|
|
|
or |
152
|
|
|
|
|
|
|
(($nodes{refaddr $self}{$_}{q[seen]} < time - (60 * 15))) |
153
|
|
|
|
|
|
|
or ($nodes{refaddr $self}{$_}{q[fail]} > 10) |
154
|
|
|
|
|
|
|
} keys %{$nodes{refaddr $self}} |
155
|
|
|
|
|
|
|
) |
156
|
|
|
|
|
|
|
{ # old/bad nodes |
157
|
0
|
|
|
|
|
0
|
delete $nodes{refaddr $self}{$paddr}; |
158
|
|
|
|
|
|
|
} |
159
|
31
|
50
|
|
|
|
92
|
for my $paddr ( |
|
58
|
|
|
|
|
370
|
|
160
|
|
|
|
|
|
|
grep { |
161
|
31
|
|
|
|
|
177
|
(($nodes{refaddr $self}{$_}{q[ping]} |
162
|
|
|
|
|
|
|
> $nodes{refaddr $self}{$_}{q[seen]} |
163
|
|
|
|
|
|
|
) |
164
|
|
|
|
|
|
|
and |
165
|
|
|
|
|
|
|
($nodes{refaddr $self}{$_}{q[ping]} < time - (60 * 8)) |
166
|
|
|
|
|
|
|
) |
167
|
|
|
|
|
|
|
} keys %{$nodes{refaddr $self}} |
168
|
|
|
|
|
|
|
) |
169
|
|
|
|
|
|
|
{ # old/bad nodes |
170
|
0
|
|
|
|
|
0
|
$self->_ping_out($nodes{refaddr $self}{$paddr}); |
171
|
|
|
|
|
|
|
} |
172
|
31
|
|
|
|
|
74
|
for my $info_hash (keys %{$tracking{refaddr $self}}) |
|
31
|
|
|
|
|
190
|
|
173
|
|
|
|
|
|
|
{ # stale tracker data |
174
|
11
|
50
|
|
|
|
89
|
delete $tracking{refaddr $self}{$info_hash} |
175
|
|
|
|
|
|
|
if $tracking{refaddr $self}{$info_hash}{q[touch]} |
176
|
|
|
|
|
|
|
< time - (60 * 30); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# TODO: remove bad nodes, etc. |
180
|
|
|
|
|
|
|
$_client{refaddr $self}->_schedule( |
181
|
15
|
|
|
15
|
|
116
|
{Code => sub { shift->_pulse() }, |
182
|
31
|
|
|
|
|
530
|
Time => time + 45, |
183
|
|
|
|
|
|
|
Object => $self |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub _on_data { |
189
|
198
|
|
|
198
|
|
2016
|
my ($self, $paddr, $data) = @_; |
190
|
198
|
50
|
|
|
|
1671
|
return if !$_client{refaddr $self}->_use_dht; |
191
|
198
|
|
|
|
|
1094
|
my ($packet, $leftover) = bdecode($data); |
192
|
198
|
|
|
|
|
512
|
my $node; |
193
|
198
|
50
|
33
|
|
|
2155
|
if ( (defined $packet) |
|
|
|
33
|
|
|
|
|
194
|
|
|
|
|
|
|
and (ref $packet eq q[HASH]) |
195
|
|
|
|
|
|
|
and $packet->{q[y]}) |
196
|
198
|
100
|
|
|
|
953
|
{ if ($packet->{q[y]} eq q[q]) { # query |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
197
|
99
|
50
|
|
|
|
694
|
if ($packet->{q[q]} eq q[ping]) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
198
|
0
|
|
|
|
|
0
|
$self->_ping_reply($paddr, $packet->{q[t]}); |
199
|
0
|
0
|
|
|
|
0
|
if (q[XXX - I don't want this in the final version. ...do I?] |
200
|
|
|
|
|
|
|
and !$nodes{refaddr $self}{$paddr}) |
201
|
0
|
|
|
|
|
0
|
{ my ($_port, $_ip) = unpack_sockaddr_in($paddr); |
202
|
0
|
|
|
|
|
0
|
$_ip = inet_ntoa($_ip); |
203
|
0
|
|
|
|
|
0
|
my $ok = $_client{refaddr $self}->_event(q[ip_filter], |
204
|
|
|
|
|
|
|
{Address => sprintf q[%s:%d], $_ip, $_port}); |
205
|
0
|
0
|
0
|
|
|
0
|
if (defined $ok and $ok == 0) { return; } |
|
0
|
|
|
|
|
0
|
|
206
|
0
|
|
|
|
|
0
|
my $new_node |
207
|
|
|
|
|
|
|
= $self->add_node({ip => $_ip, port => $_port}); |
208
|
0
|
0
|
|
|
|
0
|
return if !$new_node; |
209
|
|
|
|
|
|
|
} |
210
|
0
|
0
|
|
|
|
0
|
if (defined $nodes{refaddr $self}{$paddr}) { |
211
|
0
|
|
0
|
|
|
0
|
$nodes{refaddr $self}{$paddr}{q[id]} |
212
|
|
|
|
|
|
|
||= $packet->{q[a]}{q[id]}; |
213
|
0
|
|
|
|
|
0
|
$nodes{refaddr $self}{$paddr}{q[ping]} = time; |
214
|
0
|
|
|
|
|
0
|
$nodes{refaddr $self}{$paddr}{q[seen]} = time; |
215
|
0
|
|
|
|
|
0
|
$self->_find_node_out($nodes{refaddr $self}{$paddr}, |
216
|
|
|
|
|
|
|
$node_id{refaddr $self}); |
217
|
|
|
|
|
|
|
} |
218
|
0
|
|
|
|
|
0
|
return; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
elsif ($packet->{q[q]} eq q[find_node]) { |
221
|
47
|
|
|
|
|
306
|
my ($_port, $_ip) = unpack_sockaddr_in($paddr); |
222
|
47
|
|
|
|
|
4696
|
$_ip = inet_ntoa($_ip); |
223
|
47
|
|
|
|
|
670
|
my $ok = $_client{refaddr $self}->_event(q[ip_filter], |
224
|
|
|
|
|
|
|
{Address => sprintf q[%s:%d], $_ip, $_port}); |
225
|
47
|
50
|
33
|
|
|
248
|
if (defined $ok and $ok == 0) { return; } |
|
0
|
|
|
|
|
0
|
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# if (!$nodes{refaddr $self}{$paddr}) { |
228
|
|
|
|
|
|
|
# my ($port, $host) = unpack_sockaddr_in($paddr); |
229
|
|
|
|
|
|
|
# $self->add_node({ip=>inet_ntoa($host), port=>$port}) || return; |
230
|
|
|
|
|
|
|
#} |
231
|
|
|
|
|
|
|
#$node = $nodes{refaddr $self}{$paddr}; |
232
|
|
|
|
|
|
|
#$nodes{refaddr $self}{$paddr}{q[id]}||= |
233
|
|
|
|
|
|
|
# $packet->{q[a]}{q[id]}; |
234
|
217
|
|
|
|
|
1217
|
my $nodes = compact( |
235
|
217
|
|
|
|
|
1031
|
map { sprintf q[%s:%d], $_->{q[ip]}, $_->{q[port]} } |
236
|
47
|
|
|
|
|
302
|
grep { $_->{q[ip]} =~ m[^[\d\.]+$] } |
237
|
|
|
|
|
|
|
$self->_locate_nodes_near_target( |
238
|
|
|
|
|
|
|
$packet->{q[a]}{q[target]} |
239
|
|
|
|
|
|
|
) |
240
|
|
|
|
|
|
|
); |
241
|
47
|
|
|
|
|
527
|
$self->_find_node_reply($paddr, $packet->{q[t]}, |
242
|
|
|
|
|
|
|
$packet->{q[a]}{q[id]}, $nodes) |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
#if $nodes; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
elsif ($packet->{q[q]} eq q[get_peers]) { |
247
|
34
|
100
|
|
|
|
272
|
if (!$nodes{refaddr $self}{$paddr}) { |
248
|
23
|
|
|
|
|
175
|
my ($port, $host) = unpack_sockaddr_in($paddr); |
249
|
23
|
|
|
|
|
354
|
$self->add_node( |
250
|
|
|
|
|
|
|
{ip => inet_ntoa($host), port => $port}); |
251
|
23
|
50
|
|
|
|
155
|
return if !$nodes{refaddr $self}{$paddr}; |
252
|
|
|
|
|
|
|
} |
253
|
34
|
|
|
|
|
213
|
$node = $nodes{refaddr $self}{$paddr}; |
254
|
34
|
|
66
|
|
|
201
|
$node->{q[id]} ||= $packet->{q[a]}{q[id]}; |
255
|
34
|
|
|
|
|
63
|
$node->{q[seen]} = time; |
256
|
34
|
|
|
|
|
83
|
$node->{q[okay]}++; |
257
|
34
|
|
|
|
|
67
|
$node->{q[fail]} = 0; |
258
|
34
|
|
|
|
|
138
|
$node->{q[token_o]} = q[NB_] . $self->_generate_token; |
259
|
34
|
100
|
|
|
|
227
|
if ($tracking{refaddr $self} |
260
|
|
|
|
|
|
|
{$packet->{q[a]}{q[info_hash]}}) |
261
|
8
|
|
|
|
|
59
|
{ my @values = uncompact($tracking{refaddr $self} |
262
|
|
|
|
|
|
|
{$packet->{q[a]}{q[info_hash]}}{q[peers]}); |
263
|
18
|
|
|
|
|
47
|
@values = map { compact($_) } |
|
64
|
|
|
|
|
79
|
|
264
|
8
|
|
|
|
|
38
|
grep {$_} @values[0 .. 7]; # max 8 |
265
|
8
|
|
|
|
|
63
|
my $outgoing_packet |
266
|
|
|
|
|
|
|
= _build_dht_reply_values( |
267
|
|
|
|
|
|
|
$packet->{q[t]}, $packet->{q[a]}{q[id]}, |
268
|
|
|
|
|
|
|
\@values, $node->{q[token_o]}); |
269
|
8
|
|
|
|
|
91
|
send($_client{refaddr $self}->_udp(), |
270
|
|
|
|
|
|
|
$outgoing_packet, 0, $paddr); |
271
|
8
|
|
|
|
|
190
|
$tracking{refaddr $self} |
272
|
|
|
|
|
|
|
{$packet->{q[a]}{q[info_hash]}}{q[touch]} = time; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
else { |
275
|
106
|
|
|
|
|
566
|
my $nodes = compact( |
276
|
|
|
|
|
|
|
map { |
277
|
106
|
|
|
|
|
929
|
sprintf q[%s:%d], $_->{q[ip]}, $_->{q[port]} |
278
|
26
|
|
|
|
|
169
|
} grep { $_->{q[ip]} =~ m[^[\d\.]+$] } |
279
|
|
|
|
|
|
|
$self->_locate_nodes_near_target( |
280
|
|
|
|
|
|
|
$packet->{q[a]}{q[info_hash]} |
281
|
|
|
|
|
|
|
) |
282
|
|
|
|
|
|
|
); |
283
|
26
|
|
|
|
|
221
|
send($_client{refaddr $self}->_udp(), |
284
|
|
|
|
|
|
|
_build_dht_reply_get_peers( |
285
|
|
|
|
|
|
|
$packet->{q[t]}, $packet->{q[a]}{q[id]}, |
286
|
|
|
|
|
|
|
$nodes, $node->{q[token_o]} |
287
|
|
|
|
|
|
|
), |
288
|
|
|
|
|
|
|
0, $paddr |
289
|
|
|
|
|
|
|
); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
elsif ($packet->{q[q]} eq q[announce_peer]) { |
293
|
18
|
50
|
|
|
|
147
|
if (!$nodes{refaddr $self}{$paddr}) { |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# XXX - reply with an error msg |
296
|
|
|
|
|
|
|
#die q[...we don't know this node]; |
297
|
0
|
|
|
|
|
0
|
return; |
298
|
|
|
|
|
|
|
} |
299
|
18
|
|
|
|
|
83
|
$node = $nodes{refaddr $self}{$paddr}; |
300
|
18
|
|
33
|
|
|
80
|
$node->{q[id]} ||= $packet->{q[a]}{q[id]}; |
301
|
18
|
|
|
|
|
53
|
$node->{q[seen]} = time; |
302
|
18
|
|
|
|
|
42
|
$node->{q[okay]}++; |
303
|
18
|
|
|
|
|
36
|
$node->{q[fail]} = 0; |
304
|
18
|
50
|
33
|
|
|
268
|
if ( (!$node->{q[token_o]}) |
|
|
50
|
66
|
|
|
|
|
305
|
|
|
|
|
|
|
|| ($packet->{q[a]}{q[token]} ne $node->{q[token_o]})) |
306
|
6
|
|
|
|
|
40
|
{ # XXX - reply with token error msg |
307
|
|
|
|
|
|
|
#die pp $node; |
308
|
0
|
|
|
|
|
0
|
return; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
elsif ((!$tracking{refaddr $self} |
311
|
|
|
|
|
|
|
{$packet->{q[a]}{q[info_hash]}} |
312
|
|
|
|
|
|
|
) |
313
|
|
|
|
|
|
|
and (scalar(keys %{$tracking{refaddr $self}}) > 64) |
314
|
|
|
|
|
|
|
) |
315
|
|
|
|
|
|
|
{ # enough torrents |
316
|
|
|
|
|
|
|
# XXX - reply with error msg? |
317
|
|
|
|
|
|
|
# |
318
|
0
|
|
|
|
|
0
|
return; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
else { |
321
|
18
|
|
|
|
|
259
|
my @current_peers = uncompact($tracking{refaddr $self} |
322
|
|
|
|
|
|
|
{$packet->{q[a]}{q[info_hash]}}{q[peers]}); |
323
|
18
|
50
|
|
|
|
75
|
if (scalar(@current_peers) > 128) |
324
|
|
|
|
|
|
|
{ # enough peers for this torrent |
325
|
|
|
|
|
|
|
# XXX - reply with error msg? |
326
|
|
|
|
|
|
|
# |
327
|
0
|
|
|
|
|
0
|
return; |
328
|
|
|
|
|
|
|
} |
329
|
18
|
|
|
|
|
165
|
$tracking{refaddr $self} |
330
|
|
|
|
|
|
|
{$packet->{q[a]}{q[info_hash]}}{q[peers]} |
331
|
|
|
|
|
|
|
= compact(@current_peers, |
332
|
|
|
|
|
|
|
sprintf(q[%s:%d], |
333
|
|
|
|
|
|
|
$node->{q[ip]}, |
334
|
|
|
|
|
|
|
$packet->{q[a]}{q[port]}) |
335
|
|
|
|
|
|
|
); |
336
|
18
|
|
|
|
|
110
|
$self->_ping_reply($paddr, $packet->{q[t]}); |
337
|
18
|
|
|
|
|
388
|
$tracking{refaddr $self} |
338
|
|
|
|
|
|
|
{$packet->{q[a]}{q[info_hash]}}{q[touch]} = time; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
#warn q[Now on hand: ] |
341
|
|
|
|
|
|
|
# . pp uncompact($tracking{refaddr $self} |
342
|
|
|
|
|
|
|
# {$packet->{q[a]}{q[info_hash]}}); |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
else { |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
#die pp $packet; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
elsif ($packet->{q[y]} eq q[r]) { # reply |
351
|
99
|
|
|
|
|
1001
|
my $original_packet |
352
|
|
|
|
|
|
|
= $outstanding_p{refaddr $self}{$packet->{q[t]}}; |
353
|
99
|
50
|
|
|
|
304
|
if (!$original_packet) { |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
#warn q[...unexpected reply: ] . pp $packet; |
356
|
|
|
|
|
|
|
#warn pp $outstanding_p{refaddr $self}{$packet->{q[t]}}; |
357
|
|
|
|
|
|
|
# |
358
|
0
|
|
|
|
|
0
|
return; |
359
|
|
|
|
|
|
|
} |
360
|
99
|
50
|
|
|
|
651
|
return if !$nodes{refaddr $self}{$paddr}; |
361
|
99
|
|
|
|
|
346
|
$node = $nodes{refaddr $self}{$paddr}; |
362
|
99
|
50
|
|
|
|
459
|
if ($original_packet->{q[paddr]} ne $paddr) { |
363
|
0
|
|
|
|
|
0
|
my ($fake_port, $fake_host) = unpack_sockaddr_in($paddr); |
364
|
0
|
|
|
|
|
0
|
$fake_host = inet_ntoa($fake_host); |
365
|
0
|
|
|
|
|
0
|
my ($real_port, $real_host) |
366
|
|
|
|
|
|
|
= unpack_sockaddr_in($original_packet->{q[paddr]}); |
367
|
0
|
|
|
|
|
0
|
$real_host = inet_ntoa($real_host); |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
#warn sprintf |
370
|
|
|
|
|
|
|
# qq[...wrong remote node sent this reply %s to %s |\n %s:%d|%s\n vs\n %s:%d|%s], |
371
|
|
|
|
|
|
|
# pp($packet), |
372
|
|
|
|
|
|
|
# pp($original_packet), |
373
|
|
|
|
|
|
|
# $fake_host, $fake_port, pp($paddr), |
374
|
|
|
|
|
|
|
# $real_host, $real_port, |
375
|
|
|
|
|
|
|
# pp($original_packet->{q[paddr]}); |
376
|
0
|
|
|
|
|
0
|
return; |
377
|
|
|
|
|
|
|
} |
378
|
99
|
|
|
|
|
593
|
delete $outstanding_p{refaddr $self}{$packet->{q[t]}}; |
379
|
99
|
|
|
|
|
297
|
$node->{q[seen]} = time; |
380
|
99
|
|
|
|
|
264
|
$node->{q[okay]}++; |
381
|
99
|
|
|
|
|
268
|
$node->{q[fail]} = 0; |
382
|
99
|
100
|
|
|
|
394
|
$node->{q[id]} |
383
|
|
|
|
|
|
|
= $node->{q[id]} |
384
|
|
|
|
|
|
|
? $node->{q[id]} |
385
|
|
|
|
|
|
|
: $packet->{q[r]}{q[id]}; |
386
|
99
|
100
|
|
|
|
406
|
$node->{q[token_i]} |
387
|
|
|
|
|
|
|
= $packet->{q[r]}{q[token]} |
388
|
|
|
|
|
|
|
? $packet->{q[r]}{q[token]} |
389
|
|
|
|
|
|
|
: $node->{q[token_i]}; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
#warn sprintf q[%s:%d sent us %s in reply to %s], |
392
|
|
|
|
|
|
|
# $node->{q[ip]}, $node->{q[port]}, |
393
|
|
|
|
|
|
|
# pp(bdecode($data)), pp($original_packet); |
394
|
99
|
100
|
|
|
|
329
|
if ($packet->{q[r]}{q[nodes]}) { |
395
|
69
|
|
|
|
|
423
|
for my $_node (uncompact($packet->{q[r]}{q[nodes]})) { |
396
|
323
|
|
|
|
|
1230
|
my ($ip, $port) = split q[:], $_node, 2; |
397
|
323
|
|
|
|
|
1874
|
my $new_node |
398
|
|
|
|
|
|
|
= $self->add_node({ip => $ip, port => $port}); |
399
|
323
|
50
|
|
|
|
1083
|
next if !$new_node; |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
#$self->_ping_out($new_node); |
402
|
|
|
|
|
|
|
# |
403
|
|
|
|
|
|
|
#warn pp $original_packet; |
404
|
323
|
|
|
|
|
1059
|
my $_data = bdecode($original_packet->{q[packet]}); |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
#warn pp $_data; |
407
|
323
|
100
|
|
|
|
1343
|
my $info_hash |
408
|
|
|
|
|
|
|
= $_data->{q[a]}{q[target]} |
409
|
|
|
|
|
|
|
? $_data->{q[a]}{q[target]} |
410
|
|
|
|
|
|
|
: $_data->{q[a]}{q[info_hash]}; |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# warn pp $info_hash; |
413
|
323
|
|
|
|
|
1202
|
$self->_get_peers_out($new_node, $info_hash); |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
} |
416
|
99
|
100
|
|
|
|
1677
|
if ($packet->{q[r]}{q[values]}) { |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
#warn pp $original_packet; |
419
|
8
|
|
|
|
|
62
|
my $torrent = |
420
|
|
|
|
|
|
|
$_client{refaddr $self}->_locate_torrent( |
421
|
|
|
|
|
|
|
unpack q[H40], |
422
|
|
|
|
|
|
|
bdecode($original_packet->{q[packet]}) |
423
|
|
|
|
|
|
|
->{q[a]}{q[info_hash]} |
424
|
|
|
|
|
|
|
); |
425
|
8
|
50
|
|
|
|
54
|
if ($torrent) { |
426
|
18
|
|
|
|
|
72
|
$tracking{refaddr $self}{$torrent->infohash} = { |
427
|
|
|
|
|
|
|
peers => |
428
|
|
|
|
|
|
|
compact( |
429
|
|
|
|
|
|
|
uncompact( |
430
|
|
|
|
|
|
|
$tracking{refaddr $self} |
431
|
|
|
|
|
|
|
{$torrent->infohash}{q[peers]} |
432
|
|
|
|
|
|
|
), |
433
|
8
|
|
|
|
|
32
|
(map { uncompact($_) } |
434
|
8
|
|
|
|
|
208
|
@{$packet->{q[r]}{q[values]}} |
435
|
|
|
|
|
|
|
) |
436
|
|
|
|
|
|
|
), |
437
|
|
|
|
|
|
|
touch => time |
438
|
|
|
|
|
|
|
}; |
439
|
|
|
|
|
|
|
} |
440
|
8
|
|
|
|
|
77
|
$self->_find_node_out($node, $node_id{refaddr $self}); |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
elsif ($packet->{q[y]} eq q[e]) { # error |
444
|
|
|
|
|
|
|
#if ( $packet->{q[e]}->[0] == ) { } |
445
|
|
|
|
|
|
|
# XXX - Should DHT have events? |
446
|
|
|
|
|
|
|
#use Data::Dump qw[pp]; |
447
|
|
|
|
|
|
|
#warn sprintf qq[Error: %s\from %s\nnoriginal packet: %s], |
448
|
|
|
|
|
|
|
# pp($packet), pp($nodes{refaddr $self}{$paddr}), |
449
|
|
|
|
|
|
|
# pp(scalar bdecode( |
450
|
|
|
|
|
|
|
# $outstanding_p{refaddr $self}{$packet->{q[t]}} |
451
|
|
|
|
|
|
|
# {q[packet]} |
452
|
|
|
|
|
|
|
# ) |
453
|
|
|
|
|
|
|
# ); |
454
|
0
|
|
|
|
|
0
|
delete $outstanding_p{refaddr $self}{$packet->{q[t]}}; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
else { #warn q[...what just happend? ] . pp bdecode($data) |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
else { |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
# AZ or garbage. ...as if the two were different. |
462
|
|
|
|
|
|
|
#use Data::Dump qw[pp]; |
463
|
|
|
|
|
|
|
#warn q[Bad packet: ] . pp($data); |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
sub _ping_out { |
468
|
8
|
|
|
8
|
|
20
|
my ($self, $node) = @_; |
469
|
8
|
50
|
|
|
|
67
|
return if !$_client{refaddr $self}->_use_dht; |
470
|
8
|
50
|
|
|
|
56
|
return if $node->{q[seen]} > time - 120; |
471
|
0
|
|
|
|
|
0
|
my $tid = $self->_generate_token; |
472
|
0
|
|
|
|
|
0
|
my $packet = _build_dht_query_ping($tid, $node_id{refaddr $self}); |
473
|
0
|
|
|
|
|
0
|
$outstanding_p{refaddr $self}{$tid} = {attempts => 1, |
474
|
|
|
|
|
|
|
sent => time, |
475
|
|
|
|
|
|
|
packet => $packet, |
476
|
|
|
|
|
|
|
paddr => $node->{q[paddr]} |
477
|
|
|
|
|
|
|
}; |
478
|
|
|
|
|
|
|
return |
479
|
0
|
|
|
|
|
0
|
send($_client{refaddr $self}->_udp(), |
480
|
|
|
|
|
|
|
$packet, 0, $node->{q[paddr]}); |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
sub _ping_reply { |
484
|
18
|
|
|
18
|
|
47
|
my ($self, $paddr, $tid) = @_; |
485
|
18
|
50
|
|
|
|
120
|
return if !$_client{refaddr $self}->_use_dht; |
486
|
|
|
|
|
|
|
return |
487
|
18
|
|
|
|
|
126
|
send($_client{refaddr $self}->_udp(), |
488
|
|
|
|
|
|
|
_build_dht_reply_ping($tid, $node_id{refaddr $self}), |
489
|
|
|
|
|
|
|
0, $paddr); |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
sub _announce_peer_out { |
493
|
26
|
|
|
26
|
|
48
|
my ($self, $node, $infohash) = @_; |
494
|
26
|
50
|
|
|
|
132
|
return if !$_client{refaddr $self}->_use_dht; |
495
|
26
|
50
|
|
|
|
85
|
return if $node->{q[prev_ann]} > time - (60 * 15); |
496
|
26
|
|
|
|
|
56
|
my $tid = $self->_generate_token; |
497
|
26
|
100
|
|
|
|
91
|
return if !$node->{q[token_i]}; |
498
|
18
|
|
|
|
|
118
|
my $packet = |
499
|
|
|
|
|
|
|
_build_dht_query_announce($tid, |
500
|
|
|
|
|
|
|
$node_id{refaddr $self}, |
501
|
|
|
|
|
|
|
$infohash, |
502
|
|
|
|
|
|
|
$node->{q[token_i]}, |
503
|
|
|
|
|
|
|
$_client{refaddr $self}->_tcp_port |
504
|
|
|
|
|
|
|
); |
505
|
18
|
|
|
|
|
206
|
$outstanding_p{refaddr $self}{$tid} = {attempts => 1, |
506
|
|
|
|
|
|
|
sent => time, |
507
|
|
|
|
|
|
|
packet => $packet, |
508
|
|
|
|
|
|
|
paddr => $node->{q[paddr]} |
509
|
|
|
|
|
|
|
}; |
510
|
18
|
|
|
|
|
36
|
$node->{q[prev_find]} = 0; |
511
|
18
|
|
|
|
|
30
|
$node->{q[prev_ann]} = time; |
512
|
|
|
|
|
|
|
return |
513
|
18
|
|
|
|
|
88
|
send($_client{refaddr $self}->_udp(), |
514
|
|
|
|
|
|
|
$packet, 0, $node->{q[paddr]}); |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
sub _find_node_out { |
518
|
68
|
|
|
68
|
|
151
|
my ($self, $node, $target) = @_; |
519
|
68
|
50
|
|
|
|
344
|
return if !$_client{refaddr $self}->_use_dht; |
520
|
68
|
100
|
|
|
|
412
|
return if $node->{q[prev_find]} > time - (60 * 5); |
521
|
47
|
|
|
|
|
157
|
my $tid = $self->_generate_token; |
522
|
47
|
|
|
|
|
317
|
my $packet = _build_dht_query_find_node($tid, $node_id{refaddr $self}, |
523
|
|
|
|
|
|
|
$target); |
524
|
47
|
|
|
|
|
583
|
$outstanding_p{refaddr $self}{$tid} = {attempts => 1, |
525
|
|
|
|
|
|
|
sent => time, |
526
|
|
|
|
|
|
|
packet => $packet, |
527
|
|
|
|
|
|
|
paddr => $node->{q[paddr]} |
528
|
|
|
|
|
|
|
}; |
529
|
47
|
|
|
|
|
99
|
$node->{q[prev_find]} = time; |
530
|
|
|
|
|
|
|
return |
531
|
47
|
|
|
|
|
306
|
send($_client{refaddr $self}->_udp(), |
532
|
|
|
|
|
|
|
$packet, 0, $node->{q[paddr]}); |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
# Send find_node result to peer |
536
|
|
|
|
|
|
|
sub _find_node_reply { |
537
|
47
|
|
|
47
|
|
159
|
my ($self, $paddr, $tid, $id, $nodes) = @_; |
538
|
47
|
50
|
|
|
|
389
|
return if !$_client{refaddr $self}->_use_dht; |
539
|
|
|
|
|
|
|
return |
540
|
47
|
|
|
|
|
350
|
send($_client{refaddr $self}->_udp(), |
541
|
|
|
|
|
|
|
_build_dht_reply_find_node($tid, $id, $nodes), |
542
|
|
|
|
|
|
|
0, $paddr); |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
sub _get_peers_out { |
546
|
349
|
|
|
349
|
|
645
|
my ($self, $node, $info_hash) = @_; |
547
|
349
|
50
|
|
|
|
2048
|
return if !$_client{refaddr $self}->_use_dht; |
548
|
349
|
100
|
|
|
|
2822
|
return if $node->{q[prev_get]} > time - (60 * 10); |
549
|
34
|
|
|
|
|
112
|
my $tid = $self->_generate_token; |
550
|
34
|
|
|
|
|
598
|
my $packet = _build_dht_query_get_peers($tid, $node_id{refaddr $self}, |
551
|
|
|
|
|
|
|
$info_hash); |
552
|
34
|
|
|
|
|
465
|
$outstanding_p{refaddr $self}{$tid} = {attempts => 1, |
553
|
|
|
|
|
|
|
sent => time, |
554
|
|
|
|
|
|
|
packet => $packet, |
555
|
|
|
|
|
|
|
paddr => $node->{q[paddr]} |
556
|
|
|
|
|
|
|
}; |
557
|
34
|
|
|
|
|
76
|
$node->{q[prev_get]} = time; |
558
|
|
|
|
|
|
|
return |
559
|
34
|
|
|
|
|
227
|
send($_client{refaddr $self}->_udp(), |
560
|
|
|
|
|
|
|
$packet, 0, $node->{q[paddr]}); |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# Methods | Private | Fake routing table |
564
|
|
|
|
|
|
|
sub _locate_nodes_near_target { |
565
|
110
|
|
|
110
|
|
450
|
my ($self, $target) = @_; |
566
|
110
|
50
|
|
|
|
575
|
return if !$_client{refaddr $self}->_use_dht; |
567
|
110
|
|
|
|
|
1034
|
my $_target = hex unpack q[H4], $target; |
568
|
110
|
|
|
|
|
220
|
my @nodes; |
569
|
110
|
|
|
|
|
231
|
for my $node ( |
|
532
|
|
|
|
|
2634
|
|
570
|
|
|
|
|
|
|
sort { |
571
|
393
|
|
|
|
|
1502
|
hex(unpack q[H4], $a->{q[id]}) ^ $_target cmp |
572
|
|
|
|
|
|
|
hex(unpack q[H4], $b->{q[id]}) ^ $_target |
573
|
|
|
|
|
|
|
} |
574
|
110
|
|
|
|
|
755
|
grep { $_->{q[id]} } values %{$nodes{refaddr $self}} |
575
|
|
|
|
|
|
|
) |
576
|
375
|
|
|
|
|
542
|
{ push @nodes, $node; |
577
|
375
|
50
|
|
|
|
1245
|
last if scalar @nodes == 8; |
578
|
|
|
|
|
|
|
} |
579
|
110
|
|
|
|
|
569
|
return @nodes; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# Methods | Private | Peer search |
583
|
|
|
|
|
|
|
sub _scrape { |
584
|
25
|
|
|
25
|
|
63
|
my ($self, $torrent) = @_; |
585
|
25
|
50
|
|
|
|
162
|
return if !$_client{refaddr $self}->_use_dht; |
586
|
25
|
50
|
33
|
|
|
453
|
if ( (!$torrent) |
|
|
|
33
|
|
|
|
|
587
|
|
|
|
|
|
|
|| (!blessed $torrent) |
588
|
|
|
|
|
|
|
|| (!$torrent->isa(q[Net::BitTorrent::Torrent]))) |
589
|
0
|
|
|
|
|
0
|
{ carp |
590
|
|
|
|
|
|
|
q[Net::BitTorrent::DHT::Node->_scrape() requires a Net::BitTorrent::Torrent]; |
591
|
0
|
|
|
|
|
0
|
return; |
592
|
|
|
|
|
|
|
} |
593
|
25
|
50
|
|
|
|
211
|
if ($torrent->private) { |
594
|
0
|
|
|
|
|
0
|
carp q[Private torrents disallow DHT]; |
595
|
0
|
|
|
|
|
0
|
return; |
596
|
|
|
|
|
|
|
} |
597
|
25
|
|
|
|
|
258
|
my $info_hash = pack q[H40], $torrent->infohash; |
598
|
25
|
|
|
|
|
132
|
for my $node ($self->_locate_nodes_near_target($info_hash)) { |
599
|
26
|
|
|
|
|
91
|
$self->_find_node_out($node, $info_hash); |
600
|
26
|
|
|
|
|
93
|
$self->_get_peers_out($node, $info_hash); |
601
|
|
|
|
|
|
|
} |
602
|
25
|
|
|
|
|
100
|
return 1; |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub _announce { |
606
|
12
|
|
|
12
|
|
33
|
my ($self, $torrent) = @_; |
607
|
12
|
50
|
|
|
|
83
|
return if !$_client{refaddr $self}->_use_dht; |
608
|
12
|
50
|
33
|
|
|
285
|
if ( (!$torrent) |
|
|
|
33
|
|
|
|
|
609
|
|
|
|
|
|
|
|| (!blessed $torrent) |
610
|
|
|
|
|
|
|
|| (!$torrent->isa(q[Net::BitTorrent::Torrent]))) |
611
|
0
|
|
|
|
|
0
|
{ carp |
612
|
|
|
|
|
|
|
q[Net::BitTorrent::DHT::Node->_scrape() requires a Net::BitTorrent::Torrent]; |
613
|
0
|
|
|
|
|
0
|
return; |
614
|
|
|
|
|
|
|
} |
615
|
12
|
50
|
|
|
|
188
|
if ($torrent->private) { |
616
|
0
|
|
|
|
|
0
|
carp q[Private torrents disallow DHT]; |
617
|
0
|
|
|
|
|
0
|
return; |
618
|
|
|
|
|
|
|
} |
619
|
12
|
|
|
|
|
61
|
my $info_hash = pack q[H40], $torrent->infohash; |
620
|
12
|
|
|
|
|
62
|
for my $node ($self->_locate_nodes_near_target($info_hash)) { |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
#if !$node->{q[token_i]}; |
623
|
26
|
|
|
|
|
160
|
$self->_find_node_out($node, $info_hash); |
624
|
26
|
|
|
|
|
89
|
$self->_announce_peer_out($node, $info_hash); |
625
|
|
|
|
|
|
|
} |
626
|
12
|
|
|
|
|
226
|
return 1; |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
sub _generate_token { |
630
|
141
|
|
|
141
|
|
257
|
my ($self) = @_; |
631
|
141
|
50
|
|
|
|
929
|
return if !$_client{refaddr $self}->_use_dht; |
632
|
141
|
|
|
|
|
740
|
return ++$tid{refaddr $self}; |
633
|
|
|
|
|
|
|
} |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
sub as_string { |
636
|
0
|
|
|
0
|
1
|
0
|
my ($self, $advanced) = @_; |
637
|
0
|
0
|
|
|
|
0
|
my $dump = !$advanced ? $node_id{refaddr $self} : sprintf <<'END', |
638
|
|
|
|
|
|
|
Net::BitTorrent::DHT |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
Node ID: %s |
641
|
|
|
|
|
|
|
END |
642
|
|
|
|
|
|
|
$node_id{refaddr $self}; |
643
|
0
|
0
|
|
|
|
0
|
return defined wantarray ? $dump : print STDERR qq[$dump\n]; |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
sub CLONE { |
647
|
0
|
|
|
0
|
|
0
|
for my $_oID (keys %REGISTRY) { |
648
|
0
|
|
|
|
|
0
|
my $_obj = $REGISTRY{$_oID}; |
649
|
0
|
|
|
|
|
0
|
my $_nID = refaddr $_obj; |
650
|
0
|
|
|
|
|
0
|
for (@CONTENTS) { |
651
|
0
|
|
|
|
|
0
|
$_->{$_nID} = $_->{$_oID}; |
652
|
0
|
|
|
|
|
0
|
delete $_->{$_oID}; |
653
|
|
|
|
|
|
|
} |
654
|
0
|
|
|
|
|
0
|
weaken $_client{$_nID}; |
655
|
0
|
|
|
|
|
0
|
delete $outstanding_p{$_nID}; |
656
|
0
|
|
|
|
|
0
|
weaken($REGISTRY{$_nID} = $_obj); |
657
|
0
|
|
|
|
|
0
|
delete $REGISTRY{$_oID}; |
658
|
|
|
|
|
|
|
} |
659
|
0
|
|
|
|
|
0
|
return 1; |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
DESTROY { |
662
|
6
|
|
|
6
|
|
10
|
my ($self) = @_; |
663
|
6
|
|
|
|
|
11
|
for (@CONTENTS) { delete $_->{refaddr $self}; } |
|
36
|
|
|
|
|
88
|
|
664
|
6
|
|
|
|
|
34
|
return delete $REGISTRY{refaddr $self}; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
1; |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=pod |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=head1 NAME |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
Net::BitTorrent::DHT - Kademlia based Distributed Hash Table |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=head1 Constructor |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=over |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
=item C |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
Creates a C object. This constructor should not be |
682
|
|
|
|
|
|
|
used directly. |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=back |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
=head1 Methods |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
=over |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=item C |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
Adds a single node to the routing table. Expects a hashref with the |
693
|
|
|
|
|
|
|
following keys: |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=over |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=item C |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
The hostname/IP address of the remote node. |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
=item C |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
The port the remote node has open for DHT. |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=back |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
This is an advanced method and should not (normally) should not be used. |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
=item C |
710
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
Get the Node ID used to identify this L in the |
712
|
|
|
|
|
|
|
DHT swarm. |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=item C |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
Returns a list of nodes from the routing table in a format suitable for |
717
|
|
|
|
|
|
|
handing off to L one by one. |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=item C |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
Returns a 'ready to print' dump of the object's data structure. If |
722
|
|
|
|
|
|
|
called in void context, the structure is printed to C. |
723
|
|
|
|
|
|
|
C is a boolean value. |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=back |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=head1 Bugs |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
In this alpha, there are a number of places where I break away from the |
730
|
|
|
|
|
|
|
specification. These will all be fixed in a future version. |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=over |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=item * |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
The routing table is flat. |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=back |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=head1 Notes |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
While bandwidth to/from DHT nodes will probably never be limited like |
743
|
|
|
|
|
|
|
other traffic, in the future, it will be taken into account and "drained" |
744
|
|
|
|
|
|
|
from the rate limiter. If there's a burst of DHT traffic, the peer |
745
|
|
|
|
|
|
|
traffic may be limited to avoid the total to exceed the global limit. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
=head1 See Also |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
I have used a number of references for implementation second opinions: |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
=over |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
=item The Kademlia Paper |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=item BEP 5: DHT |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
http://www.bittorrent.org/beps/bep_0005.html |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
=item Notes about the BitTorrent DHT Protocol from GetRight |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
http://getright.com/torrentdev.html |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
=back |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
=head1 Author |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
Sanko Robinson - http://sankorobinson.com/ |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
CPAN ID: SANKO |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
=head1 License and Legal |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
Copyright (C) 2008-2009 by Sanko Robinson Esanko@cpan.orgE |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
780
|
|
|
|
|
|
|
it under the terms of The Artistic License 2.0. See the F |
781
|
|
|
|
|
|
|
file included with this distribution or |
782
|
|
|
|
|
|
|
http://www.perlfoundation.org/artistic_license_2_0. For |
783
|
|
|
|
|
|
|
clarification, see http://www.perlfoundation.org/artistic_2_0_notes. |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
When separated from the distribution, all POD documentation is covered |
786
|
|
|
|
|
|
|
by the Creative Commons Attribution-Share Alike 3.0 License. See |
787
|
|
|
|
|
|
|
http://creativecommons.org/licenses/by-sa/3.0/us/legalcode. For |
788
|
|
|
|
|
|
|
clarification, see http://creativecommons.org/licenses/by-sa/3.0/us/. |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
Neither this module nor the L is affiliated with |
791
|
|
|
|
|
|
|
BitTorrent, Inc. |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=for svn $Id: DHT.pm d3c97de 2009-09-12 04:31:46Z sanko@cpan.org $ |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=cut |