File Coverage

blib/lib/Net/BitTorrent/Peer.pm
Criterion Covered Total %
statement 726 1083 67.0
branch 238 512 46.4
condition 77 217 35.4
subroutine 92 105 87.6
pod 45 45 100.0
total 1178 1962 60.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             package Net::BitTorrent::Peer;
3             {
4 11     11   62 use strict;
  11         19  
  11         401  
5 11     11   59 use warnings;
  11         54  
  11         331  
6 11     11   54 use Carp qw[carp];
  11         20  
  11         542  
7 11     11   58 use Scalar::Util qw[blessed weaken refaddr];
  11         24  
  11         865  
8 11     11   57 use List::Util qw[sum max];
  11         22  
  11         663  
9 11     11   2388 use Socket qw[/F_INET/ SOMAXCONN SOCK_STREAM /inet_/ /pack_sockaddr_in/];
  11         9191  
  11         7215  
10 11     11   82 use Fcntl qw[F_SETFL O_NONBLOCK];
  11         20  
  11         538  
11 11     11   25553 use Math::BigInt;
  11         301057  
  11         87  
12 11     11   228971 use Digest::SHA qw[sha1];
  11         32  
  11         822  
13 11     11   70 use version qw[qv];
  11         25  
  11         120  
14             our $VERSION_BASE = 52; our $UNSTABLE_RELEASE = 0; our $VERSION = sprintf(($UNSTABLE_RELEASE ? q[%.3f_%03d] : q[%.3f]), (version->new(($VERSION_BASE))->numify / 1000), $UNSTABLE_RELEASE);
15 11     11   1508 use vars qw[@EXPORT_OK %EXPORT_TAGS];
  11         22  
  11         769  
16 11     11   69 use Exporter qw[];
  11         28  
  11         928  
17             *import = *import = *Exporter::import;
18             @EXPORT_OK = qw[
19             DISCONNECT_BY_REMOTE DISCONNECT_LOOPBACK
20             DISCONNECT_NO_SUCH_TORRENT DISCONNECT_HANDSHAKE_INFOHASH
21             DISCONNECT_MALFORMED_HANDSHAKE DISCONNECT_MALFORMED_PACKET
22             DISCONNECT_PREXISTING DISCONNECT_TOO_MANY
23             DISCONNECT_HASHCHECKING DISCONNECT_SEED
24             DISCONNECT_TIMEOUT_HANDSHAKE DISCONNECT_USELESS_PEER
25             DISCONNECT_HANDSHAKE_SYNC_DH5 ];
26             %EXPORT_TAGS = (
27             all => [@EXPORT_OK],
28             disconnect => [
29             qw[ DISCONNECT_BY_REMOTE DISCONNECT_LOOPBACK
30             DISCONNECT_NO_SUCH_TORRENT DISCONNECT_HANDSHAKE_INFOHASH
31             DISCONNECT_MALFORMED_HANDSHAKE DISCONNECT_MALFORMED_PACKET
32             DISCONNECT_PREXISTING DISCONNECT_TOO_MANY
33             DISCONNECT_HASHCHECKING DISCONNECT_SEED
34             DISCONNECT_TIMEOUT_HANDSHAKE DISCONNECT_USELESS_PEER
35             DISCONNECT_HANDSHAKE_SYNC_DH5 ]
36             ],
37             );
38 11     11   62 use lib q[../../../lib];
  11         32  
  11         114  
39 11     11   10703 use Net::BitTorrent::Protocol qw[:build parse_packet :types];
  11         40  
  11         4528  
40 11     11   129 use Net::BitTorrent::Util qw[:bencode];
  11         30  
  11         1080  
41 11     11   13526 use Net::BitTorrent::Version;
  11         38  
  11         261962  
42             my (@CONTENTS) = \my (
43             %_client, %_socket, %torrent,
44             %_data_out, %_data_in, %peerid,
45             %bitfield, %am_choking, %am_interested,
46             %peer_choking, %peer_interested, %incoming,
47             %requests_out, %requests_in, %_last_contact,
48             %_incoming_fastset, %reserved_bytes, %source,
49             %host, %port,
50             #################### Alpha code:
51             %_RC4_S, %_crypto_select, %_S, %_i, %_j, %_state,
52             %_Xa, %_Ya, %_Yb, %_Xb,
53             %_KeyA, %_KeyB, %_parse_packets_schedule
54             );
55             my %REGISTRY;
56             my %_Disconnect_Strings = (
57             DISCONNECT_BY_REMOTE() =>
58             q[Connection closed by remote peer], # or unknown
59             DISCONNECT_LOOPBACK() => q[...we've connected to ourself.],
60             DISCONNECT_NO_SUCH_TORRENT() => q[We aren't serving this torrent]
61             , # comes with { Infohash => [...] }
62             DISCONNECT_HANDSHAKE_INFOHASH() =>
63             q[Bad plaintext handshake (Incorrect Infohash)],
64             DISCONNECT_MALFORMED_HANDSHAKE() => q[Bad plaintext handshake],
65             DISCONNECT_MALFORMED_PACKET() => q[...bad packet.],
66             DISCONNECT_PREXISTING() => q[Already connected to this peer]
67             , # comes with { PeerID => [...] }
68             DISCONNECT_TOO_MANY() => q[Enough peers already!],
69             DISCONNECT_HASHCHECKING() => q[Hash checking],
70             DISCONNECT_SEED() => q[Disconnect seed],
71             -26 => q[Handed a piece we never asked for]
72             , # { Index => \d, Offset => \d, Length=> \d }
73             -28 => q[Sent a reject to a non-existant piece],
74             -29 => q[Rejected a request we never made.],
75             DISCONNECT_TIMEOUT_HANDSHAKE() =>
76             q[Failed to complete handshake within 30s],
77             -40 => q[Peer is idle],
78             DISCONNECT_USELESS_PEER() =>
79             q[Useless peer (Not interested and not interesting.)],
80             -101 => q[Bad VC in encrypted handshake],
81             DISCONNECT_HANDSHAKE_SYNC_DH5() => q[Failed to sync DH-5],
82             -103 => q[Bad encrypted header at stage 4],
83             -104 => q[Bad encrypted handshake (Bad SKEY)],
84             -105 => q[Unsupported encryption scheme]
85             );
86 11     11 1 58 sub DISCONNECT_BY_REMOTE {0}
87 11     11 1 38 sub DISCONNECT_LOOPBACK {-10}
88 11     11 1 92 sub DISCONNECT_NO_SUCH_TORRENT {-11}
89 11     11 1 144 sub DISCONNECT_HANDSHAKE_INFOHASH {-12}
90 11     11 1 40 sub DISCONNECT_MALFORMED_HANDSHAKE {-13}
91 11     11 1 40 sub DISCONNECT_MALFORMED_PACKET {-22}
92 11     11 1 71 sub DISCONNECT_PREXISTING {-16}
93 11     11 1 38 sub DISCONNECT_TOO_MANY {-17}
94 11     11 1 39 sub DISCONNECT_HASHCHECKING {-18}
95 14     14 1 56 sub DISCONNECT_SEED {-25}
96 29     29 1 277 sub DISCONNECT_TIMEOUT_HANDSHAKE {-30}
97 11     11 1 37 sub DISCONNECT_USELESS_PEER {-41}
98 42     42 1 468 sub DISCONNECT_HANDSHAKE_SYNC_DH5 {-102}
99              
100             # States
101 43     43 1 185 sub MSE_ONE {1}
102 78     78 1 368 sub MSE_TWO {2}
103 43     43 1 198 sub MSE_THREE {3}
104 43     43 1 287 sub MSE_FOUR {4}
105 43     43 1 186 sub MSE_FIVE {5}
106 76     76 1 420 sub REG_ONE {11}
107 68     68 1 323 sub REG_TWO {12}
108 62     62 1 336 sub REG_THREE {13}
109 1045     1045 1 4256 sub REG_OKAY {100}
110              
111             #
112 222     222 1 1041 sub CRYPTO_PLAIN {0x01}
113 854     854 1 3514 sub CRYPTO_RC4 {0x02}
114 0     0 1 0 sub CRYPTO_XOR {0x04} # unimplemented
115 0     0 1 0 sub CRYPTO_AES {0x08} # unimplemented
116              
117             sub DH_P {
118 128     128 1 17043 return Math::BigInt->new(
119             q[0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563]
120             );
121             }
122 64     64 1 695 sub DH_G {2}
123 148     148 1 1387 sub VC { qq[\0] x 8 }
124              
125             sub crypto_provide {
126 32     32 1 139 return pack q[N],
127             CRYPTO_PLAIN # | CRYPTO_RC4 #| CRYPTO_XOR | CRYPTO_AES;
128             }
129 92     92 1 766 sub len { pack(q[n], length(shift)) }
130              
131             sub new {
132              
133             # warn((caller(0))[3]);
134 135     135 1 357 my ($class, $args) = @_;
135 135         304 my $self = undef;
136 135 50       428 if (not defined $args) {
137 0         0 carp q[Net::BitTorrent::Peer->new({ }) requires ]
138             . q[parameters a hashref];
139 0         0 return;
140             }
141 135 50 66     690 if ( !$args->{q[Socket]}
142             and !$args->{q[Address]})
143 0         0 { carp <<'END'; return; }
  0         0  
144             Net::BitTorrent::Peer->new({}) requires either...
145             - an 'Address' (IPv4:port for new, outgoing connections)
146             or
147             - a 'Socket' (GLOB-type for newly accepted incoming connections)
148             END
149 135 100       481 if ($args->{q[Socket]}) {
150 67 50       8701 if (ref($args->{q[Socket]}) ne q[GLOB]) {
151 0         0 carp
152             q[Net::BitTorrent::Peer->new({ }) requires a GLOB-type socket];
153 0         0 return;
154             }
155 67 50 33     892 if ( (!$args->{q[Client]})
      33        
156             || (!blessed $args->{q[Client]})
157             || (!$args->{q[Client]}->isa(q[Net::BitTorrent])))
158 0         0 { carp
159             q[Net::BitTorrent::Peer->new({ }) requires a blessed Net::BitTorrent object in the 'Client' parameter];
160 0         0 return;
161             }
162 67         1399 my ($port, $packed_ip)
163             = unpack_sockaddr_in(getpeername($args->{q[Socket]}));
164 67         1228 my $ok = $args->{q[Client]}->_event(q[ip_filter],
165             {Address => sprintf(q[%s:%d], inet_ntoa($packed_ip), $port)});
166 67 50 66     4707 if (defined $ok and $ok == 0) { return; }
  0         0  
167 67         368 my $ip = inet_ntoa($packed_ip);
168 67 50       137 if (scalar(
169 540 50 66     3548 grep {
      66        
      33        
170 67         415 $_->{q[Object]}->isa(q[Net::BitTorrent::Peer])
171             && $_->{q[Object]}->host
172             && $_->{q[Object]}->host eq $ip
173             && $_->{q[Object]}->port
174             && $_->{q[Object]}->port eq $port
175             } values %{$args->{q[Client]}->_connections}
176             ) > $args->{q[Client]}->_connections_per_host
177             )
178 0         0 { shutdown($args->{q[Socket]}, 2);
179 0         0 close($args->{q[Socket]});
180 0         0 return;
181             }
182             $self
183 67         433 = bless \sprintf(q[%s:%d], $ip, $port),
184             $class;
185 67         410 $_socket{refaddr $self} = $args->{q[Socket]};
186 67         268 $_client{refaddr $self} = $args->{q[Client]};
187 67         285 weaken $_client{refaddr $self};
188 67 50       414 $_client{refaddr $self}->_add_connection($self, q[ro]) or return;
189 67         350 $_data_out{refaddr $self} = q[];
190 67         240 $_data_in{refaddr $self} = q[];
191 67         226 $incoming{refaddr $self} = 1;
192 67         299 $source{refaddr $self} = q[Incoming];
193 67 50       477 $_state{refaddr $self} = (
194             $_client{refaddr $self}->_encryption_mode
195             ? MSE_TWO
196             : REG_TWO
197             );
198             }
199             else {
200 68 50       999 if ($args->{q[Address]}
201             !~ m[^(?:(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.]?){4}):\d+$]
202             )
203 0         0 { carp
204             q[Net::BitTorrent::Peer->new({ }) requires an IPv4:port 'Address'];
205 0         0 return;
206             }
207 68 50 33     998 if ( (!$args->{q[Torrent]})
      33        
208             || (!blessed $args->{q[Torrent]})
209             || (!$args->{q[Torrent]}->isa(q[Net::BitTorrent::Torrent])))
210 0         0 { carp
211             q[Net::BitTorrent::Peer->new({ }) requires a blessed 'Torrent'];
212 0         0 return;
213             }
214 68 50       264 if (!$args->{q[Source]}) {
215 0         0 carp
216             q[Net::BitTorrent::Peer->new({ }) would like to know where this peer info is from];
217 0         0 return;
218             }
219 487 100       3371 my $half_open = grep {
220 68         442 $_->{q[Object]}->isa(q[Net::BitTorrent::Peer])
221             && !$_->{q[Object]}->torrent
222 68         130 } values %{$args->{q[Torrent]}->_client->_connections};
223 68 50       312 if ($half_open >= $args->{q[Torrent]}->_client->_half_open) {
224              
225             #warn sprintf q[%d half open sockets!], $half_open;
226 0         0 return;
227             }
228 68 50       344 if (scalar($args->{q[Torrent]}->peers)
229             >= $args->{q[Torrent]}->_client->_peers_per_torrent)
230 0         0 { return;
231             }
232 68         466 my ($_host, $_port) = split q[:], $args->{q[Address]}, 2;
233 68 100       134 if (scalar(
234 487 50 66     2855 grep {
      66        
      33        
235 68         353 $_->{q[Object]}->isa(q[Net::BitTorrent::Peer])
236             && $_->{q[Object]}->host
237             && $_->{q[Object]}->host eq $_host
238             && $_->{q[Object]}->port
239             && $_->{q[Object]}->port == $_port
240             } values %{$args->{q[Torrent]}->_client->_connections}
241             ) > $args->{q[Torrent]}->_client->_connections_per_host
242             )
243 1         4 { return;
244             }
245 67 50       17003 socket(my ($socket), PF_INET, SOCK_STREAM, getprotobyname(q[tcp]))
246             or return;
247 67         832 $self = bless \$args->{q[Address]}, $class;
248 67         776 ($host{refaddr $self}, $port{refaddr $self}) = ($_host, $_port);
249 67         466 $_socket{refaddr $self} = $socket;
250 67 50       1214 if (not($^O eq q[MSWin32] # set non blocking
    50          
251             ? ioctl($_socket{refaddr $self}, 0x8004667e,
252             pack(q[I], 1))
253             : fcntl($_socket{refaddr $self}, F_SETFL, O_NONBLOCK)
254             )
255             )
256 0         0 { return;
257             }
258 67         11610 connect($_socket{refaddr $self},
259             pack_sockaddr_in($port{refaddr $self},
260             inet_aton($host{refaddr $self})
261             )
262             );
263 67         622 $_client{refaddr $self} = $args->{q[Torrent]}->_client;
264 67         370 weaken $_client{refaddr $self};
265 67         279 $torrent{refaddr $self} = $args->{q[Torrent]};
266 67         263 weaken $torrent{refaddr $self};
267 67         397 ${$bitfield{refaddr $self}}
  67         382  
268             = pack(q[b*], qq[\0] x $torrent{refaddr $self}->piece_count);
269 67         449 my %_payload = (
270             Reserved => $_client{refaddr $self}->_build_reserved,
271             Infohash => pack(q[H40], $torrent{refaddr $self}->infohash),
272             PeerID => $_client{refaddr $self}->peerid
273             );
274              
275 67 100 66     451 if ( ($_client{refaddr $self}->_encryption_mode != 0x00)
276             && (!$args->{q[_plaintext]}) # XXX
277             )
278 32         86 { $_state{refaddr $self} = MSE_ONE;
279             }
280             else {
281 35         139 $_state{refaddr $self} = REG_ONE;
282             }
283 67         264 $_data_in{refaddr $self} = q[];
284 67 50       448 $_client{refaddr $self}->_add_connection($self, q[wo]) or return;
285 67         271 $incoming{refaddr $self} = 0;
286 67         390 $source{refaddr $self} = $args->{q[Source]};
287             }
288 134 50       635 if ($self) {
289 134         241 ${$am_choking{refaddr $self}} = 1;
  134         498  
290 134         215 ${$am_interested{refaddr $self}} = 0;
  134         530  
291 134         214 ${$peer_choking{refaddr $self}} = 1;
  134         1296  
292 134         220 ${$peer_interested{refaddr $self}} = 0;
  134         885  
293 134   66     254 ${$bitfield{refaddr $self}} ||= ();
  134         783  
294 134         487 $_last_contact{refaddr $self} = time; # lies
295 134         414 $_crypto_select{refaddr $self} = CRYPTO_PLAIN; # passthrough
296 134         776 $requests_out{refaddr $self} = [];
297 134         518 $requests_in{refaddr $self} = [];
298 134         1701 $_client{refaddr $self}->_schedule({Time => time + 120,
299             Code => \&_send_keepalive,
300             Object => $self
301             }
302             );
303 134         1226 $_client{refaddr $self}->_schedule(
304             {Time => time + 30,
305             Code => \&_cancel_old_requests,
306             Object => $self
307             }
308             );
309 134         1906 $_client{refaddr $self}->_schedule(
310             {Time => time + 90,
311             Code => \&_disconnect_useless_peer,
312             Object => $self
313             }
314             );
315             $_client{refaddr $self}->_schedule(
316             { Time => time + 30,
317             Code => sub {
318 44     44   113 my $s = shift;
319 44 100       282 if (!$peerid{refaddr $s}) {
320 18         60 weaken $s;
321 18         56 $s->_disconnect(DISCONNECT_TIMEOUT_HANDSHAKE);
322 18 100 66     255 if ((!$incoming{refaddr $self}
      100        
323             ) # outgoing connection
324             #&& ($_crypto_select{refaddr $self} != CRYPTO_PLAIN)
325             && ($torrent{refaddr $self})
326             && ($_state{refaddr $self} < REG_ONE)
327             )
328             {
329              
330             #warn q[RETRY! :D];
331 5         27 my $peer = # retry unencrypted
332             Net::BitTorrent::Peer->new(
333             { Address => (
334             sprintf q[%s:%d], $self->host,
335             $self->port
336             ),
337             Torrent => $torrent{refaddr $self},
338             Source => q[TODO],
339             _plaintext => 1 # XXX
340             }
341             );
342             }
343             }
344 44         177 return 1;
345             },
346 134         2099 Object => $self
347             }
348             );
349             $_client{refaddr $self}->_schedule(
350             { Time => time + 1,
351             Code => sub {
352 130     130   396 my $s = shift;
353 130         538 return $s->_parse_packets;
354             },
355 134         1530 Object => $self
356             }
357             );
358 134 50       668 if ($threads::shared::threads_shared) {
359 0 0       0 threads::shared::share($bitfield{refaddr $self})
360             if defined $bitfield{refaddr $self};
361 0         0 threads::shared::share($am_choking{refaddr $self});
362 0         0 threads::shared::share($am_interested{refaddr $self});
363 0         0 threads::shared::share($peer_choking{refaddr $self});
364 0         0 threads::shared::share($peer_interested{refaddr $self});
365             }
366 134         820 weaken($REGISTRY{refaddr $self} = $self);
367             }
368 134         658 return $self;
369             }
370              
371             # Accessors | Public | General
372 943     943 1 6042 sub peerid { return $peerid{refaddr +shift}; }
373              
374             # Accessors | Private | General
375 2453     2453   10901 sub _socket { return $_socket{refaddr +shift}; }
376 3954     3954 1 30401 sub torrent { return $torrent{refaddr +shift}; }
377 4     4 1 29 sub reserved_bytes { return $reserved_bytes{refaddr +shift}; }
378 22     22 1 43 sub bitfield { return ${$bitfield{refaddr +shift}}; }
  22         406  
379              
380             sub port {
381 2198 50   2198 1 4452 return if defined $_[1];
382 2198         2718 my ($self) = @_;
383 2198 100       19213 if (!$port{refaddr $self}) {
384 62 50       270 return if not defined $_socket{refaddr $self};
385 62         413 my $peername = getpeername($_socket{refaddr $self});
386 62 50       168 return if not defined $peername;
387 62         463 ($port{refaddr $self}, undef) = unpack_sockaddr_in($peername);
388             }
389 2198         16244 return $port{refaddr $self};
390             }
391              
392             sub host {
393 2198 50   2198 1 13361 return if defined $_[1];
394 2198         2748 my ($self) = @_;
395 2198 100       6961 if (!$host{refaddr $self}) {
396 62 50       388 return if not defined $_socket{refaddr $self};
397 62         616 my $peername = getpeername($_socket{refaddr $self});
398 62 50       186 return if not defined $peername;
399 62         267 my (undef, $packed_ip) = unpack_sockaddr_in($peername);
400 62         432 $host{refaddr $self} = inet_ntoa($packed_ip);
401             }
402 2198         16120 return $host{refaddr $self};
403             }
404              
405             # Accessors | Private | Status
406 4     4 1 10 sub peer_choking { return ${$peer_choking{refaddr +shift}}; }
  4         33  
407 176     176 1 168 sub am_choking { return ${$am_choking{refaddr +shift}}; }
  176         639  
408 4     4 1 10 sub peer_interested { return ${$peer_interested{refaddr +shift}}; }
  4         32  
409 4     4 1 11 sub am_interested { return ${$am_interested{refaddr +shift}}; }
  4         31  
410 4     4 1 32 sub incoming { return $incoming{refaddr +shift}; }
411 4     4 1 31 sub source { return $source{refaddr +shift}; }
412              
413             # Methods | Private
414             sub _rw {
415              
416             # warn((caller(0))[3]);
417 4657     4657   9703 my ($self, $read, $write, $error) = @_;
418              
419             #Carp::cluck sprintf q[%s->_rw(%d, %d, %d) | %d], __PACKAGE__, $read,
420             # $write, $error, $_state{refaddr $self};
421 4657 50 66     38092 if (defined $torrent{refaddr $self}
422             and !($torrent{refaddr $self}->status & 1))
423 0         0 { weaken $self;
424 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT);
425 0         0 return;
426             }
427 4657 50       11357 if ($error) {
428 0         0 weaken $self;
429 0         0 $self->_disconnect($^E);
430 0         0 return;
431             }
432 4657 50 66     34966 if (defined $torrent{refaddr $self}
433             and $torrent{refaddr $self}->status & 2)
434 0         0 { weaken $self;
435 0         0 $self->_disconnect(DISCONNECT_HASHCHECKING);
436 0         0 return;
437             }
438 4657         12287 my ($actual_read, $actual_write) = (0, 0);
439 4657 100       10192 if ($read) {
440 322 50 33     1607 if ( ($_crypto_select{refaddr $self} == CRYPTO_RC4)
441             && ($_state{refaddr $self} >= REG_ONE))
442 0         0 { $actual_read
443             = sysread($_socket{refaddr $self}, my ($data_in), $read);
444 0 0       0 $_data_in{refaddr $self} .=
445             $self->_RC4(( $incoming{refaddr $self}
446             ? $_KeyA{refaddr $self}
447             : $_KeyB{refaddr $self}
448             ),
449             $data_in
450             );
451              
452             #warn $data_in;
453             #use Data::Dump qw[pp];
454             #warn pp $_data_in{refaddr $self};
455             #warn $_data_in{refaddr $self};
456             }
457             else {
458              
459             #warn q[Reading plaintext data];
460 322         14125 $actual_read = sysread($_socket{refaddr $self},
461             $_data_in{refaddr $self},
462             $read,
463             length($_data_in{refaddr $self})
464             );
465             }
466 322 100       1125 if (!$actual_read) {
467 19         78 weaken $self;
468 19         112 $self->_disconnect($^E);
469 19         91 return;
470             }
471              
472             #warn sprintf q[Read %d bytes of data], $actual_read;
473 303         1181 $_last_contact{refaddr $self} = time;
474 303 100       1532 if (!$peerid{refaddr $self}) {
475 207         1542 $_client{refaddr $self}
476             ->_event(q[peer_connect], {Peer => $self});
477             }
478 303         3312 $_client{refaddr $self}->_event(q[peer_read],
479             {Peer => $self, Length => $actual_read});
480             }
481 4638 100 100     37009 if ($write && $_data_out{refaddr $self}) {
482 306         19108 $actual_write =
483             syswrite($_socket{refaddr $self},
484             $_data_out{refaddr $self},
485             $write, 0);
486              
487             #warn sprintf q[Wrote %d bytes of data], $actual_write;
488 306 50       970 if (not $actual_write) {
489 0         0 weaken $self;
490 0         0 $self->_disconnect($^E);
491 0         0 return;
492             }
493             else {
494 306         5016 $_client{refaddr $self}->_event(q[peer_write],
495             {Peer => $self, Length => $actual_write});
496 306         1653 substr($_data_out{refaddr $self}, 0, $actual_write, q[]);
497             }
498             }
499              
500             #$_client{refaddr $self}->_add_connection($self, q[rw]);
501 4638         16733 return ($actual_read, $actual_write);
502             }
503              
504             sub _syswrite { # applies any encryption
505 518     518   1183 my ($self, $data) = @_;
506 518 100       1653 return if !$data;
507              
508             #warn sprintf q[Sending %d bytes], length($data);
509 486 50 33     2905 if ( ($_crypto_select{refaddr $self} == CRYPTO_RC4)
510             && ($_state{refaddr $self} >= REG_ONE))
511 0 0       0 { $data = $self->_RC4(( $incoming{refaddr $self}
512             ? $_KeyB{refaddr $self}
513             : $_KeyA{refaddr $self}
514             ),
515             $data
516             );
517             }
518 486         4112 $_client{refaddr $self}->_add_connection($self, q[rw]);
519 486         3567 return length($_data_out{refaddr $self} .= $data);
520             }
521             my %_parse_packets_handshake_dispatch = (
522             &MSE_ONE => \&___handle_encrypted_handshake_one,
523             &MSE_TWO => \&___handle_encrypted_handshake_two,
524             &MSE_THREE => \&___handle_encrypted_handshake_three,
525             &MSE_FOUR => \&___handle_encrypted_handshake_four,
526             &MSE_FIVE => \&___handle_encrypted_handshake_five,
527             ®_ONE => \&___handle_plaintext_handshake_one,
528             ®_TWO => \&___handle_plaintext_handshake_two,
529             ®_THREE => \&___handle_plaintext_handshake_three
530             );
531             my %_parse_packets_dispatch = (
532             &KEEPALIVE => \&__handle_keepalive,
533             &CHOKE => \&__handle_choke,
534             &UNCHOKE => \&__handle_unchoke,
535             &INTERESTED => \&__handle_interested,
536             &NOT_INTERESTED => \&__handle_not_interested,
537             &HAVE => \&__handle_have,
538             &BITFIELD => \&__handle_bitfield,
539             &REQUEST => \&__handle_request,
540             &PIECE => \&__handle_piece,
541             &CANCEL => \&__handle_cancel,
542             &HAVE_ALL => \&__handle_have_all,
543             &HAVE_NONE => \&__handle_have_none,
544             &ALLOWED_FAST => \&__handle_allowed_fast,
545             &REJECT => \&__handle_reject,
546             &EXTPROTOCOL => \&__handle_ext_protocol
547             );
548              
549             sub _parse_packets {
550 942     942   1848 my ($self, $time) = @_;
551              
552             #warn((caller(0))[3] . q[ | ] . $_state{refaddr $self}) . q[ | ]
553             # . ($peerid{refaddr $self} || q[Unknown]);
554             #warn q[$_state{refaddr $self} == ] . $_state{refaddr $self};
555 942 100       4701 if ($_state{refaddr $self} != REG_OKAY) {
    100          
556 617 50       3582 if (defined
557             $_parse_packets_handshake_dispatch{$_state{refaddr $self}})
558 617         2736 { $_parse_packets_handshake_dispatch{$_state{refaddr $self}}(
559             $self);
560             }
561             else {
562 0         0 Carp::cluck q[Unknown state: ] . $_state{refaddr $self};
563             }
564             }
565             elsif (length $_data_in{refaddr $self}) {
566 109         435 PACKET: while ($_data_in{refaddr $self}) {
567 166         504 my $data_len = length $_data_in{refaddr $self};
568 166         865 my $packet = parse_packet(\$_data_in{refaddr $self});
569              
570             #use Data::Dump qw[pp];
571             #warn pp $packet;
572 166 50       442 if (!$packet) {
573 0 0       0 if (length($_data_in{refaddr $self}) != $data_len) {
574              
575             # N::B::Protocol removed some data but couldn't parse
576             # a packet from what was removed. Gotta be bad data.
577 0         0 weaken $self;
578 0         0 $self->_disconnect(DISCONNECT_MALFORMED_PACKET);
579 0         0 return;
580             }
581 0         0 last PACKET;
582             }
583 166 50       614 if (defined $_parse_packets_dispatch{$packet->{q[Type]}}) {
584 166         1000 $_parse_packets_dispatch{$packet->{q[Type]}}($self,
585             $packet->{q[Payload]});
586             }
587             else {
588 0         0 my $packet_dump = q[];
589 0 0       0 if (eval require Data::Dump) { # I like this better
590 0         0 $packet_dump = Data::Dump::pp($packet);
591             }
592             else { # fallback to lame core module
593 0         0 require Data::Dumper;
594 0         0 $packet_dump = Data::Dumper::Dumper($packet);
595             }
596 0         0 Carp::carp
597             sprintf <<'END', $self->as_string(1), $packet_dump;
598             ------------------------------------------------------------------------------
599             Unknown incoming packet. This may be a bug in Net::BitTorrent, so please c+p
600             the following block when you report this in the Net::BitTorrent Issue Tracker:
601             http://github.com/sanko/net-bittorrent/issues
602             ------------------------------------------------------------------------------
603             = Peer Information ===========================================================
604             %s
605             = Packet Information =========================================================
606             %s
607             ------------------------------------------------------------------------------
608             See the "Issue Tracker" section in 'perldoc Net::BitTorrent::Notes' for more
609             information. Thanks!
610             ------------------------------------------------------------------------------
611             END
612             }
613             }
614             }
615 942         5177 $_client{refaddr $self}->_add_connection($self, q[rw]);
616 942 50       13643 $_parse_packets_schedule{refaddr $self}
617             = $_client{refaddr $self}->_schedule({Time => time + 3,
618             Code => \&_parse_packets,
619             Object => $self
620             }
621             ) if !$time;
622 942         3639 return 1;
623             }
624              
625             sub ___handle_encrypted_handshake_one {
626              
627             # warn((caller(0))[3]);
628 32     32   70 my ($self) = @_;
629              
630             # Encryption is enabled
631             # Step 1A:
632             # - Generate Ya, PadA
633             # - Send Ya, PadA to B
634 32         208 $_Xa{refaddr $self} = int rand(9999999999999999);
635 32         134 $_Ya{refaddr $self}
636             = Math::BigInt->new(DH_G)->bmodpow($_Xa{refaddr $self}, DH_P);
637             my @bits
638 32         8552823 = map { chr hex $_ } ($_Ya{refaddr $self}->as_hex =~ m[(..)]g);
  3103         117527  
639 32         387 shift @bits;
640              
641             #warn sprintf q[Step 1A Complete: %s | %d bytes in cache],
642             #$self->as_string,
643 8717         13527 $self->_syswrite(join q[], @bits,
644 32         617 (map { chr rand(255) } 1 .. int(rand 512)));
645 32         967 $_client{refaddr $self}->_add_connection($self, q[rw]);
646 32         167 $_state{refaddr $self} = MSE_THREE;
647 32         299 return 1;
648             }
649              
650             sub ___handle_encrypted_handshake_two {
651              
652             # warn((caller(0))[3]);
653 124     124   242 my ($self) = @_;
654 124         1043 $_client{refaddr $self}->_add_connection($self, q[rw]);
655 124 100       1257 if ($_data_in{refaddr $self} =~ m[^\x13BitTorrent protocol.{48}$]s) {
656              
657             #warn q[Switching to plaintext handshake];
658 29         127 $_state{refaddr $self} = REG_TWO;
659 29         97 return;
660             }
661              
662             # Step 2B:
663             # - Read Ya from A
664             # - Generate Yb, PadB
665             # - Generate S
666             # - Send Yb, PadB to A
667 95 100       423 if (length($_data_in{refaddr $self}) < 96) {
668              
669             #warn sprintf
670             # q[Not enough data for Step 2B (req: 96, have: %d)],
671             # length($_data_in{refaddr $self});
672 63         502 $_client{refaddr $self}->_add_connection($self, q[rw]);
673 63         197 return 1;
674             }
675 3072         5978 $_Ya{refaddr $self} = Math::BigInt->new(
676             join q[], # Read Ya from A
677             q[0x],
678 32         865 map { sprintf q[%02x], ord $_ } split //,
679             substr($_data_in{refaddr $self}, 0, 96, q[])
680             );
681 32         104318 $_Xb{refaddr $self} = int rand(9999999999999999); # Random Xb
682 32         228 $_Yb{refaddr $self}
683             = Math::BigInt->new(DH_G)->bmodpow($_Xb{refaddr $self}, DH_P);
684             my @bits
685 32         8300915 = map { chr hex $_ }
  3100         9883340  
686             ($_Ya{refaddr $self}->bmodpow($_Xb{refaddr $self}, DH_P)->as_hex
687             =~ m[(..)]g);
688 32         469 shift @bits;
689 32         452 $_S{refaddr $self} = join q[], @bits;
690             my @_bits
691 32         220 = map { chr hex $_ } ($_Yb{refaddr $self}->as_hex =~ m[(..)]g);
  3103         117844  
692 32         714 shift @_bits;
693 6328         12011 $self->_syswrite(
694             join(q[], @_bits)
695 32         1527 . join(q[], map { chr int rand(255) } 1 .. (rand(1024) % 512))
696             );
697              
698             #warn sprintf q[Step 2B Complete: %s | %d bytes in cache],
699             # $self->as_string,
700 9172         15613 $self->_syswrite(
701             join(q[], @_bits)
702 32         1318 . join(q[], map { chr int rand(255) } 1 .. (rand(1024) % 512))
703             );
704 32         892 $_state{refaddr $self} = MSE_FOUR;
705 32         642 return 1;
706             }
707              
708             sub ___handle_encrypted_handshake_three {
709              
710             # warn((caller(0))[3]);
711 88     88   199 my ($self) = @_;
712              
713             # Step 3A:
714             # - Read Yb from B
715             # - Generate S
716             # - Generate SKEY
717             # - Send HASH('req1', S)
718             # - Send HASH('req2', SKEY) xor HASH('req3', S)
719             # - Generate PadC, IA, SKEY
720             # - Send ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA))
721             # - Send ENCRYPT(IA)
722 88 100       519 if (length($_data_in{refaddr $self}) < 96) {
723              
724             #warn sprintf
725             # q[Not enough data for Step 3A (req: 96, have: %d)],
726             # length($_data_in{refaddr $self});
727 56         346 $_client{refaddr $self}->_add_connection($self, q[rw]);
728 56         242 return 1;
729             }
730 3072         6898 $_Yb{refaddr $self} =
731             Math::BigInt->new(join q[],
732             q[0x],
733 32         928 map { sprintf q[%02x], ord $_ }
734             split //,
735             substr($_data_in{refaddr $self}, 0, 96, q[])
736             );
737             my @bits
738 32         131461 = map { chr hex $_ }
  3100         10507109  
739             ($_Yb{refaddr $self}->bmodpow($_Xa{refaddr $self}, DH_P)->as_hex
740             =~ m[(..)]g);
741 32         530 shift @bits;
742 32         450 $_S{refaddr $self} = join q[], @bits;
743 32 50       295 $torrent{refaddr $self} || return 0; # XXX - Local error. Disconnect?
744 32         479 $_KeyA{refaddr $self}
745             = sha1( q[keyA]
746             . $_S{refaddr $self}
747             . pack(q[H*], $torrent{refaddr $self}->infohash));
748              
749             # first piece: HASH('req1' . S)
750 32         378 $self->_syswrite(sha1(q[req1] . $_S{refaddr $self}));
751              
752             # second piece: HASH('req2', SKEY) xor HASH('req3', S)
753 32         318 $self->_syswrite(
754             sha1(q[req2] . pack(q[H*], $torrent{refaddr $self}->infohash))
755             ^ sha1(q[req3] . $_S{refaddr $self}));
756              
757             # third piece: ENCRYPT(VC, crypto_provide, len(PadC), PadC, len(IA))
758 32         108 my $PadC = q[];
759 32         64 my $IA = q[];
760 32         792 $self->_RC4($_KeyA{refaddr $self}, q[ ] x 1024, 1);
761 32         1755 $self->_syswrite(
762             $self->_RC4(
763             $_KeyA{refaddr $self},
764             VC # 64 | 8
765             . crypto_provide # 32 | 4
766             . len($PadC) # 16 | 2
767             . $PadC # '' | 0
768             . len($IA) # 16 | 2
769             )
770             );
771              
772             # fouth piece: ENCRYPT(IA)
773 32         220 $self->_syswrite($self->_RC4($_KeyA{refaddr $self}, $IA));
774              
775             #warn q[Step 3A Complete ] . $self->as_string;
776 32         301 $_state{refaddr $self} = MSE_FIVE;
777 32         494 return 1;
778             }
779              
780             sub ___handle_encrypted_handshake_four {
781              
782             #warn((caller(0))[3]);
783 82     82   191 my ($self) = @_;
784 82 100       505 if (length($_data_in{refaddr $self}) < 40) {
785              
786             #warn sprintf
787             # q[Not enough data for Step 4B (req: 40, have: %d)],
788             # length($_data_in{refaddr $self});
789 3         419 $_client{refaddr $self}->_add_connection($self, q[rw]);
790 3         19 return 1;
791             }
792              
793             # Step 4B:
794             # - Sync on sha1('req1', S)
795             # - Get !req2|req3
796             # - Locate torrent
797             # - Disconnect if we aren't serving this torrent
798             # - Get crypto_provide (requires decode)
799             # - Decide on an encryption scheme
800             # - Generate PadD
801             # - Send ENCRYPT(VC, crypto_select, len(padD), padD)
802             # - Send ENCRYPT2(Payload Stream)
803 79 100       1519 if (index($_data_in{refaddr $self}, sha1(q[req1], $_S{refaddr $self})
804             ) == -1
805             )
806 51         347 { $_client{refaddr $self}->_add_connection($self, q[rw]);
807 51         194 return;
808             }
809             substr( # Sync on sha1(q[req1], $Ali{q[S]})
810 28         384 $_data_in{refaddr $self},
811             0,
812             index($_data_in{refaddr $self}, sha1(q[req1], $_S{refaddr $self})
813             ),
814             q[]
815             );
816 28         151 my $req1 = substr($_data_in{refaddr $self}, 0, 20, q[]);
817 28         122 my $req2_req3 = substr($_data_in{refaddr $self}, 0, 20, q[]);
818 28         209 INFOHASH:
819 28         50 for my $torrent (values %{$_client{refaddr $self}->torrents}) {
820 28 50       225 if ((sha1(q[req2], pack q[H*], $torrent->infohash)
821             ^ sha1(q[req3], $_S{refaddr $self})
822             ) eq $req2_req3
823             )
824 28         124 { $torrent{refaddr $self} = $torrent;
825 28         159 weaken $torrent{refaddr $self};
826 28         196 ${$bitfield{refaddr $self}} = pack(q[b*],
  28         139  
827             qq[\0] x $torrent{refaddr $self}->piece_count);
828 28         110 last INFOHASH;
829             }
830             }
831 28 50       179 if (!$torrent{refaddr $self}) {
832 0         0 $self->_disconnect(-103);
833 0         0 return;
834             }
835 28 50       131 if (!$torrent{refaddr $self}) {
836 0         0 $self->_disconnect(-104);
837 0         0 return;
838             }
839 28         298 $_KeyB{refaddr $self}
840             = sha1( q[keyB]
841             . $_S{refaddr $self}
842             . pack(q[H*], $torrent{refaddr $self}->infohash));
843 28         398 $_KeyA{refaddr $self}
844             = sha1( q[keyA]
845             . $_S{refaddr $self}
846             . pack(q[H*], $torrent{refaddr $self}->infohash));
847 28         272 $self->_RC4($_KeyA{refaddr $self}, q[ ] x 1024, 1);
848 28         1905 my ($VC, $crypto_provide, $len_padC, $PadC, $len_IA)
849             = ($self->_RC4($_KeyA{refaddr $self},
850             substr($_data_in{refaddr $self}, 0, 16, q[]))
851             =~ m[^(.{8})(....)(..)()(..)$]
852             );
853 28 50 33     294 if (!($VC && $crypto_provide)) { # XXX - Hmmm...
854             #$self->_disconnect(DISCONNECT_HANDSHAKE_DH4);
855 0         0 return;
856             }
857              
858             # Prioritize encryption schemes: RC4, plaintext, XOR , ...
859             # XXX - Allow the user to set the order
860 28 50       217 if (unpack(q[N], $crypto_provide) & CRYPTO_RC4) {
    50          
861 0         0 $_crypto_select{refaddr $self} = CRYPTO_RC4;
862             }
863             elsif (unpack(q[N], $crypto_provide) & CRYPTO_PLAIN) {
864 28         92 $_crypto_select{refaddr $self} = CRYPTO_PLAIN;
865             }
866              
867             #elsif (unpack(q[N], $crypto_provide) & CRYPTO_XOR) {
868             # $_crypto_select{refaddr $self} = CRYPTO_XOR;
869             #}
870             #elsif (unpack(q[N], $crypto_provide) & CRYPTO_AES) {
871             # $_crypto_select{refaddr $self} = CRYPTO_AES;
872             #}
873             else {
874 0         0 weaken $self;
875 0         0 $self->_disconnect(-105);
876 0         0 return;
877             }
878 28         236 my $IA
879             = substr($_data_in{refaddr $self}, 0, unpack(q[n], $len_IA), q[]);
880 28 50       254 if ($IA) {
881 0 0       0 if ($_crypto_select{refaddr $self} = CRYPTO_RC4) {
882 0         0 $IA = $self->_RC4($_KeyA{refaddr $self}, $IA);
883             }
884 0         0 $_data_in{refaddr $self} = $IA;
885             }
886              
887             # reset for ENCRYPT2
888 28         297 $self->_RC4($_KeyB{refaddr $self}, q[ ] x 1024, 1);
889              
890             # Send ENCRYPT(VC, crypto_select, len(padD), padD)
891 28         1644 my $PadD = q[];
892 28         271 $self->_syswrite(
893             $self->_RC4($_KeyB{refaddr $self},
894             VC
895             . pack(q[N], $_crypto_select{refaddr $self})
896             . len($PadD)
897             . $PadD
898             )
899             );
900 28         228 $_client{refaddr $self}->_add_connection($self, q[rw]);
901 28         134 $_state{refaddr $self} = REG_TWO;
902 28         134 return 1;
903             }
904              
905             sub ___handle_encrypted_handshake_five {
906              
907             #warn((caller(0))[3]);
908 70     70   210 my ($self) = @_;
909 70 50       448 if (length($_data_in{refaddr $self}) < 34) {
910              
911             #warn sprintf q[Not enough data for enc five (req: 34, have: %d)],
912             # length($_data_in{refaddr $self});
913 0         0 $_client{refaddr $self}->_add_connection($self, q[rw]);
914 0         0 return;
915             }
916              
917             # Step 5A:
918             # - Synch on ENCRYPT(VC)
919             # - Find crypto_select
920             # -
921             # - Send ENCRYPT2(Payload Stream)
922 70         1203 $_KeyB{refaddr $self}
923             = sha1( q[keyB]
924             . $_S{refaddr $self}
925             . pack(q[H40], $torrent{refaddr $self}->infohash));
926 70         937 $self->_RC4($_KeyB{refaddr $self}, q[ ] x 1024, 1);
927 70         4526 my $index = index($_data_in{refaddr $self},
928             $self->_RC4($_KeyB{refaddr $self}, VC));
929 70 100       362 if ($index == -1) {
930 52 100       353 if (length($_data_in{refaddr $self}) >= 628) {
931 31         148 $self->_disconnect(DISCONNECT_HANDSHAKE_SYNC_DH5);
932 31 50       344 my $peer = # retry unencrypted
933             Net::BitTorrent::Peer->new(
934             { Address =>
935             (sprintf q[%s:%d], $self->host, $self->port),
936             Torrent => $torrent{refaddr $self},
937             Source => q[TODO],
938             _plaintext => 1 # XXX
939             }
940             ) if !$incoming{refaddr $self};
941             }
942             else {
943 21         334 $_client{refaddr $self}->_add_connection($self, q[rw]);
944             }
945 52         368 return;
946             }
947 18         124 substr($_data_in{refaddr $self}, 0, $index, q[]);
948 18         179 $self->_RC4($_KeyB{refaddr $self}, q[ ] x 1024, 1);
949 18         1268 my ($VC, $crypto_select, $len_PadD) = (
950             $self->_RC4( # Find crypto_select
951             $_KeyB{refaddr $self},
952             substr($_data_in{refaddr $self}, 0, 14, q[])
953             ) =~ m[^(.{8})(....)(..)$]
954             );
955 18 50 33     177 if (!$VC || $VC ne VC) {
956 0         0 weaken $self;
957 0         0 $self->_disconnect(-101);
958 0 0       0 my $peer = # retry unencrypted
959             Net::BitTorrent::Peer->new(
960             { Address => (sprintf q[%s:%d], $self->host, $self->port),
961             Torrent => $torrent{refaddr $self},
962             Source => q[TODO],
963             _plaintext => 1 # XXX
964             }
965             ) if !$incoming{refaddr $self};
966 0         0 return;
967             }
968 18         174 $_crypto_select{refaddr $self} = unpack(q[N], $crypto_select);
969              
970             # warn q[Plain]
971             #~ if $_crypto_select{refaddr $self} == CRYPTO_PLAIN;
972             #~ warn q[XOR ]
973             #~ if $_crypto_select{refaddr $self} == CRYPTO_XOR;
974             #~ warn q[RC4 ]
975             #~ if $_crypto_select{refaddr $self} == CRYPTO_RC4;
976             #~ warn q[AES ]
977             #~ if $_crypto_select{refaddr $self} == CRYPTO_AES;
978             #
979 18         100 substr($_data_in{refaddr $self}, 0, unpack(q[n], $len_PadD), q[]);
980 18 50       143 if ($_crypto_select{refaddr $self} == CRYPTO_RC4) {
981              
982             # reset and prime for ENCRYPT2
983 0         0 $self->_RC4($_KeyA{refaddr $self}, q[ ] x (1024 + 16), 1);
984              
985             #$self->_RC4($_KeyB{refaddr $self}, q[ ] x 1024 , 1);
986             }
987 18 50       98 if ($_data_in{refaddr $self}) {
988              
989             # XXX - Decode their IA
990             }
991 18         272 $_client{refaddr $self}->_add_connection($self, q[rw]);
992              
993             #warn q[Step 5A Complete: ] . $self->as_string;
994 18         130 $_state{refaddr $self} = REG_ONE;
995 18         64 return 1;
996             }
997              
998             sub ___handle_plaintext_handshake_one {
999              
1000             # warn((caller(0))[3]);
1001 51     51   137 my ($self) = @_;
1002              
1003             # Initiate connection by sending the plaintext handshake
1004 51         358 my %_payload = (
1005             Reserved => $_client{refaddr $self}->_build_reserved,
1006             Infohash => pack(q[H40], $torrent{refaddr $self}->infohash),
1007             PeerID => $_client{refaddr $self}->peerid
1008             );
1009 51         556 $self->_syswrite(build_handshake(
1010             $_payload{q[Reserved]}, $_payload{q[Infohash]},
1011             $_payload{q[PeerID]}
1012             )
1013             );
1014 51         377 $_client{refaddr $self}->_event(q[outgoing_packet],
1015             {Peer => $self,
1016             Payload => \%_payload,
1017             Type => HANDSHAKE
1018             }
1019             );
1020 51         319 $_client{refaddr $self}->_add_connection($self, q[rw]);
1021 51         215 $_state{refaddr $self} = REG_THREE;
1022 51         145 return 1;
1023             }
1024              
1025             sub ___handle_plaintext_handshake_two {
1026              
1027             # warn((caller(0))[3]);
1028 89     89   189 my ($self) = @_;
1029 89 100       489 return if !defined $_socket{refaddr $self};
1030 68         389 $_client{refaddr $self}->_add_connection($self, q[rw]);
1031 68 100       466 return if !$_data_in{refaddr $self};
1032 38         323 my $packet = parse_packet(\$_data_in{refaddr $self});
1033 38 50       110 return if !defined $packet;
1034 38 50       144 if ($packet->{q[Type]} != HANDSHAKE) {
1035 0         0 weaken $self;
1036 0         0 $self->_disconnect(DISCONNECT_MALFORMED_HANDSHAKE);
1037 0         0 return;
1038             }
1039 38         91 my $payload = $packet->{q[Payload]};
1040 38 50       154 return if !defined $payload;
1041              
1042             #warn q[plaintext handshake two!];
1043             #use Data::Dump qw[pp];
1044             #warn pp $packet;
1045             # Locate torrent or disconnect
1046             # Set torrent
1047             # Set bitfield
1048             # Send bitfield
1049             # Send ext handshake
1050 38         265 ($reserved_bytes{refaddr $self}, undef, $peerid{refaddr $self})
1051 38         72 = @{$payload};
1052 38         338 $_client{refaddr $self}->_event(q[incoming_packet],
1053             {Peer => $self,
1054             Payload => {
1055             Reserved => $payload->[0],
1056             Infohash => $payload->[1],
1057             PeerID => $payload->[2]
1058             },
1059             Type => HANDSHAKE
1060             }
1061             );
1062 38 50       297 if ($payload->[2] eq $_client{refaddr $self}->peerid) {
1063 0         0 weaken $self;
1064 0         0 $self->_disconnect(DISCONNECT_LOOPBACK);
1065 0         0 return;
1066             }
1067 38         904 $torrent{refaddr $self} = $_client{refaddr $self}
1068             ->_locate_torrent(unpack(q[H40], $payload->[1]));
1069 38 50       200 if (!defined $torrent{refaddr $self}) {
1070 0         0 weaken $self;
1071 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT,
1072             {Infohash => unpack(q[H40], $payload->[1])});
1073 0         0 return;
1074             }
1075 38         169 weaken $torrent{refaddr $self};
1076 38         252 ${$bitfield{refaddr $self}}
  38         170  
1077             = pack(q[b*], qq[\0] x $torrent{refaddr $self}->piece_count);
1078 38 50       100 if (scalar(
1079 443 100 100     2876 grep {
1080 38         231 $_->{q[Object]}->isa(q[Net::BitTorrent::Peer])
1081             && $_->{q[Object]}->peerid
1082             && $_->{q[Object]}->peerid eq $peerid{refaddr $self}
1083             } values %{$_client{refaddr $self}->_connections}
1084             ) > $_client{refaddr $self}->_connections_per_host
1085             )
1086 0         0 { $self->_disconnect(DISCONNECT_PREXISTING,
1087             {PeerID => $peerid{refaddr $self}});
1088 0         0 return;
1089             }
1090 38 50       239 if (scalar($torrent{refaddr $self}->peers)
1091             >= $_client{refaddr $self}->_peers_per_torrent)
1092 0         0 { $self->_disconnect(DISCONNECT_TOO_MANY);
1093 0         0 return;
1094             }
1095 38 50       137 if ($threads::shared::threads_shared) {
1096 0         0 threads::shared::share($bitfield{refaddr $self});
1097             }
1098 38         90 $_state{refaddr $self} = REG_OKAY;
1099 38         226 my %_payload = (
1100             Reserved => $_client{refaddr $self}->_build_reserved,
1101             Infohash => pack(q[H40], $torrent{refaddr $self}->infohash),
1102             PeerID => $_client{refaddr $self}->peerid
1103             );
1104 38         233 $self->_syswrite(build_handshake(
1105             $_payload{q[Reserved]}, $_payload{q[Infohash]},
1106             $_payload{q[PeerID]}
1107             )
1108             );
1109 38         201 $_client{refaddr $self}->_event(q[outgoing_packet],
1110             {Peer => $self,
1111             Payload => \%_payload,
1112             Type => HANDSHAKE
1113             }
1114             );
1115 38         196 $self->_send_bitfield;
1116 38         127 $self->_send_extended_handshake;
1117 38         371 $_client{refaddr $self}->_add_connection($self, q[rw]);
1118 38         118 $_state{refaddr $self} = REG_OKAY;
1119 38         177 return 1;
1120             }
1121              
1122             sub ___handle_plaintext_handshake_three {
1123              
1124             # warn((caller(0))[3]);
1125 81     81   176 my ($self) = @_;
1126 81 50       375 return if !defined $_socket{refaddr $self};
1127 81         752 $_client{refaddr $self}->_add_connection($self, q[rw]);
1128 81 100       596 return if !$_data_in{refaddr $self};
1129 27         203 my $packet = parse_packet(\$_data_in{refaddr $self});
1130 27 50       114 return if !defined $packet;
1131 27 50       261 if ($packet->{q[Type]} != HANDSHAKE) {
1132 0         0 weaken $self;
1133 0         0 $self->_disconnect(DISCONNECT_MALFORMED_HANDSHAKE);
1134 0         0 return;
1135             }
1136 27         88 my $payload = $packet->{q[Payload]};
1137 27 50       106 return if !defined $payload;
1138              
1139             #warn q[plaintext handshake two!];
1140             #use Data::Dump qw[pp];
1141             #warn pp $packet;
1142             # Locate torrent or disconnect
1143             # Set torrent
1144             # Set bitfield
1145             # Send bitfield
1146             # Send ext handshake
1147             #
1148 27         199 ($reserved_bytes{refaddr $self}, undef, $peerid{refaddr $self})
1149 27         38 = @{$payload};
1150 27         229 $_client{refaddr $self}->_event(q[incoming_packet],
1151             {Peer => $self,
1152             Payload => {
1153             Reserved => $payload->[0],
1154             Infohash => $payload->[1],
1155             PeerID => $payload->[2]
1156             },
1157             Type => HANDSHAKE
1158             }
1159             );
1160              
1161             # Avoid connecting to ourselves
1162 27 50       207 if ($payload->[2] eq $_client{refaddr $self}->peerid) {
1163 0         0 weaken $self;
1164 0         0 $self->_disconnect(DISCONNECT_LOOPBACK);
1165 0         0 return;
1166             }
1167              
1168             # make sure the infohash is what we expect
1169 27 50       227 if ($payload->[1] ne pack q[H*], $torrent{refaddr $self}->infohash) {
1170 0         0 weaken $self;
1171 0         0 $self->_disconnect(DISCONNECT_HANDSHAKE_INFOHASH);
1172 0         0 return;
1173             }
1174              
1175             # Send bitfield and ext handshake before moving beyond the handshake
1176             # phase
1177 27         116 $self->_send_bitfield;
1178 27         101 $self->_send_extended_handshake;
1179 27         138 $_client{refaddr $self}->_add_connection($self, q[rw]);
1180 27         89 $_state{refaddr $self} = REG_OKAY;
1181 27         98 return 1;
1182             }
1183              
1184             sub __handle_keepalive {
1185              
1186             # warn((caller(0))[3]);
1187 0     0   0 my ($self) = @_;
1188 0         0 $_client{refaddr $self}->_add_connection($self, q[rw]);
1189 0 0       0 return if !defined $torrent{refaddr $self};
1190 0 0       0 return if !defined $_socket{refaddr $self};
1191 0 0 0     0 if (defined $torrent{refaddr $self}
1192             and !($torrent{refaddr $self}->status & 1))
1193 0         0 { weaken $self;
1194 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1195             ; # XXX - this should never happen
1196 0         0 return;
1197             }
1198 0         0 $_client{refaddr $self}->_event(q[incoming_packet],
1199             {Peer => $self,
1200             Payload => {},
1201             Type => KEEPALIVE
1202             }
1203             );
1204 0         0 return 1;
1205             }
1206              
1207             sub __handle_choke {
1208              
1209             # warn((caller(0))[3]);
1210 0     0   0 my ($self) = @_;
1211 0         0 $_client{refaddr $self}->_add_connection($self, q[rw]);
1212 0 0       0 return if !defined $torrent{refaddr $self};
1213 0 0       0 return if !defined $_socket{refaddr $self};
1214 0 0 0     0 if (defined $torrent{refaddr $self}
1215             and !($torrent{refaddr $self}->status & 1))
1216 0         0 { weaken $self;
1217 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1218             ; # this should never happen
1219 0         0 return;
1220             }
1221 0         0 $_client{refaddr $self}->_event(q[incoming_packet],
1222             {Peer => $self,
1223             Payload => {},
1224             Type => CHOKE
1225             }
1226             );
1227 0         0 ${$peer_choking{refaddr $self}} = 1;
  0         0  
1228 0         0 ${$am_interested{refaddr $self}} = 0;
  0         0  
1229 0         0 for my $request (@{$requests_out{refaddr $self}}) {
  0         0  
1230 0         0 my $piece = $torrent{refaddr $self}
1231             ->_piece_by_index($request->{q[Index]});
1232 0         0 delete $piece->{q[Blocks_Requested]}->[$request->{q[_vec_offset]}]
1233             ->{refaddr $self};
1234             }
1235 0         0 return $requests_out{refaddr $self} = [];
1236             }
1237              
1238             sub __handle_unchoke {
1239              
1240             # warn((caller(0))[3]);
1241 17     17   41 my ($self) = @_;
1242 17         147 $_client{refaddr $self}->_add_connection($self, q[rw]);
1243 17 50       114 return if !defined $torrent{refaddr $self};
1244 17 50       215 return if !defined $_socket{refaddr $self};
1245 17 50 33     207 if (defined $torrent{refaddr $self}
1246             and !($torrent{refaddr $self}->status & 1))
1247 0         0 { weaken $self;
1248 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1249             ; # this should never happen
1250 0         0 return;
1251             }
1252 17         35 ${$peer_choking{refaddr $self}} = 0;
  17         77  
1253 17         466 $_client{refaddr $self}->_event(q[incoming_packet],
1254             {Peer => $self,
1255             Payload => {},
1256             Type => UNCHOKE
1257             }
1258             );
1259 17         99 $self->_request_block(2);
1260 17         128 return 1;
1261             }
1262              
1263             sub __handle_interested {
1264              
1265             # warn((caller(0))[3]);
1266 17     17   34 my ($self) = @_;
1267 17 50       87 return if !defined $torrent{refaddr $self};
1268 17 50       71 return if !defined $_socket{refaddr $self};
1269 17 50 33     191 if (defined $torrent{refaddr $self}
1270             and !($torrent{refaddr $self}->status & 1))
1271 0         0 { weaken $self;
1272 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1273             ; # this should never happen
1274 0         0 return;
1275             }
1276 17         32 ${$peer_interested{refaddr $self}} = 1;
  17         65  
1277 17         100 $_client{refaddr $self}->_event(q[incoming_packet],
1278             {Peer => $self,
1279             Payload => {},
1280             Type => INTERESTED
1281             }
1282             );
1283 17         115 $_client{refaddr $self}->_add_connection($self, q[rw]);
1284 17         92 $self->_send_unchoke();
1285 17         199 return 1;
1286             }
1287              
1288             sub __handle_not_interested {
1289              
1290             # warn((caller(0))[3]);
1291 2     2   5 my ($self) = @_;
1292 2         15 $_client{refaddr $self}->_add_connection($self, q[rw]);
1293 2 50       12 return if !defined $torrent{refaddr $self};
1294 2 50       11 return if !defined $_socket{refaddr $self};
1295 2 50 33     21 if (defined $torrent{refaddr $self}
1296             and !($torrent{refaddr $self}->status & 1))
1297 0         0 { weaken $self;
1298 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1299             ; # this should never happen
1300 0         0 return;
1301             }
1302 2         16 $_client{refaddr $self}->_event(q[incoming_packet],
1303             {Peer => $self,
1304             Payload => {},
1305             Type => NOT_INTERESTED
1306             }
1307             );
1308 2         6 ${$peer_interested{refaddr $self}} = 1;
  2         23  
1309 2         3 ${$am_choking{refaddr $self}} = 1;
  2         8  
1310 2         12 return 1;
1311             }
1312              
1313             sub __handle_have {
1314              
1315             # warn((caller(0))[3]);
1316 24     24   53 my ($self, $index) = @_;
1317 24         148 $_client{refaddr $self}->_add_connection($self, q[rw]);
1318 24 50       144 return if !defined $torrent{refaddr $self};
1319 24 50       95 return if !defined $_socket{refaddr $self};
1320 24 50       64 return if !defined $index;
1321 24 50 33     278 if (defined $torrent{refaddr $self}
1322             and !($torrent{refaddr $self}->status & 1))
1323 0         0 { weaken $self;
1324 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1325             ; # this should never happen
1326 0         0 return;
1327             }
1328 24         167 $_client{refaddr $self}->_event(q[incoming_packet],
1329             {Peer => $self,
1330             Payload => {Index => $index},
1331             Type => HAVE
1332             }
1333             );
1334 24         76 vec(${$bitfield{refaddr $self}}, $index, 1) = 1;
  24         487  
1335 24 50 33     86 if ((unpack(q[b*], ${$bitfield{refaddr $self}}) !~ m[1])
  24         191  
1336             && $torrent{refaddr $self}->is_complete)
1337 0         0 { weaken $self;
1338 0         0 $self->_disconnect(DISCONNECT_SEED);
1339 0         0 return;
1340             }
1341 24         87 $self->_check_interest;
1342 24 50 66     35 if (${$am_interested{refaddr $self}}
  24         167  
  12         122  
1343             and not ${$peer_choking{refaddr $self}})
1344 0         0 { $self->_request_block;
1345             }
1346             }
1347              
1348             sub __handle_bitfield {
1349              
1350             # warn((caller(0))[3]);
1351 0     0   0 my ($self, $payload) = @_;
1352 0         0 $_client{refaddr $self}->_add_connection($self, q[rw]);
1353 0 0       0 return if !defined $torrent{refaddr $self};
1354 0 0       0 return if !defined $_socket{refaddr $self};
1355 0 0       0 return if !defined $payload;
1356 0 0 0     0 if (defined $torrent{refaddr $self}
1357             and !($torrent{refaddr $self}->status & 1))
1358 0         0 { weaken $self;
1359 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1360             ; # this should never happen
1361 0         0 return;
1362             }
1363 0         0 ${$bitfield{refaddr $self}} = $payload;
  0         0  
1364 0         0 $_client{refaddr $self}->_event(q[incoming_packet],
1365             {Peer => $self,
1366             Payload => {Bitfield => $payload},
1367             Type => BITFIELD
1368             }
1369             );
1370 0 0 0     0 if ((unpack(q[b*], ${$bitfield{refaddr $self}}) !~ m[1])
  0         0  
1371             && $torrent{refaddr $self}->is_complete)
1372 0         0 { weaken $self;
1373 0         0 $self->_disconnect(DISCONNECT_SEED);
1374 0         0 return;
1375             }
1376 0         0 return $self->_check_interest;
1377             }
1378              
1379             sub __handle_request {
1380              
1381             # warn((caller(0))[3]);
1382 5     5   10 my ($self, $payload) = @_;
1383 5         36 $_client{refaddr $self}->_add_connection($self, q[rw]);
1384 5 50       40 return if !defined $torrent{refaddr $self};
1385 5 50       26 return if !defined $_socket{refaddr $self};
1386 5 50       19 return if !defined $payload;
1387 5 50 33     71 if (defined $torrent{refaddr $self}
1388             and !($torrent{refaddr $self}->status & 1))
1389 0         0 { weaken $self;
1390 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1391             ; # this should never happen
1392 0         0 return;
1393             }
1394 5         65 $_client{refaddr $self}->_event(q[incoming_packet],
1395             {Peer => $self,
1396             Payload => {Index => $payload->[0],
1397             Offset => $payload->[1],
1398             Length => $payload->[2]
1399             },
1400             Type => REQUEST
1401             }
1402             );
1403 5 50       17 if (not @{$requests_in{refaddr $self}}) {
  5         35  
1404 5         47 $_client{refaddr $self}->_schedule({Time => time + 3,
1405             Code => \&_fill_requests,
1406             Object => $self
1407             }
1408             );
1409             }
1410 5         17 return push @{$requests_in{refaddr $self}},
  5         65  
1411             {Index => $payload->[0],
1412             Offset => $payload->[1],
1413             Length => $payload->[2]
1414             };
1415             }
1416              
1417             sub __handle_piece {
1418              
1419             # warn((caller(0))[3]);
1420 3     3   8 my ($self, $payload) = @_;
1421 3         21 $_client{refaddr $self}->_add_connection($self, q[rw]);
1422 3 50       16 return if !defined $payload;
1423 3 50 33     34 if (defined $torrent{refaddr $self}
1424             and !($torrent{refaddr $self}->status & 1))
1425 0         0 { weaken $self;
1426 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1427             ; # this should never happen
1428 0         0 return;
1429             }
1430 3 50       17 return if !defined $torrent{refaddr $self};
1431 3 50       14 return if !defined $_socket{refaddr $self};
1432 3         6 my ($index, $offset, $data) = @{$payload};
  3         8  
1433 3         6 my $length = length($data);
1434 3 50 33     38 my ($request) = grep {
1435 3         12 ($_->{q[Index]} == $index)
1436             and ($_->{q[Offset]} == $offset)
1437             and ($_->{q[Length]} == $length)
1438 3         8 } @{$requests_out{refaddr $self}};
1439 3 50       10 if (not defined $request) {
1440 0         0 weaken $self;
1441 0         0 $self->_disconnect(-26);
1442 0         0 return;
1443             }
1444 3         25 $torrent{refaddr $self}->_add_downloaded($request->{q[Length]});
1445 3 50 33     14 @{$requests_out{refaddr $self}} = grep {
  3         35  
1446 3         11 ($_->{q[Index]} != $index)
1447             or ($_->{q[Offset]} != $offset)
1448             or ($_->{q[Length]} != $length)
1449 3         4 } @{$requests_out{refaddr $self}};
1450 3         31 $_client{refaddr $self}->_event(q[incoming_packet],
1451             {Payload => {Index => $index,
1452             Offset => $offset,
1453             Length => $length
1454             },
1455             Peer => $self,
1456             Type => PIECE
1457             }
1458             );
1459 3         26 my $piece = $torrent{refaddr $self}->_piece_by_index($index);
1460 3         6 my $okay = 0;
1461 3         10 for my $_retry (1 .. 3) {
1462              
1463 3 50       26 if ($torrent{refaddr $self}->_write_data($index, $offset, \$data))
1464 3         5 { $okay++;
1465 3         6 last;
1466             }
1467             }
1468 3 50       10 return if !$okay;
1469 3         13 $piece->{q[Blocks_Received]}->[$request->{q[_vec_offset]}] = 1;
1470 3         7 $piece->{q[Slow]} = 0;
1471 3         9 $piece->{q[Touch]} = time;
1472 3         25 for my $peer ($torrent{refaddr $self}->peers) {
1473 31         40 for my $x (reverse 0 .. $#{$requests_out{refaddr $peer}}) {
  31         135  
1474 0 0 0     0 if ( (defined $requests_out{refaddr $peer}->[$x])
      0        
      0        
1475             and
1476             ($requests_out{refaddr $peer}->[$x]->{q[Index]} == $index)
1477             and ($requests_out{refaddr $peer}->[$x]->{q[Offset]}
1478             == $offset)
1479             and ($requests_out{refaddr $peer}->[$x]->{q[Length]}
1480             == $length)
1481             )
1482 0         0 { $_data_out{refaddr $peer } .=
1483             build_cancel($request->{q[Index]},
1484             $request->{q[Offset]},
1485             $request->{q[Length]}
1486             );
1487 0         0 $_client{refaddr $self}->_event(q[outgoing_packet],
1488             {Payload => {
1489             Index => $index,
1490             Offset => $offset,
1491             Length => $length
1492             },
1493             Peer => $peer,
1494             Type => CANCEL
1495             }
1496             );
1497 0         0 $_client{refaddr $self}->_add_connection($peer, q[rw]);
1498 0         0 splice(@{$requests_out{refaddr $peer }}, $x, 1);
  0         0  
1499 0         0 last;
1500             }
1501             }
1502             }
1503 3 50       9 if (not grep { !$_ } @{$piece->{q[Blocks_Received]}}) {
  3         14  
  3         9  
1504 3 50 33     22 if ($torrent{refaddr $self}->_check_piece_by_index($index)
1505             and defined $torrent{refaddr $self})
1506 3         19 { for my $p ($torrent{refaddr $self}->peers) {
1507 31         129 $_data_out{refaddr $p} .= build_have($index);
1508 31         125 $_client{refaddr $self}->_add_connection($p, q[rw]);
1509             }
1510             }
1511             }
1512 3 50       312 $self->_request_block if $self->_check_interest;
1513 3         57 return 1;
1514             }
1515              
1516             sub __handle_cancel {
1517              
1518             # warn((caller(0))[3]);
1519 0     0   0 my ($self, $payload) = @_;
1520 0         0 $_client{refaddr $self}->_add_connection($self, q[rw]);
1521 0 0       0 return if !defined $torrent{refaddr $self};
1522 0 0       0 return if !defined $_socket{refaddr $self};
1523 0 0       0 return if !defined $payload;
1524 0 0 0     0 if (defined $torrent{refaddr $self}
1525             and !($torrent{refaddr $self}->status & 1))
1526 0         0 { weaken $self;
1527 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1528             ; # this should never happen
1529 0         0 return;
1530             }
1531 0         0 my ($index, $offset, $length) = @$payload;
1532 0         0 $_client{refaddr $self}->_event(q[incoming_packet],
1533             {Payload => {Index => $index,
1534             Offset => $offset,
1535             Length => $length
1536             },
1537             Peer => $self,
1538             Type => CANCEL
1539             }
1540             );
1541 0         0 for my $x (reverse 0 .. $#{$requests_in{refaddr $self}}) {
  0         0  
1542 0 0 0     0 if ( ($requests_in{refaddr $self}->[$x]->{q[Index]} == $index)
      0        
1543             and
1544             ($requests_in{refaddr $self}->[$x]->{q[Offset]} == $offset)
1545             and
1546             ($requests_in{refaddr $self}->[$x]->{q[Length]} == $length))
1547 0         0 { splice(@{$requests_in{refaddr $self}}, $x, 1);
  0         0  
1548             }
1549             }
1550 0         0 return 1;
1551             }
1552              
1553             sub __handle_have_all {
1554              
1555             # warn((caller(0))[3]);
1556 9     9   24 my ($self) = @_;
1557 9         65 $_client{refaddr $self}->_add_connection($self, q[rw]);
1558 9 50       62 return if !defined $torrent{refaddr $self};
1559 9 50       34 return if !defined $_socket{refaddr $self};
1560 9 50       53 if (!$torrent{refaddr $self}->status & 1) {
1561 0         0 weaken $self;
1562 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1563             ; # this should never happen
1564 0         0 return;
1565             }
1566 9         56 ${$bitfield{refaddr $self}}
  9         36  
1567             = pack(q[b*], qq[\1] x $torrent{refaddr $self}->piece_count);
1568 9         62 $_client{refaddr $self}->_event(q[incoming_packet],
1569             {Peer => $self,
1570             Payload => {},
1571             Type => HAVE_ALL
1572             }
1573             );
1574 9 100       61 if ($torrent{refaddr $self}->is_complete) {
1575 3         9 weaken $self;
1576 3         11 $self->_disconnect(DISCONNECT_SEED);
1577 3         24 return;
1578             }
1579 6         37 $self->_check_interest;
1580 6 50 33     12 if (${$am_interested{refaddr $self}}
  6         55  
  6         36  
1581             and not ${$peer_choking{refaddr $self}})
1582 0         0 { $self->_request_block;
1583             }
1584 6         37 return 1;
1585             }
1586              
1587             sub __handle_have_none {
1588              
1589             # warn((caller(0))[3]);
1590 39     39   85 my ($self) = @_;
1591 39 50       161 return if !defined $torrent{refaddr $self};
1592 39 50       233 return if !defined $_socket{refaddr $self};
1593 39 50       208 if (!($torrent{refaddr $self}->status & 1)) {
1594 0         0 weaken $self; # this should never happen
1595 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT);
1596 0         0 return;
1597             }
1598 39         218 ${$bitfield{refaddr $self}}
  39         181  
