File Coverage

lib/Net/BitTorrent/DHT/Security.pm
Criterion Covered Total %
statement 16 16 100.0
branch 3 4 75.0
condition n/a
subroutine 5 5 100.0
pod n/a
total 24 25 96.0


line stmt bran cond sub pod time code
1 33     33   501346 use v5.40;
  33         151  
2 33     33   203 use feature 'class';
  33         64  
  33         4355  
3 33     33   207 no warnings 'experimental::class';
  33         73  
  33         9802  
4             #
5             my @CRC32C_TABLE;
6              
7             sub _init_table {
8 33 50   33   195 return if @CRC32C_TABLE;
9 33         198 for my $i ( 0 .. 255 ) {
10 8448         9968 my $res = $i;
11 8448 100       56971 $res = ( $res & 1 ) ? ( $res >> 1 ) ^ 0x82F63B78 : ( $res >> 1 ) for 1 .. 8;
12 8448         13750 $CRC32C_TABLE[$i] = $res & 0xFFFFFFFF;
13             }
14             }
15             #
16             _init_table();
17             #
18             class Net::BitTorrent::DHT::Security v2.0.6 {
19 33     33   15768 use Socket qw[inet_aton inet_pton AF_INET AF_INET6];
  33         79416  
  33         30313  
20              
21             method _crc32c ($data) {
22             my $crc = 0xFFFFFFFF;
23             $crc = ( $crc >> 8 ) ^ $CRC32C_TABLE[ ( $crc ^ $_ ) & 0xFF ] for unpack 'C*', $data;
24             return ( $crc ^ 0xFFFFFFFF ) & 0xFFFFFFFF;
25             }
26              
27             method generate_node_id ( $ip, $seed //= undef ) {
28             $seed //= int rand 256;
29             my $ip_bin;
30             my @v4_mask = ( 0x03, 0x0f, 0x3f, 0xff );
31             my @v6_mask = ( 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff );
32             my $ip_masked = '';
33             if ( $ip !~ /:/ ) {
34             $ip_bin = inet_aton($ip);
35             my @bytes = unpack( 'C*', $ip_bin );
36             $bytes[$_] &= $v4_mask[$_] for 0 .. 3;
37             $ip_masked = pack( 'C*', @bytes );
38             }
39             else {
40             $ip_bin = inet_pton( AF_INET6, $ip );
41             my @bytes = unpack( 'C*', $ip_bin );
42             $bytes[$_] &= $v6_mask[$_] for 0 .. 7;
43             $ip_masked = pack 'C*', @bytes[ 0 .. 7 ];
44             }
45             my $input = $ip_masked . chr( $seed & 0x07 );
46             my $crc = $self->_crc32c($input);
47             my @id;
48             $id[0] = ( $crc >> 24 ) & 0xFF;
49             $id[1] = ( $crc >> 16 ) & 0xFF;
50             $id[2] = ( ( $crc >> 8 ) & 0xF8 ) | ( int( rand(256) ) & 0x07 );
51             $id[$_] = int rand(256) for 3 .. 18;
52             $id[19] = $seed & 0xFF;
53             pack 'C*', @id;
54             }
55              
56             method validate_node_id ( $id_bin, $ip ) {
57             return 1 if !$ip; # Can't validate without IP
58             my $seed = unpack 'C', substr $id_bin, 19, 1;
59             my $expected_id = $self->generate_node_id( $ip, $seed );
60              
61             # Compare first 21 bits
62             my @id = unpack( 'C*', $id_bin );
63             my @exp = unpack( 'C*', $expected_id );
64             return 0 if $id[0] != $exp[0];
65             return 0 if $id[1] != $exp[1];
66             return 0 if ( $id[2] & 0xF8 ) != ( $exp[2] & 0xF8 );
67             return 1;
68             }
69             };
70             #
71             1;