File Coverage

blib/lib/Convert/ASN1/IO.pm
Criterion Covered Total %
statement 90 125 72.0
branch 46 94 48.9
condition 9 13 69.2
subroutine 9 9 100.0
pod 6 6 100.0
total 160 247 64.7


line stmt bran cond sub pod time code
1             # Copyright (c) 2000-2005 Graham Barr . All rights reserved.
2             # This program is free software; you can redistribute it and/or
3             # modify it under the same terms as Perl itself.
4              
5             package Convert::ASN1;
6             $Convert::ASN1::VERSION = '0.34';
7 23     23   163 use strict;
  23         41  
  23         707  
8 23     23   15259 use Socket;
  23         87252  
  23         9935  
9              
10             BEGIN {
11 23     23   124 local $SIG{__DIE__};
12 23 50       47 eval { require bytes } and 'bytes'->import
  23         309  
13             }
14              
15             sub asn_recv { # $socket, $buffer, $flags
16              
17 1     1 1 13 my $peer;
18             my $buf;
19 1         2 my $n = 128;
20 1         1 my $pos = 0;
21 1         4 my $depth = 0;
22 1         2 my $len = 0;
23 1         2 my($tmp,$tb,$lb);
24              
25             MORE:
26 1         22 for(
27             $peer = recv($_[0],$buf,$n,MSG_PEEK);
28             defined $peer;
29             $peer = recv($_[0],$buf,$n<<=1,MSG_PEEK)
30             ) {
31              
32 1 50       5 if ($depth) { # Are we searching of "\0\0"
33              
34 0 0       0 unless (2+$pos <= length $buf) {
35 0 0       0 next MORE if $n == length $buf;
36 0         0 last MORE;
37             }
38              
39 0 0       0 if(substr($buf,$pos,2) eq "\0\0") {
40 0 0       0 unless (--$depth) {
41 0         0 $len = $pos + 2;
42 0         0 last MORE;
43             }
44             }
45             }
46              
47             # If we can decode a tag and length we can determine the length
48 1         6 ($tb,$tmp) = asn_decode_tag(substr($buf,$pos));
49 1 50 33     6 unless ($tb || $pos+$tb < length $buf) {
50 0 0       0 next MORE if $n == length $buf;
51 0         0 last MORE;
52             }
53              
54 1 50       5 if (unpack("C",substr($buf,$pos+$tb,1)) == 0x80) {
55             # indefinite length, grrr!
56 0         0 $depth++;
57 0         0 $pos += $tb + 1;
58 0         0 redo MORE;
59             }
60              
61 1         4 ($lb,$len) = asn_decode_length(substr($buf,$pos+$tb));
62              
63 1 50       4 if ($lb) {
64 1 50       3 if ($depth) {
65 0         0 $pos += $tb + $lb + $len;
66 0         0 redo MORE;
67             }
68             else {
69 1         3 $len += $tb + $lb + $pos;
70 1         2 last MORE;
71             }
72             }
73             }
74              
75 1 50       12 if (defined $peer) {
76 1 50       6 if ($len > length $buf) {
    50          
77             # Check we can read the whole element
78             goto error
79 0 0       0 unless defined($peer = recv($_[0],$buf,$len,MSG_PEEK));
80              
81 0 0       0 if ($len > length $buf) {
82             # Cannot get whole element
83 0         0 $_[1]='';
84 0         0 return $peer;
85             }
86             }
87             elsif ($len == 0) {
88 0         0 $_[1] = '';
89 0         0 return $peer;
90             }
91              
92 1 50       25 if ($_[2] & MSG_PEEK) {
    50          
93 0         0 $_[1] = substr($buf,0,$len);
94             }
95             elsif (!defined($peer = recv($_[0],$_[1],$len,0))) {
96 0         0 goto error;
97             }
98              
99 1         5 return $peer;
100             }
101              
102             error:
103 0         0 $_[1] = undef;
104             }
105              
106             sub asn_read { # $fh, $buffer, $offset
107              
108             # We need to read one packet, and exactly only one packet.
109             # So we have to read the first few bytes one at a time, until
110             # we have enough to decode a tag and a length. We then know
111             # how many more bytes to read
112              
113 2 50   2 1 205 if ($_[2]) {
114 0 0       0 if ($_[2] > length $_[1]) {
115 0         0 require Carp;
116 0         0 Carp::carp("Offset beyond end of buffer");
117 0         0 return;
118             }
119 0         0 substr($_[1],$_[2]) = '';
120             }
121             else {
122 2         6 $_[1] = '';
123             }
124              
125 2         4 my $pos = 0;
126 2         5 my $need = 0;
127 2         3 my $depth = 0;
128 2         5 my $ch;
129             my $n;
130 2         0 my $e;
131            
132              
133 2         4 while(1) {
134 266   100     474 $need = ($pos + ($depth * 2)) || 2;
135              
136 266         442 while(($n = $need - length $_[1]) > 0) {
137 196 50       1635 $e = sysread($_[0],$_[1],$n,length $_[1]) or
138             goto READ_ERR;
139             }
140              
141 266         710 my $tch = unpack("C",substr($_[1],$pos++,1));
142             # Tag may be multi-byte
143 266 100       516 if(($tch & 0x1f) == 0x1f) {
144 175         205 my $ch;
145 175         201 do {
146 273         334 $need++;
147 273         436 while(($n = $need - length $_[1]) > 0) {
148 273 50       2113 $e = sysread($_[0],$_[1],$n,length $_[1]) or
149             goto READ_ERR;
150             }
151 273         1031 $ch = unpack("C",substr($_[1],$pos++,1));
152             } while($ch & 0x80);
153             }
154              
155 266         384 $need = $pos + 1;
156              
157 266         472 while(($n = $need - length $_[1]) > 0) {
158 0 0       0 $e = sysread($_[0],$_[1],$n,length $_[1]) or
159             goto READ_ERR;
160             }
161              
162 266         452 my $len = unpack("C",substr($_[1],$pos++,1));
163              
164 266 100 66     653 if($len & 0x80) {
    100          
165 71 50       152 unless ($len &= 0x7f) {
166 71         90 $depth++;
167 71         98 next;
168             }
169 0         0 $need = $pos + $len;
170              
171 0         0 while(($n = $need - length $_[1]) > 0) {
172 0 0       0 $e = sysread($_[0],$_[1],$n,length $_[1]) or
173             goto READ_ERR;
174             }
175              
176 0         0 $pos += $len + unpack("N", "\0" x (4 - $len) . substr($_[1],$pos,$len));
177             }
178             elsif (!$len && !$tch) {
179 71 50       116 die "Bad ASN PDU" unless $depth;
180 71 100       108 unless (--$depth) {
181 1         15 last;
182             }
183             }
184             else {
185 124         182 $pos += $len;
186             }
187              
188 194 100       314 last unless $depth;
189             }
190              
191 2         11 while(($n = $pos - length $_[1]) > 0) {
192 1 50       21 $e = sysread($_[0],$_[1],$n,length $_[1]) or
193             goto READ_ERR;
194             }
195              
196 2         8 return length $_[1];
197              
198 0 0       0 READ_ERR:
199             $@ = defined($e) ? "Unexpected EOF" : "I/O Error $!"; # . CORE::unpack("H*",$_[1]);
200 0         0 return undef;
201             }
202              
203             sub asn_send { # $sock, $buffer, $flags, $to
204              
205 1 50   1 1 851 @_ == 4
206             ? send($_[0],$_[1],$_[2],$_[3])
207             : send($_[0],$_[1],$_[2]);
208             }
209              
210             sub asn_write { # $sock, $buffer
211              
212 2     2 1 199 syswrite($_[0],$_[1], length $_[1]);
213             }
214              
215             sub asn_get { # $fh
216              
217 2 50   2 1 61 my $fh = ref($_[0]) ? $_[0] : \($_[0]);
218 2         5 my $href = \%{*$fh};
  2         6  
219              
220 2 100       8 $href->{'asn_buffer'} = '' unless exists $href->{'asn_buffer'};
221              
222 2   100     9 my $need = delete $href->{'asn_need'} || 0;
223 2         4 while(1) {
224 3 100       10 next if $need;
225 2 100       8 my($tb,$tag) = asn_decode_tag($href->{'asn_buffer'}) or next;
226 1 50       8 my($lb,$len) = asn_decode_length(substr($href->{'asn_buffer'},$tb,8)) or next;
227 1         4 $need = $tb + $lb + $len;
228             }
229             continue {
230 3 100 66     13 if ($need && $need <= length $href->{'asn_buffer'}) {
231 2         15 my $ret = substr($href->{'asn_buffer'},0,$need);
232 2         5 substr($href->{'asn_buffer'},0,$need) = '';
233 2         7 return $ret;
234             }
235              
236 1 50       4 my $get = $need > 1024 ? $need : 1024;
237              
238 1 50       23 sysread($_[0], $href->{'asn_buffer'}, $get, length $href->{'asn_buffer'})
239             or return undef;
240             }
241             }
242              
243             sub asn_ready { # $fh
244              
245 2 50   2 1 18 my $fh = ref($_[0]) ? $_[0] : \($_[0]);
246 2         4 my $href = \%{*$fh};
  2         6  
247              
248 2 50       6 return 0 unless exists $href->{'asn_buffer'};
249            
250             return $href->{'asn_need'} <= length $href->{'asn_buffer'}
251 2 50       7 if exists $href->{'asn_need'};
252              
253 2 100       5 my($tb,$tag) = asn_decode_tag($href->{'asn_buffer'}) or return 0;
254 1 50       4 my($lb,$len) = asn_decode_length(substr($href->{'asn_buffer'},$tb,8)) or return 0;
255              
256 1         4 $href->{'asn_need'} = $tb + $lb + $len;
257              
258 1         4 $href->{'asn_need'} <= length $href->{'asn_buffer'};
259             }
260              
261             1;