1599             = pack(q[b*], qq[\0] x $torrent{refaddr $self}->piece_count);
1600 39         257 $_client{refaddr $self}->_event(q[incoming_packet],
1601             {Peer => $self,
1602             Payload => {},
1603             Type => HAVE_NONE
1604             }
1605             );
1606 39         221 return $self->_check_interest;
1607             }
1608              
1609             sub __handle_allowed_fast {
1610              
1611             # warn((caller(0))[3]);
1612 0     0   0 my ($self, $payload) = @_;
1613 0         0 $_client{refaddr $self}->_add_connection($self, q[rw]);
1614 0 0       0 return if !defined $torrent{refaddr $self};
1615 0 0       0 return if !defined $_socket{refaddr $self};
1616 0 0       0 return if !defined $payload;
1617 0         0 $_client{refaddr $self}->_event(q[incoming_packet],
1618             {Payload => {Index => $payload},
1619             Peer => $self,
1620             Type => ALLOWED_FAST
1621             }
1622             );
1623 0         0 push(@{$_incoming_fastset{refaddr $self}}, $payload);
  0         0  
1624 0         0 return 1;
1625             }
1626              
1627             sub __handle_reject {
1628              
1629             # warn((caller(0))[3]);
1630 0     0   0 my ($self, $payload) = @_;
1631 0         0 $_client{refaddr $self}->_add_connection($self, q[rw]);
1632 0 0       0 return if !defined $payload;
1633 0 0       0 return if !defined $torrent{refaddr $self};
1634 0 0       0 return if !defined $_socket{refaddr $self};
1635 0         0 my ($index, $offset, $length) = @{$payload};
  0         0  
1636 0 0 0     0 my ($request) = grep {
1637 0         0 ($_->{q[Index]} == $index)
1638             and ($_->{q[Offset]} == $offset)
1639             and ($_->{q[Length]} == $length)
1640 0         0 } @{$requests_out{refaddr $self}};
1641              
1642 0 0       0 if (not defined $request) {
1643 0         0 weaken $self;
1644 0         0 $self->_disconnect(-29);
1645 0         0 return;
1646             }
1647 0 0 0     0 @{$requests_out{refaddr $self}} = grep {
  0         0  
1648 0         0 ($_->{q[Index]} != $index)
1649             or ($_->{q[Offset]} != $offset)
1650             or ($_->{q[Length]} != $length)
1651 0         0 } @{$requests_out{refaddr $self}};
1652 0         0 my $piece = $torrent{refaddr $self}->_piece_by_index($index);
1653 0 0       0 if (not defined $piece) {
1654 0         0 weaken $self;
1655 0         0 $self->_disconnect(-28);
1656 0         0 return;
1657             }
1658 0         0 delete $piece->{q[Blocks_Requested]}->[$request->{q[_vec_offset]}]
1659             ->{refaddr $self};
1660 0         0 $_client{refaddr $self}->_event(q[incoming_packet],
1661             {Payload => {Index => $index,
1662             Offset => $offset,
1663             Length => $length
1664             },
1665             Peer => $self,
1666             Type => REJECT
1667             }
1668             );
1669 0         0 return 1;
1670             }
1671              
1672             sub __handle_ext_protocol {
1673              
1674             # warn((caller(0))[3]);
1675 50     50   102 my ($self, $payload) = @_;
1676 50 50       229 return if !defined $torrent{refaddr $self};
1677 50 100       207 return if !defined $_socket{refaddr $self};
1678 47 50       118 return if !defined $payload;
1679 47 50 33     470 if (defined $torrent{refaddr $self} # this should never happen
1680             and !($torrent{refaddr $self}->status & 1))
1681 0         0 { weaken $self;
1682 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT);
1683 0         0 return;
1684             }
1685 47 50       253 return if $torrent{refaddr $self}->status & 32;
1686 47         72 my ($id, $packet) = @{$payload};
  47         102  
