File Coverage

lib/Noise/Stream.pm
Criterion Covered Total %
statement 17 17 100.0
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 23 23 100.0


line stmt bran cond sub pod time code
1 1     1   238815 use v5.42.0;
  1         4  
2 1     1   7 use feature 'class';
  1         2  
  1         208  
3 1     1   10 no warnings 'experimental::class';
  1         8  
  1         124  
4             #
5             class Noise::Stream v0.0.1 {
6 1     1   530 use Noise::CipherState;
  1         3  
  1         47  
7 1     1   736 use IO::Socket::INET;
  1         30083  
  1         8  
8 1     1   603 use Errno qw[EAGAIN EWOULDBLOCK];
  1         2  
  1         1763  
9             #
10             field $socket : param : reader;
11             field $c_send : param; # Noise::CipherState
12             field $c_recv : param; # Noise::CipherState
13             field $raw_recv_buffer = '';
14             field $decrypted_buffer = '';
15             field $initial_buffer : param //= $raw_recv_buffer;
16             #
17             ADJUST {
18             $socket->blocking(0);
19             }
20              
21             method write_bin ($data) {
22             my $payload = $c_send->encrypt_with_ad( '', $data );
23             my $prefixed = pack( 'n', length($payload) ) . $payload;
24             my $written = 0;
25             while ( $written < length($prefixed) ) {
26             my $res = syswrite( $socket, $prefixed, length($prefixed) - $written, $written );
27             if ( defined $res ) { $written += $res; }
28             elsif ( $! != 11 && $! != 10035 ) { die "SecureStream write error: $!"; }
29             }
30             }
31              
32             method _try_read_frame () {
33             if ( length($raw_recv_buffer) < 2 ) {
34             my $bytes_read = sysread( $socket, my $buf, 2 - length($raw_recv_buffer) );
35             if ( !defined $bytes_read ) {
36             return 0 if $! == EAGAIN || $! == EWOULDBLOCK || $! == 10035;
37             warn "SecureStream read error (prefix): $!";
38             return 0;
39             }
40             return 0 if $bytes_read == 0; # EOF
41             $raw_recv_buffer .= $buf;
42             }
43             return 0 if length($raw_recv_buffer) < 2;
44             my $len = unpack( 'n', substr( $raw_recv_buffer, 0, 2 ) );
45             if ( length($raw_recv_buffer) < ( $len + 2 ) ) {
46             my $to_read = ( $len + 2 ) - length($raw_recv_buffer);
47             my $bytes_read = sysread( $socket, my $buf, $to_read );
48             if ( !defined $bytes_read ) {
49             return 0 if $! == EAGAIN || $! == EWOULDBLOCK || $! == 10035;
50             warn "SecureStream read error (payload): $!";
51             return 0;
52             }
53             return 0 if $bytes_read == 0; # EOF
54             $raw_recv_buffer .= $buf;
55             }
56             if ( length($raw_recv_buffer) >= ( $len + 2 ) ) {
57             my $frame = substr( $raw_recv_buffer, 2, $len );
58             substr( $raw_recv_buffer, 0, $len + 2, '' );
59              
60             # CipherState handles tag extraction and nonce increment
61             my $pt = $c_recv->decrypt_with_ad( '', $frame );
62             $decrypted_buffer .= $pt;
63             return 1;
64             }
65             return 0;
66             }
67              
68             method read_bin ( $len = undef ) {
69             $self->_try_read_frame();
70             if ( defined $len ) {
71             return undef if length($decrypted_buffer) < $len;
72             my $res = substr( $decrypted_buffer, 0, $len );
73             substr( $decrypted_buffer, 0, $len, '' );
74             return $res;
75             }
76             my $res = $decrypted_buffer;
77             $decrypted_buffer = '';
78             return $res;
79             }
80             method rekey_send () { $c_send->rekey() }
81             method rekey_recv () { $c_recv->rekey() }
82             method close () { $socket->close() }
83             };
84             #
85             1;