File Coverage

blib/lib/Net/SSH/Perl/Buffer.pm
Criterion Covered Total %
statement 99 112 88.3
branch 13 30 43.3
condition 4 10 40.0
subroutine 27 29 93.1
pod 20 23 86.9
total 163 204 79.9


line stmt bran cond sub pod time code
1             package Net::SSH::Perl::Buffer;
2 8     8   109099 use strict;
  8         25  
  8         234  
3 8     8   43 use warnings;
  8         87  
  8         261  
4              
5 8     8   54 use constant MAX_BIGNUM => 2048;
  8         14  
  8         12136  
6              
7             {
8             my %MP_MAP = (
9             SSH1 => [ "use Math::GMP;", \&_get_mp_int_ssh1, \&_put_mp_int_ssh1 ],
10             SSH2 => [ "1;", \&get_bignum2_bytes, \&put_bignum2_bytes],
11             );
12              
13             sub new {
14 22     22 1 159 my $class = shift;
15 22         63 my $buf = bless { buf => '', offset => 0 }, $class;
16 22         57 my %param = @_;
17 22   100     84 my $mp = $MP_MAP{ $param{MP} || 'SSH1' };
18 22 50       49 die "Unrecognized SSH protocol $param{MP}" unless $mp;
19 22     2   1388 eval $mp->[0];
  2         553  
  2         3763  
  2         10  
20 22         353 $buf->{_get_mp_int} = $mp->[1];
21 22         45 $buf->{_put_mp_int} = $mp->[2];
22 22         84 $buf;
23             }
24             }
25              
26             sub empty {
27 5     5 1 782 my $buf = shift;
28 5         14 $buf->{buf} = "";
29 5         16 $buf->{offset} = 0;
30             }
31              
32             sub append {
33 15     15 1 32 my $buf = shift;
34 15         56 $buf->{buf} .= $_[0];
35             }
36              
37             sub consume {
38 2     2 0 6 my $buf = shift;
39 2         8 my $len = shift;
40 2         5 substr $buf->{buf}, 0, $len, '';
41             }
42              
43             sub bytes {
44 125     125 1 191 my $buf = shift;
45 125         214 my($off, $len, $rep) = @_;
46 125   100     689 $off ||= 0;
47 125 100       230 $len = length $buf->{buf} unless defined $len;
48             return defined $rep ?
49             substr($buf->{buf}, $off, $len, $rep) :
50 125 100       645 substr($buf->{buf}, $off, $len);
51             }
52              
53 55     55 1 165 sub length { length $_[0]->{buf} }
54 20     20 1 52 sub offset { $_[0]->{offset} }
55              
56             sub dump {
57 4     4 1 22 my $buf = shift;
58 4         8 my @r;
59 4         21 for my $c (split //, $buf->bytes(@_)) {
60 4         20 push @r, sprintf "%02x", ord $c;
61             }
62 4         19 join ' ', @r
63             }
64              
65             sub insert_padding {
66 6     6 1 11 my $buf = shift;
67 6         15 my $pad = 8 - ($buf->length + 4 - 8) % 8;
68 6         94 my $junk = join '', map chr rand 128, 0..$pad-1;
69 6         27 $buf->bytes(0, 0, $junk);
70             }
71              
72             sub get_int8 {
73 2     2 1 10 my $buf = shift;
74 2 50       7 my $off = defined $_[0] ? shift : $buf->{offset};
75 2         7 $buf->{offset} += 1;
76 2         10 unpack "c", $buf->bytes($off, 1);
77             }
78              
79             sub put_int8 {
80 7     7 1 16 my $buf = shift;
81 7         41 $buf->{buf} .= pack "c", $_[0];
82             }
83              
84             sub get_int16 {
85 1     2 1 2 my $buf = shift;
86 1 50       6 my $off = defined $_[0] ? shift : $buf->{offset};
87 1         2 $buf->{offset} += 2;
88 1         4 unpack "n", $buf->bytes($off, 2);
89             }
90              
91             sub put_int16 {
92 2     3 1 4 my $buf = shift;
93 2         9 $buf->{buf} .= pack "n", $_[0];
94             }
95              
96             sub get_int32 {
97 21     21 1 44 my $buf = shift;
98 21 50       51 my $off = defined $_[0] ? shift : $buf->{offset};
99 21         30 $buf->{offset} += 4;
100 21         47 unpack "N", $buf->bytes($off, 4);
101             }
102              
103             sub put_int32 {
104 17     17 1 28 my $buf = shift;
105 17         75 $buf->{buf} .= pack "N", $_[0];
106             }
107              
108             sub get_char {
109 16     16 1 24 my $buf = shift;
110 16 50       31 my $off = defined $_[0] ? shift : $buf->{offset};
111 16         23 $buf->{offset}++;
112 16         40 $buf->bytes($off, 1);
113             }
114              
115             sub put_char {
116 7     7 1 13 my $buf = shift;
117 7         21 $buf->{buf} .= $_[0];
118             }
119             *put_chars = \&put_char;
120              
121             sub get_str {
122 16     16 1 69 my $buf = shift;
123 16 50       41 my $off = defined $_[0] ? shift : $buf->{offset};
124 16         38 my $len = $buf->get_int32;
125 16         33 $buf->{offset} += $len;
126 16         396 $buf->bytes($off+4, $len);
127             }
128              
129             sub put_str {
130 6     6 1 529 my $buf = shift;
131 6         11 my $str = shift;
132 6 50       18 $str = "" unless defined $str;
133 6         55 $buf->put_int32(CORE::length($str));
134 6         17 $buf->{buf} .= $str;
135             }
136              
137 1     1 1 10 sub get_mp_int { $_[0]->{_get_mp_int}->(@_) }
138 1     1 1 63 sub put_mp_int { $_[0]->{_put_mp_int}->(@_) }
139              
140             sub _get_mp_int_ssh1 {
141 1     1   2 my $buf = shift;
142 1 50       4 my $off = defined $_[0] ? shift : $buf->{offset};
143 1         3 my $bits = unpack "n", $buf->bytes($off, 2);
144 1         4 my $bytes = int(($bits + 7) / 8);
145 1         4 my $hex = join '', map { sprintf "%02x", ord } split //,
  13         29  
146             $buf->bytes($off+2, $bytes);
147 1         4 $buf->{offset} += 2 + $bytes;
148 1         5 Math::GMP->new("0x$hex");
149             }
150              
151             sub _put_mp_int_ssh1 {
152 1     1   3 my $buf = shift;
153 1         2 my $int = shift;
154 1         5 my $bits = Math::GMP::sizeinbase_gmp($int, 2);
155 1         4 my $hex_size = Math::GMP::sizeinbase_gmp($int, 16);
156 1         5 my $tmp = Math::GMP::get_str_gmp($int, 16);
157 1 50       8 $tmp = "0$tmp" if CORE::length($tmp) % 2;
158 1         7 $tmp =~ s/(..)/ chr hex $1 /ge;
  13         33  
159 1         4 $buf->put_int16($bits);
160 1         2 $buf->put_chars($tmp);
161             }
162              
163             sub put_bignum2_bytes {
164 0     0 0 0 my $buf = shift;
165 0         0 my $num = shift;
166              
167             # skip leading zero bytes
168 0         0 substr($num,0,1,'') while ord(substr($num,0,1)) == 0;
169              
170             # prepend a zero byte if mosting significant bit is set
171             # (to avoid interpretation as a negative number)
172 0 0       0 $num = "\0" . $num
173             if (ord(substr($num,0,1))) & 0x80;
174 0         0 $buf->put_str($num);
175             }
176              
177             sub get_bignum2_bytes {
178 0     0 0 0 my $buf = shift;
179              
180 0         0 my $num = $buf->get_str;
181 0         0 my $len = CORE::length($num);
182 0 0       0 return unless $len;
183             # refuse negative (MSB set) bignums
184 0 0       0 return if ord(substr($num,0,1)) & 0x80;
185             # refuse overlong bignums, allow prepended \0 to avoid MSB set
186 0 0 0     0 return if $len > MAX_BIGNUM+1 ||
      0        
187             ($len == MAX_BIGNUM+1 &&
188             ord(substr($num,0,1)) != 0);
189             # trim leading zero bytes
190 0         0 substr($num,0,1,'') while ord(substr($num,0,1)) == 0;
191 0         0 return $num;
192             }
193              
194             1;
195             __END__