1687 47 50       129 if ($packet) {
1688 47 50       111 if ($id == 0) {
1689 47 50 33     286 if ( defined $_client{refaddr $self}->_dht
1690             and defined $packet->{q[p]})
1691 0         0 { my (undef, $packed_ip)
1692             = unpack_sockaddr_in(
1693             getpeername($_socket{refaddr $self}));
1694 0         0 $_client{refaddr $self}->_dht->add_node(
1695             {ip => inet_ntoa($packed_ip),
1696             port => $packet->{q[p]}
1697             }
1698             );
1699             }
1700             }
1701 47         100 $packet->{q[ID]} = $id;
1702 47         345 $_client{refaddr $self}->_event(q[incoming_packet],
1703             {Payload => $packet,
1704             Peer => $self,
1705             Type => EXTPROTOCOL
1706             }
1707             );
1708             }
1709 47         417 return 1;
1710             }
1711              
1712             sub _check_interest {
1713              
1714             # warn((caller(0))[3]);
1715 72     72   149 my ($self) = @_;
1716 72 50       352 if (!$torrent{refaddr $self}) { return; }
  0         0  
1717 72 50       374 if (!($torrent{refaddr $self}->status & 1)) {
1718 0         0 weaken $self; # this should never happen
1719 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT);
1720 0         0 return;
1721             }
1722 72 50       305 return if $torrent{refaddr $self}->status & 32;
1723 72         197 my $interesting = ${$am_interested{refaddr $self}};
  72         260  
1724 72         414 my $torrent_have = $torrent{refaddr $self}->bitfield();
1725 72         426 my $torrent_want = $torrent{refaddr $self}->_wanted();
1726 72         130 my $relevence = ${$bitfield{refaddr $self}} & $torrent_want;
  72         479  
1727 72 100       365 $interesting = (index(unpack(q[b*], $relevence), 1, 0) != -1) ? 1 : 0;
1728 72 100 100     389 if ($interesting and not ${$am_interested{refaddr $self}}) {
  18 100 100     112  
  54         502  
1729 17         32 ${$am_interested{refaddr $self}} = 1;
  17         48  
1730 17         71 $self->_syswrite(build_interested);
1731 17         102 $_client{refaddr $self}->_event(q[outgoing_packet],
1732             {Peer => $self, Payload => {}, Type => INTERESTED});
1733 17         101 $_client{refaddr $self}->_add_connection($self, q[rw]);
1734             }
1735             elsif (not $interesting and ${$am_interested{refaddr $self}}) {
1736 3         5 ${$am_interested{refaddr $self}} = 0;
  3         9  
1737 3         11 $self->_syswrite(build_not_interested);
1738 3         23 $_client{refaddr $self}->_event(q[outgoing_packet],
1739             {Peer => $self, Payload => {}, Type => NOT_INTERESTED});
1740 3         103 $_client{refaddr $self}->_add_connection($self, q[rw]);
1741             }
1742 72         127 return ${$am_interested{refaddr $self}};
  72         397  
1743             }
1744              
1745             sub _disconnect_useless_peer {
1746              
1747             # warn((caller(0))[3]);
1748 0     0   0 my ($self) = @_;
1749 0 0       0 return if not defined $self;
1750 0 0       0 if ($_last_contact{refaddr $self} < (time - 180)) {
1751 0         0 weaken $self;
1752 0         0 $self->_disconnect(-40);
1753 0         0 return;
1754             }
1755 0 0 0     0 if ( ${$peer_choking{refaddr $self}}
  0         0  
  0         0  
1756             and ${$am_interested{refaddr $self}})
1757             { # XXX - send uninterested?
1758 0         0 $self->_check_interest;
1759             }
1760 0 0 0     0 if ( (${$peer_choking{refaddr $self}})
  0   0     0  
  0         0  
1761 0         0 and (!${$am_interested{refaddr $self}})
1762             and (!${$peer_interested{refaddr $self}}))
1763 0         0 { weaken $self;
1764 0         0 $self->_disconnect(DISCONNECT_USELESS_PEER);
1765 0         0 return;
1766             }
1767 0         0 $_client{refaddr $self}->_schedule(
1768             {Time => (180 + $_last_contact{refaddr $self}),
1769             Code => \&_disconnect_useless_peer,
1770             Object => $self
1771             }
1772             );
1773 0         0 return 1;
1774             }
1775              
1776             sub _cancel_old_requests {
1777              
1778             # warn((caller(0))[3]);
1779 36     36   107 my ($self) = @_;
1780 36 50       116 return if not defined $self;
1781 36 100       301 return if not defined $_socket{refaddr $self};
1782 29         281 $_client{refaddr $self}->_schedule({Time => time + 15,
1783             Code => \&_cancel_old_requests,
1784             Object => $self
1785             }
1786             );
1787 29         87 my $canceled = 0;
1788 29 50       44 if (@{$requests_out{refaddr $self}} == []) {
  29         169  
1789 0         0 return;
1790             }
1791 29         78 for my $i (reverse(0 .. $#{$requests_out{refaddr $self}})) {
  29         155  
1792 1         6 my $request = $requests_out{refaddr $self}->[$i];
1793 1 50       9 if (time <= ($request->{q[Timestamp]} + 60)) {
1794 1         4 next;
1795             }
1796 0         0 my $piece = $torrent{refaddr $self}
1797             ->_piece_by_index($request->{q[Index]});
1798 0         0 delete $piece->{q[Blocks_Requested]}->[$request->{q[_vec_offset]}]
1799             ->{refaddr $self};
1800 0 0 0     0 if (!$piece->{q[Touch]} || $piece->{q[Touch]} <= time - 180) {
1801 0         0 $piece->{q[Slow]} = 1;
1802             }
1803 0         0 $self->_syswrite(build_cancel(
1804             $request->{q[Index]}, $request->{q[Offset]},
1805             $request->{q[Length]}
1806             )
1807             );
1808 0         0 $_client{refaddr $self}->_event(
1809             q[outgoing_packet],
1810             {Payload => {
1811             Index => $request->{q[Index]},
1812             Offset => $request->{q[Offset]},
1813             Length => $request->{q[Length]}
1814             },
1815             Peer => $self,
1816             Type => CANCEL
1817             }
1818             );
1819 0         0 splice(@{$requests_out{refaddr $self}}, $i, 1);
  0         0  
1820 0         0 $canceled++;
1821             }
1822 29 50       94 $_client{refaddr $self}->_add_connection($self, q[rw])
1823             if $canceled;
1824 29         87 return $canceled;
1825             }
1826              
1827             sub _request_block {
1828              
1829             # warn((caller(0))[3]);
1830 17     17   36 my ($self, $_range) = @_;
1831 17 50       92 return if not defined $_socket{refaddr $self};
1832 17 50       37 return if ${$peer_choking{refaddr $self}};
  17         76  
1833 17 50       171 if (!($torrent{refaddr $self}->status & 1)) {
1834 0         0 weaken $self;
1835 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1836             ; # this should never happen
1837 0         0 return;
1838             }
1839 17 50       131 return if $torrent{refaddr $self}->status & 32;
1840 17         40 my $return = 0;
1841 0         0 my $range
1842             = defined $_range
1843             ? [1 .. $_range]
1844 17 50       89 : [max(1, scalar(@{$requests_out{refaddr $self}})) .. max(
1845             25,
1846             int((2**21)
1847             / $torrent{refaddr $self}->raw_data(1)
1848             ->{q[info]}{q[piece length]}
1849             )
1850             )
1851             ];
1852             REQUEST:
1853 17         52 for (@$range) {
1854 22         277 my $piece = $torrent{refaddr $self}->_pick_piece($self);
1855 22 100       60 if ($piece) {
1856 18         24 my $vec_offset;
1857 18 50       61 if ($piece->{q[Endgame]}) {
1858              
1859             # This next bit selects the least requested block (max 5 requests),
1860             # makes sure this peer isn't already sitting on this request
1861             # and... I just lost my train of thought; It's Friday afternoon.
1862             # Regardless of how it looks, it does what I mean.
1863 0         0 my $tmp_index = -1;
1864 0         0 my %temp = map {
1865 0         0 $tmp_index++;
1866 0 0 0     0 ( ($_ < 5)
1867             and
1868             ($piece->{q[Blocks_Received]}->[$tmp_index] == 0)
1869             )
1870             ? ($tmp_index => $_)
1871             : ()
1872 0         0 } @{$piece->{q[Blocks_Received]}};
1873 0         0 INDEX:
1874 0         0 for my $index (sort { $temp{$a} <=> $temp{$b} }
  0         0  
1875             sort { $a <=> $b } keys %temp)
1876             {
1877 0 0 0     0 if (not grep {
  0 0 0     0  
1878 0         0 (defined $piece->{q[Blocks_Requested]}
1879             ->[$index]->{refaddr $self})
1880             and (
1881             ($piece->{q[Blocks_Received]}->[$index])
1882             or (($piece->{q[Index]} == $_->{q[Index]})
1883             and ($index == $_->{q[_vec_offset]}))
1884             )
1885             } @{$requests_out{refaddr $self}}
1886             )
1887 0         0 { $vec_offset = $index;
1888 0         0 last INDEX;
1889             }
1890             }
1891             }
1892             else {
1893 18         70 BLOCK:
1894 18         40 for my $i (0 .. $#{$piece->{q[Blocks_Requested]}}) {
1895 18 100       25 if (not(keys %{$piece->{q[Blocks_Requested]}->[$i]}))
  18         110  
1896 5         12 { $vec_offset = $i;
1897 5         16 last BLOCK;
1898             }
1899             }
1900             }
1901 18 100 66     261 if (not defined $vec_offset or $vec_offset == -1) {
1902              
1903             # xxx - pick a different piece?
1904             # xxx - Honestly, this piece shouldn't have been returned
1905             # from _pick_piece in the first place...
1906 13         38 last REQUEST;
1907             }
1908 5         28 $piece->{q[Blocks_Requested]}->[$vec_offset]->{refaddr $self}
1909             = $self;
1910 5         32 weaken $piece->{q[Blocks_Requested]}->[$vec_offset]
1911             ->{refaddr $self};
1912 5         15 my $offset = $vec_offset * $piece->{q[Block_Length]};
1913 5 50 33     55 my $length = (
1914             (($vec_offset + 1) == $piece->{q[Block_Count]})
1915             ? (($piece->{q[Length]} % $piece->{q[Block_Length]})
1916             || $piece->{q[Block_Length]})
1917             : ($piece->{q[Block_Length]})
1918             );
1919 5         256 my $request = {Index => $piece->{q[Index]},
1920             Offset => $offset,
1921             Length => $length,
1922             Timestamp => time,
1923             _vec_offset => $vec_offset,
1924             };
1925 5         12 push @{$requests_out{refaddr $self}}, $request;
  5         29  
1926 5         60 $_client{refaddr $self}->_event(
1927             q[outgoing_packet],
1928             {Payload => {
1929             Index => $piece->{q[Index]},
1930             Offset => $offset,
1931             Length => $length
1932             },
1933             Peer => $self,
1934             Type => REQUEST
1935             }
1936             );
1937 5         48 $_client{refaddr $self}->_add_connection($self, q[rw]);
1938 5         41 $self->_syswrite(
1939             build_request($piece->{q[Index]}, $offset, $length));
1940 5         17 $return++;
1941             }
1942             else {
1943 4         12 last REQUEST;
1944             }
1945             }
1946 17         48 return $return;
1947             }
1948              
1949             sub _send_bitfield {
1950              
1951             # warn((caller(0))[3]);
1952 65     65   108 my ($self) = @_;
1953 65 50       277 return if !defined $torrent{refaddr $self};
1954 65 50       333 return $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
1955             if !$torrent{refaddr $self}->status & 1;
1956 65 50       285 return if !defined $_socket{refaddr $self};
1957 65 50       266 return if !defined $reserved_bytes{refaddr $self};
1958 65         130 my ($_i, @have) = (0);
1959 65         406 for my $x (split q[], unpack q[b*], $torrent{refaddr $self}->bitfield)
1960 520 100       927 { push @have, $_i if $x;
1961 520         670 $_i++;
1962             }
1963 65 100 66     598 if ( (scalar(@have) == 0)
    100 66        
    50          
1964             && (ord(substr($reserved_bytes{refaddr $self}, 7, 1)) & 0x04))
1965 50         224 { $self->_syswrite(build_have_none);
1966 50         313 $_client{refaddr $self}->_event(q[outgoing_packet],
1967             {Peer => $self, Payload => {}, Type => HAVE_NONE});
1968             }
1969             elsif ( (scalar(@have) == $self->torrent->piece_count)
1970             && (ord(substr($reserved_bytes{refaddr $self}, 7, 1)) & 0x04))
1971 11         50 { $self->_syswrite(build_have_all);
1972 11         78 $_client{refaddr $self}->_event(q[outgoing_packet],
1973             {Peer => $self, Payload => {}, Type => HAVE_ALL});
1974             }
1975             elsif (scalar(@have) > 12) {
1976 0         0 $self->_syswrite(build_bitfield(pack q[B*], unpack q[b*],
1977             $torrent{refaddr $self}->bitfield
1978             )
1979             );
1980 0         0 $_client{refaddr $self}->_event(q[outgoing_packet],
1981             {Peer => $self, Payload => {}, Type => BITFIELD});
1982             }
1983             else {
1984 4         10 for my $index (@have) {
1985 4         19 $self->_syswrite(build_have($index));
1986 4         41 $_client{refaddr $self}->_event(q[outgoing_packet],
1987             {Peer => $self,
1988             Payload => {Index => $index},
1989             Type => HAVE
1990             }
1991             );
1992             }
1993             }
1994 65         236 return 1;
1995             }
1996              
1997             sub _send_extended_handshake {
1998              
1999             # warn((caller(0))[3]);
2000 65     65   113 my ($self) = @_;
2001 65 50 33     546 if (defined $torrent{refaddr $self}
2002             and !($torrent{refaddr $self}->status & 1))
2003 0         0 { weaken $self;
2004 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
2005             ; # this should never happen
2006 0         0 return;
2007             }
2008 65 50       265 return if !defined $torrent{refaddr $self};
2009 65 50       266 return if !defined $_socket{refaddr $self};
2010 65 50       239 return if not defined $reserved_bytes{refaddr $self};
2011             return
2012 65 50       341 if not ord(substr($reserved_bytes{refaddr $self}, 5, 1)) & 0x10;
2013 65         1061 my ($_peerport, $_packed_ip)
2014             = unpack_sockaddr_in(getpeername($_socket{refaddr $self}));
2015 65         136 my $_id = 0;
2016 65 100       428 my $_payload = {
    50          
2017              
2018             #m => {$_private{refaddr $self} ? () : (ut_pex => 1)}, # TODO: PEX
2019             ($_client{refaddr $self}->_use_dht
2020             ? (p => $_client{refaddr $self}->_udp_port)
2021             : ()
2022             ),
2023             v => $Net::BitTorrent::Version::PRODUCT_TOKEN,
2024             ($_packed_ip ? (yourip => $_packed_ip) : ()),
2025             reqq => 30 # XXX - Lies
2026             };
2027 65         374 $_client{refaddr $self}->_event(q[outgoing_packet],
2028             {Payload => $_payload,
2029             ID => $_id,
2030             Peer => $self,
2031             Type => EXTPROTOCOL
2032             }
2033             );
2034 65         349 return $self->_syswrite(build_extended($_id, $_payload));
2035             }
2036              
2037             sub _send_keepalive {
2038              
2039             # warn((caller(0))[3]);
2040 0     0   0 my ($self) = @_;
2041 0 0       0 return if not defined $self;
2042 0 0       0 return if not defined $_socket{refaddr $self};
2043 0 0       0 return if not defined $torrent{refaddr $self};
2044 0 0 0     0 if (defined $torrent{refaddr $self}
2045             and !($torrent{refaddr $self}->status & 1))
2046 0         0 { weaken $self;
2047 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
2048             ; # this should never happen
2049 0         0 return;
2050             }
2051 0         0 $_client{refaddr $self}->_schedule({Time => time + 120,
2052             Code => \&_send_keepalive,
2053             Object => $self
2054             }
2055             );
2056 0 0       0 return if $torrent{refaddr $self}->status & 32;
2057 0         0 $_client{refaddr $self}->_event(q[outgoing_packet],
2058             {Peer => $self,
2059             Payload => {},
2060             Type => KEEPALIVE
2061             }
2062             );
2063 0         0 $self->_syswrite(build_keepalive);
2064 0         0 $self->_check_interest;
2065 0         0 return 1;
2066             }
2067              
2068             sub _fill_requests {
2069              
2070             # warn((caller(0))[3]);
2071 5     5   14 my ($self) = @_;
2072 5 50       32 return if !defined $torrent{refaddr $self};
2073 5 50       25 return if !defined $_socket{refaddr $self};
2074 5 50       29 return if $torrent{refaddr $self}->status & 32;
2075 5 50       8 return if not @{$requests_in{refaddr $self}};
  5         32  
2076 5 50       10 return if ${$am_choking{refaddr $self}};
  5         26  
2077 5 50 33     52 if (defined $torrent{refaddr $self}
2078             and !($torrent{refaddr $self}->status & 1))
2079 0         0 { weaken $self;
2080 0         0 $self->_disconnect(DISCONNECT_NO_SUCH_TORRENT)
2081             ; # this should never happen
2082 0         0 return;
2083             }
2084 5 50 33     53 if (defined $torrent{refaddr $self}
2085             and ($torrent{refaddr $self}->status & 32))
2086 0         0 { return;
2087             }
2088 5   66     37 while ((length($_data_out{refaddr $self}) < 2**18)
  10         65  
2089             and @{$requests_in{refaddr $self}})
2090 5         7 { my $request = shift @{$requests_in{refaddr $self}};
  5         21  
2091             next
2092 5 50       43 unless $torrent{refaddr $self}
2093             ->_check_piece_by_index($request->{q[Index]});
2094 5 50       23 next unless $request->{q[Length]};
2095 5         40 $torrent{refaddr $self}->_add_uploaded($request->{q[Length]});
2096 5         58 $_client{refaddr $self}->_event(
2097             q[outgoing_packet],
2098             {Payload => {
2099             Index => $request->{q[Index]},
2100             Offset => $request->{q[Offset]},
2101             Length => $request->{q[Length]}
2102             },
2103             Peer => $self,
2104             Type => PIECE
2105             }
2106             );
2107 5         40 $self->_syswrite(
2108             build_piece(
2109             $request->{q[Index]},
2110             $request->{q[Offset]},
2111             $torrent{refaddr $self}->_read_data(
2112             $request->{q[Index]}, $request->{q[Offset]},
2113             $request->{q[Length]}
2114             )
2115             )
2116             );
2117              
2118             #if (rand(20) >= 20) { $self->_send_choke; }
2119             }
2120 5         24 $_client{refaddr $self}->_schedule({Time => time + 3,
2121             Code => \&_fill_requests,
2122             Object => $self
2123             }
2124 5 50       17 ) if @{$requests_in{refaddr $self}};
2125 5 50       28 $_client{refaddr $self}->_add_connection($self, q[rw]) or return;
2126 5         17 return 1;
2127             }
2128              
2129             sub _send_choke {
2130              
2131             # warn((caller(0))[3]);
2132 0     0   0 my ($self) = @_;
2133 0 0       0 return if !defined $torrent{refaddr $self};
2134 0 0       0 return if !defined $_socket{refaddr $self};
2135 0 0 0     0 if (defined $torrent{refaddr $self}
2136             and $torrent{refaddr $self}->status & 2)
2137 0         0 { weaken $self;
2138 0         0 $self->_disconnect(DISCONNECT_HASHCHECKING);
2139 0         0 return;
2140             }
2141 0 0       0 return if ${$am_choking{refaddr $self}} == 1;
  0         0  
2142 0         0 $requests_in{refaddr $self} = [];
2143 0         0 ${$am_choking{refaddr $self}} = 1;
  0         0  
2144 0         0 ${$peer_interested{refaddr $self}} = 0;
  0         0  
2145 0         0 $self->_syswrite(build_choke);
2146 0         0 $_client{refaddr $self}->_event(q[outgoing_packet],
2147             {Peer => $self,
2148             Payload => {},
2149             Type => CHOKE
2150             }
2151             );
2152 0 0       0 $_client{refaddr $self}->_add_connection($self, q[rw]) or return;
2153 0         0 return 1;
2154             }
2155              
2156             sub _send_unchoke {
2157              
2158             # warn((caller(0))[3]);
2159 17     17   42 my ($self) = @_;
2160 17 50       72 return if !defined $torrent{refaddr $self};
2161 17 50       67 return if !defined $_socket{refaddr $self};
2162 17 50 33     117 if (defined $torrent{refaddr $self}
2163             and $torrent{refaddr $self}->status & 2)
2164 0         0 { weaken $self;
2165 0         0 $self->_disconnect(DISCONNECT_HASHCHECKING);
2166 0         0 return;
2167             }
2168 17 50       79 return if $torrent{refaddr $self}->status & 32;
2169 17 50       25 return if ${$am_choking{refaddr $self}} == 0;
  17         89  
2170 17 50       98 if (scalar(grep { $_->am_choking == 0 } $torrent{refaddr $self}->peers
  172         292  
2171             ) <= 16 # XXX - client wide limit on number of unchoked peers
2172             )
2173 17         38 { ${$am_choking{refaddr $self}} = 0;
  17         60  
2174 17         77 $self->_syswrite(build_unchoke);
2175 17         100 $_client{refaddr $self}->_event(q[outgoing_packet],
2176             {Peer => $self, Payload => {}, Type => UNCHOKE});
2177 17 50       106 $_client{refaddr $self}->_add_connection($self, q[rw])
2178             or return;
2179             }
2180             else {
2181 0         0 $_client{refaddr $self}->_schedule({Time => time + 15,
2182             Code => \&_send_unchoke,
2183             Object => $self
2184             }
2185             );
2186             }
2187 17         51 return 1;
2188             }
2189              
2190             sub _disconnect {
2191              
2192             # warn((caller(0))[3]);
2193             # Returns...
2194             # 1) ...when the socket is disconnected and (if applicable)
2195             # removed from the client object.
2196             # undef) ...whe any of the following cause an early return or
2197             # the socket cannot be removed from the parent.
2198             # Expects the following parameters:
2199             # - a reference to a blessed N::B::S::Peer (the only required parameter)
2200             # - a numeric 'reason' (defaults to 0, positive are winsock, negative are user)
2201             # - extra data in a hash ref (passed on to peer_disconnect callback in 'Advanced' key)
2202 71     71   252 my ($self, $reason, $extra) = @_;
2203 71         733 $_client{refaddr $self}->_remove_connection($self);
2204 71 100       428 if (defined $_socket{refaddr $self}) {
2205 40         3820 shutdown($_socket{refaddr $self}, 2);
2206 40         3819 close($_socket{refaddr $self});
2207             }
2208 71         477 delete $_socket{refaddr $self};
2209 71 50       4104 $_client{refaddr $self}->_event(q[peer_disconnect],
2210             {Peer => $self,
2211             Reason => ($reason + 0),
2212             ($extra ? (Advanced => $extra) : ())
2213             }
2214             );
2215 71         2984 return 1;
2216             }
2217              
2218             sub as_string {
2219              
2220             # warn((caller(0))[3]);
2221 0     0 1 0 my ($self, $advanced) = @_;
2222 0 0       0 my $dump = sprintf(
2223             (!$advanced ? q[%s:%s (%s)] : <<'ADVANCED'),
2224             Net::BitTorrent::Peer
2225              
2226             Address: %s:%s
2227             Peer ID: %s
2228             Torrent: %s
2229             Direction: %s
2230              
2231             Interested: %s
2232             Interesting: %s
2233             Choked: %s
2234             Choking: %s
2235              
2236             Progress:
2237             [%s]
2238             ADVANCED
2239             ($self->host || q[]),
2240             ($self->port || q[]),
2241             ($peerid{refaddr $self} ? $peerid{refaddr $self} : q[Unknown]),
2242             ( $torrent{refaddr $self}
2243             ? $torrent{refaddr $self}->infohash
2244             : q[Unknown]
2245             ),
2246             ($incoming{refaddr $self} ? q[Incoming] : q[Outgoing]),
2247 0         0 (map { $_ ? q[Yes] : q[No] } (${$peer_interested{refaddr $self}},
  0         0  
2248 0         0 ${$am_interested{refaddr $self}},
2249 0         0 ${$am_choking{refaddr $self}},
2250 0         0 ${$peer_choking{refaddr $self}}
2251             )
2252             ),
2253             (($_state{refaddr $self} == REG_OKAY)
2254             ? (sprintf q[%s],
2255             join q[],
2256 0 0 0     0 map { vec(${$bitfield{refaddr $self}}, $_, 1) ? q[|] : q[ ] }
  0 0 0     0  
    0          
    0          
    0          
    0          
2257             0 .. $torrent{refaddr $self}->piece_count - 1
2258             )
2259             : q[NA]
2260             )
2261             );
2262 0 0       0 return defined wantarray ? $dump : print STDERR qq[$dump\n];
2263             }
2264              
2265             sub CLONE {
2266             ## warn((caller(0))[3]);
2267 0     0   0 for my $_oID (keys %REGISTRY) {
2268 0         0 my $_obj = $REGISTRY{$_oID};
2269 0         0 my $_nID = refaddr $_obj;
2270 0         0 for (@CONTENTS) {
2271 0         0 $_->{$_nID} = $_->{$_oID};
2272 0         0 delete $_->{$_oID};
2273             }
2274 0         0 weaken $_client{$_nID};
2275 0         0 weaken $torrent{$_nID};
2276 0         0 weaken($REGISTRY{$_nID} = $_obj);
2277 0         0 delete $REGISTRY{$_oID};
2278             }
2279 0         0 return 1;
2280             }
2281             DESTROY {
2282 24     24   49 my ($self) = @_;
2283 24 100       129 if ($torrent{refaddr $self}) {
2284 23         45 for my $request (@{$requests_out{refaddr $self}}) {
  23         125  
2285 0         0 my $piece = $torrent{refaddr $self}
2286             ->_piece_by_index($request->{q[Index]});
2287 0         0 delete $piece->{q[Blocks_Requested]}
2288             ->[$request->{q[_vec_offset]}]->{refaddr $self};
2289             }
2290             }
2291 24         62 for (@CONTENTS) { delete $_->{refaddr $self}; }
  792         7913  
2292 24         284 return delete $REGISTRY{refaddr $self};
2293             }
2294              
2295             sub _RC4 {
2296             ## warn((caller(0))[3]);
2297 384     384   1092 my ($self, $pass, $text, $reset) = @_;
2298             my $rc4_output = sub { # PRGA
2299 182388     182388   734273 $_i{refaddr $self}{$pass} = ($_i{refaddr $self}{$pass} + 1) & 255;
2300 182388         1075389 $_j{refaddr $self}{$pass}
2301             = ( $_j{refaddr $self}{$pass}
2302             + $_RC4_S{refaddr $self}{$pass}[$_i{refaddr $self}{$pass}]
2303             ) & 255;
2304 182388         525989 @{$_RC4_S{refaddr $self}{$pass}}[$_i{refaddr $self}{$pass},
  182388         876939  
2305             $_j{refaddr $self}{$pass}]
2306 182388         622036 = @{$_RC4_S{refaddr $self}{$pass}}[$_j{refaddr $self}{$pass},
2307             $_i{refaddr $self}{$pass}];
2308             return
2309 182388         1453521 $_RC4_S{refaddr $self}{$pass}[
2310             ( $_RC4_S{refaddr $self}{$pass}[$_i{refaddr $self}{$pass}]
2311             + $_RC4_S{refaddr $self}{$pass}
2312             [$_j{refaddr $self}{$pass}]) & 255
2313             ];
2314 384         4861 };
2315 384         938 my $_j = 0;
2316 384 100 66     4037 if ($reset || !$_RC4_S{refaddr $self}{$pass}) {
2317 176         1364 my @key = unpack q[C*], $pass;
2318 176         745 @{$_RC4_S{refaddr $self}{$pass}} = 0 .. 255; # KSA
  176         6079  
2319 176         6894 ($_i{refaddr $self}{$pass}, $_j{refaddr $self}{$pass}) = (0, 0);
2320 176         644 for my $_i (0 .. 255) {
2321 45056         178546 $_j
2322             = ( $_j
2323             + $key[$_i % @key]
2324             + $_RC4_S{refaddr $self}{$pass}[$_i]) & 255;
2325 45056         169073 @{$_RC4_S{refaddr $self}{$pass}}[$_i, $_j]
  45056         117757  
2326 45056         63884 = @{$_RC4_S{refaddr $self}{$pass}}[$_j, $_i];
2327             }
2328             }
2329 182388         388070 return pack q[C*],
2330 384         14136 map { ord(substr($text, $_, 1)) ^ $rc4_output->() }
2331             0 .. length($text) - 1;
2332             }
2333             1;
2334             }
2335              
2336             =pod
2337              
2338             =head1 NAME
2339              
2340             Net::BitTorrent::Peer - Remote BitTorrent Peer
2341              
2342             =head1 Description
2343              
2344             L represents a single peer
2345             connection.
2346              
2347             =head1 Constructor
2348              
2349             =over
2350              
2351             =item C
2352              
2353             Creates a L object. This
2354             constructor should not be used directly.
2355              
2356             =back
2357              
2358             =head1 Methods
2359              
2360             =over
2361              
2362             =item C
2363              
2364             Returns a bitfield representing the pieces that have been reported to be
2365             successfully downloaded by the remote peer.
2366              
2367             =item C
2368              
2369             Returns a boolean value based on whether or not we are currently choking
2370             the remote peer.
2371              
2372             =item C
2373              
2374             Returns a boolean value based on whether or not we are currently
2375             interested in the set of pieces held by the remote peer
2376              
2377             =item C
2378              
2379             Returns the host (typically an IP address) of the remote peer.
2380              
2381             =item C
2382              
2383             Returns a boolean value based on whether or not this connection was
2384             initiated by the remote peer or us.
2385              
2386             =item C
2387              
2388             Returns a boolean value based on whether or not the remote peer is
2389             currently choking us.
2390              
2391             =item C
2392              
2393             Returns a boolean value based on whether or not the remote peer is
2394             currently interested in being unchoked or in requesting data from us.
2395              
2396             =item C
2397              
2398             Returns the Peer ID used to identify this peer.
2399              
2400             See also: theory.org (http://tinyurl.com/4a9cuv)
2401              
2402             =item C
2403              
2404             The port used by the remote peer.
2405              
2406             =item C
2407              
2408             Returns the C<8> reserved bytes from the plaintext handshake. Each bit in
2409             these bytes can be used to change the behavior of the protocol.
2410              
2411             See also: theory.org (http://tinyurl.com/aw76zb)
2412              
2413             =item C
2414              
2415             In a future version, this will return how we obtained this connection
2416             (DHT, user, incoming, certain tracker, etc.).
2417              
2418             =item C
2419              
2420             Returns the related L
2421             object. This will be C if the peer has not completed the
2422             handshake.
2423              
2424             =item C
2425              
2426             Returns a 'ready to print' dump of the object's data structure. If
2427             called in void context, the structure is printed to C.
2428             C is a boolean value.
2429              
2430             =back
2431              
2432             =begin :podcoverage
2433              
2434             =over
2435              
2436             =item CRYPTO_AES
2437              
2438             =item CRYPTO_PLAIN
2439              
2440             =item CRYPTO_RC4
2441              
2442             =item CRYPTO_XOR
2443              
2444             =item DH_G
2445              
2446             =item DH_P
2447              
2448             =item MSE_FIVE
2449              
2450             =item MSE_FOUR
2451              
2452             =item MSE_ONE
2453              
2454             =item MSE_THREE
2455              
2456             =item MSE_TWO
2457              
2458             =item REG_OKAY
2459              
2460             =item REG_ONE
2461              
2462             =item REG_THREE
2463              
2464             =item REG_TWO
2465              
2466             =item VC
2467              
2468             =item crypto_provide
2469              
2470             =item len
2471              
2472             =back
2473              
2474             =end :podcoverage
2475              
2476             =head1 Notes
2477              
2478             As of version C<0.049_8> of this module, C callbacks are
2479             provided with a language agnostic, numeric reason. So far, this is the
2480             list of possible disconnections:
2481              
2482             =over
2483              
2484             =item DISCONNECT_BY_REMOTE
2485              
2486             The connection closed by remote peer for unknown reasons
2487              
2488             =item DISCONNECT_LOOPBACK
2489              
2490             We connected to ourself according to PeerID.
2491              
2492             =item DISCONNECT_NO_SUCH_TORRENT
2493              
2494             Remote peer attempted to create a session related to a torrent we aren't
2495             currently serving. Occasionally, this will also provide an C
2496             parameter for your callback.
2497              
2498             =item DISCONNECT_HANDSHAKE_INFOHASH
2499              
2500             A remote peer sent us a bad plaintext handshake. This is triggered when,
2501             after a particular infohash was implied in an encrypted handshake, the
2502             remote peer sent us a mismatched infohash in the plaintext handshake.
2503              
2504             =item DISCONNECT_MALFORMED_HANDSHAKE
2505              
2506             Bad plaintext handshake. May be malformed or, if encryption is disabled
2507             locally, the remote peer attempted an encrypted handshake.
2508              
2509             =item DISCONNECT_MALFORMED_PACKET
2510              
2511             This is given when the remote peer gives us a malformed packet. See also
2512             L.
2513              
2514             =item DISCONNECT_PREXISTING
2515              
2516             Already connected to this peer. When there are too many established
2517             connections with a particular peer (as determined by their PeerID), we
2518             disconnect further connections with the reason. This reason provides
2519             the remote peer's C when triggered.
2520              
2521             =item DISCONNECT_TOO_MANY
2522              
2523             Enough peers already! We've hit the hard limit for the number of peers
2524             allowed globally or per torrent.
2525              
2526             =item DISCONNECT_HASHCHECKING
2527              
2528             This reason is given when a remote peer connects to us while the torrent
2529             they're seeking is busy being hash checked (potentially in another
2530             thread).
2531              
2532             =item DISCONNECT_SEED
2533              
2534             This is given when we and the remote peer are both seeds.
2535              
2536             =item DISCONNECT_TIMEOUT_HANDSHAKE
2537              
2538             Peer failed to complete plaintext or encrypted handshake within 30s.
2539              
2540             =item DISCONNECT_USELESS_PEER
2541              
2542             Peer has been connected for at least 3m and is neither interested nor
2543             interesting.
2544              
2545             =item DISCONNECT_HANDSHAKE_SYNC_DH5
2546              
2547             Failed to sync MSE handshake at stage five.
2548              
2549             =begin TODO
2550              
2551             -26 => q[Handed a piece we never asked for]
2552             , # { Index => \d, Offset => \d, Length=> \d }
2553             -28 => q[Sent a reject to a non-existant piece],
2554             -29 => q[Rejected a request we never made.],
2555             -40 => q[Peer is idle],
2556             -101 => q[Bad VC in encrypted handshake],
2557             -103 => q[Bad encrypted header at stage 4],
2558             -104 => q[Bad encrypted handshake (Bad SKEY)],
2559             -105 => q[Unsupported encryption scheme]
2560              
2561             =end TODO
2562              
2563             =back
2564              
2565             To import this list of keywords into your namespace, use the C
2566             tag. Please note that this API tweak is experimental and may change or be
2567             removed in a future version. ...it's also probably incomplete.
2568              
2569             =head1 Author
2570              
2571             Sanko Robinson - http://sankorobinson.com/
2572              
2573             CPAN ID: SANKO
2574              
2575             =head1 License and Legal
2576              
2577             Copyright (C) 2008-2009 by Sanko Robinson Esanko@cpan.orgE
2578              
2579             This program is free software; you can redistribute it and/or modify
2580             it under the terms of The Artistic License 2.0. See the F
2581             file included with this distribution or
2582             http://www.perlfoundation.org/artistic_license_2_0. For
2583             clarification, see http://www.perlfoundation.org/artistic_2_0_notes.
2584              
2585             When separated from the distribution, all POD documentation is covered
2586             by the Creative Commons Attribution-Share Alike 3.0 License. See
2587             http://creativecommons.org/licenses/by-sa/3.0/us/legalcode. For
2588             clarification, see http://creativecommons.org/licenses/by-sa/3.0/us/.
2589              
2590             Neither this module nor the L is affiliated with
2591             BitTorrent, Inc.
2592              
2593             =for svn $Id: Peer.pm 07f0c35 2010-04-02 18:31:29Z sanko@cpan.org $
2594              
2595             =cut