File Coverage

blib/lib/Net/Radius/Packet.pm
Criterion Covered Total %
statement 225 319 70.5
branch 57 106 53.7
condition 15 37 40.5
subroutine 49 93 52.6
pod 27 34 79.4
total 373 589 63.3


line stmt bran cond sub pod time code
1             package Net::Radius::Packet;
2              
3 8     8   330182 use strict;
  8         20  
  8         420  
4             require Exporter;
5 8     8   60 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $VSA);
  8         15  
  8         1138  
6             @ISA = qw(Exporter);
7             @EXPORT = qw(auth_resp auth_acct_verify auth_req_verify);
8             @EXPORT_OK = qw( );
9              
10             $VERSION = '1.55';
11              
12             $VSA = 26; # Type assigned in RFC2138 to the
13             # Vendor-Specific Attributes
14              
15             # Be sure our dictionaries are current
16 8     8   4520 use Net::Radius::Dictionary 1.50;
  8         268  
  8         247  
17 8     8   59 use Carp;
  8         16  
  8         802  
18 8     8   10652 use Socket;
  8         47493  
  8         7631  
19 8     8   85 use Digest::MD5;
  8         16  
  8         51247  
20              
21             my (%unkvprinted, %unkgprinted);
22              
23             sub new {
24 27     27 1 41071 my ($class, $dict, $data) = @_;
25 27         99 my $self = { unknown_entries => 1 };
26 27         163 bless $self, $class;
27 27 100       156 $self->set_dict($dict) if defined($dict);
28 27 100       131 $self->unpack($data) if defined($data);
29 27         129 return $self;
30             }
31              
32             # Set the dictionary
33             sub set_dict {
34 27     27 1 880 my ($self, $dict) = @_;
35 27         122 $self->{Dict} = $dict;
36             }
37              
38             # Functions for accessing data structures
39 7     7 1 628 sub code { $_[0]->{Code}; }
40 21     21 1 5849 sub identifier { $_[0]->{Identifier}; }
41 26     26 1 5338 sub authenticator { $_[0]->{Authenticator}; }
42              
43 27     27 1 666 sub set_code { $_[0]->{Code} = $_[1]; }
44 26     26 1 4027 sub set_identifier { $_[0]->{Identifier} = $_[1]; }
45 282     282 1 167389 sub set_authenticator { $_[0]->{Authenticator} = substr($_[1]
46             . "\x0" x 16,
47             0, 16); }
48              
49 262     262 0 227 sub vendors { keys %{$_[0]->{VSAttributes}}; }
  262         960  
50 2     2 0 9 sub vsattributes { keys %{$_[0]->{VSAttributes}->{$_[1]}}; }
  2         9  
51 9     9 0 65 sub vsattr { $_[0]->{VSAttributes}->{$_[1]}->{$_[2]}; }
52             sub set_vsattr {
53 43     43 1 115 my ($self, $vendor, $name, $value, $rewrite_flag) = @_;
54 43 100       138 $self->{VSAttributes}->{$vendor} = {} unless exists($self->{VSAttributes}->{$vendor});
55 43         70 my $attr = $self->{VSAttributes}->{$vendor};
56              
57 43 50       77 if ($rewrite_flag) {
58 0         0 my $found = 0;
59              
60 0 0       0 if (exists($attr->{$name})) {
61 0         0 $found = $#{$attr->{$name}} + 1;
  0         0  
62             }
63              
64 0 0       0 if ($found == 1) {
65 0         0 $attr->{$name}[0] = $value;
66 0         0 return;
67             }
68             }
69              
70 43         46 push @{$attr->{$name}}, $value;
  43         175  
71             }
72              
73             sub unset_vsattr {
74 0     0 1 0 my ($self, $vendor, $name) = @_;
75              
76 0         0 delete($self->{VSAttributes}->{$name});
77             }
78              
79 0     0 1 0 sub show_unknown_entries { $_[0]->{unknown_entries} = $_[1]; }
80              
81             sub set_attr
82             {
83 138     138 1 335 my ($self, $name, $value, $rewrite_flag) = @_;
84 138         145 my ($push, $pos );
85              
86 138 100       280 $push = 1 unless $rewrite_flag;
87              
88 138 100       262 if ($rewrite_flag) {
89 3         6 my $found = 0;
90 3         10 my @attr = $self->_attributes;
91              
92 3         21 for (my $i = 0; $i <= $#attr; $i++ ) {
93 11 100       35 if ($attr[$i][0] eq $name) {
94 3         3 $found++;
95 3         8 $pos = $i;
96             }
97             }
98              
99 3 100       21 if ($found > 1) {
    100          
100 1         5 $push = 1;
101             } elsif ($found) {
102 1         3 $attr[$pos][0] = $name;
103 1         3 $attr[$pos][1] = $value;
104 1         4 $self->_set_attributes( \@attr );
105 1         4 return;
106             } else {
107 1         3 $push = 1;
108             }
109             }
110              
111 137 50       403 $self->_push_attr( $name, $value ) if $push;
112             }
113              
114             sub attr
115             {
116 15     15 1 34 my ($self, $name ) = @_;
117            
118 15         34 my @attr = $self->_attributes;
119            
120 15         565 for (my $i = $#attr; $i >= 0; $i-- ) {
121 17 100       98 return $attr[$i][1] if $attr[$i][0] eq $name;
122             }
123 3         58 return;
124             }
125              
126             sub attributes {
127 0     0 0 0 my ($self) = @_;
128            
129 0         0 my @attr = $self->_attributes;
130 0         0 my @attriblist = ();
131 0         0 for (my $i = $#attr; $i >= 0; $i-- ) {
132 0         0 push @attriblist, $attr[$i][0];
133             }
134 0         0 return @attriblist;
135             }
136              
137             sub unset_attr
138             {
139 3     3 1 7 my ($self, $name, $value ) = @_;
140            
141 3         3 my $found;
142 3         8 my @attr = $self->_attributes;
143              
144 3         11 for (my $i = 0; $i <= $#attr; $i++ ) {
145 3 50 33     22 if ( $name eq $attr[$i][0] && $value eq $attr[$i][1])
146             {
147 3         4 $found = 1;
148 3 50       9 if ( $#attr == 0 ) {
149             # no more attributes left on the stack
150 3         13 $self->_set_attributes( [ ] );
151             } else {
152 0         0 splice @attr, $i, 1;
153 0         0 $self->_set_attributes( \@attr );
154             }
155 3         10 return 1;
156             }
157             }
158 0         0 return 0;
159             }
160              
161             # XXX - attr_slot is deprecated - Use attr_slot_* instead
162 0     0 1 0 sub attr_slot { attr_slot_val($@); }
163              
164 824     824 1 10692 sub attr_slots { scalar ($_[0]->_attributes); }
165              
166             sub attr_slot_name
167             {
168 567     567 1 1181 my $self = shift;
169 567         550 my $slot = shift;
170 567         1073 my @stack = $self->_attributes;
171              
172 567 100       1243 return unless exists $stack[$slot];
173 554 50       1023 return unless exists $stack[$slot]->[0];
174 554         1735 $stack[$slot]->[0];
175             }
176              
177             sub attr_slot_val
178             {
179 567     567 1 11297 my $self = shift;
180 567         645 my $slot = shift;
181 567         907 my @stack = $self->_attributes;
182              
183 567 100       1158 return unless exists $stack[$slot];
184 554 50       948 return unless exists $stack[$slot]->[0];
185 554         1393 $stack[$slot]->[1];
186             }
187              
188             sub unset_attr_slot {
189 6     6 1 11 my ($self, $position ) = @_;
190              
191 6         17 my @attr = $self->_attributes;
192              
193 6 100       22 if ( not $position > $#attr ) {
194 4         11 splice @attr, $position, 1;
195 4         21 $self->_set_attributes( \@attr );
196 4         13 return 1;
197             } else {
198 2         5 return;
199             }
200              
201             }
202              
203             # 'Attributes' is now array of arrays, so that we can have multiple
204             # Proxy-State values in the order in which they were added,
205             # as specified in RFC 2865
206 1985 100   1985   1784 sub _attributes { @{ $_[0]->{Attributes} || [] }; }
  1985         10048  
207 8     8   22 sub _set_attributes { $_[0]->{Attributes} = $_[1]; }
208 137     137   149 sub _push_attr { push @{ $_[0]->{Attributes} }, [ $_[1], $_[2] ]; }
  137         677  
209              
210             # Decode the password
211             sub password {
212 2     2 1 1113 my ($self, $secret, $attr) = @_;
213 2         7 my $lastround = $self->authenticator;
214 2   50     23 my $pwdin = $self->attr($attr || "User-Password");
215 2         4 my $pwdout = ""; # avoid possible undef warning
216 2         7 for (my $i = 0; $i < length($pwdin); $i += 16) {
217 2         14 $pwdout .= substr($pwdin, $i, 16) ^ Digest::MD5::md5($secret . $lastround);
218 2         8 $lastround = substr($pwdin, $i, 16);
219             }
220 2 50       16 $pwdout =~ s/\000*$// if $pwdout;
221 2 50       6 substr($pwdout,length($pwdin)) = ""
222             unless length($pwdout) <= length($pwdin);
223 2         47 return $pwdout;
224             }
225              
226             # Encode the password
227             sub set_password {
228 1     1 1 5 my ($self, $pwdin, $secret, $attribute) = @_;
229 1   50     23 $attribute ||= 'User-Password';
230 1         5 my $lastround = $self->authenticator;
231 1         2 my $pwdout = ""; # avoid possible undef warning
232 1         6 $pwdin .= "\000" x (15-(15 + length $pwdin)%16); # pad to 16n bytes
233              
234 1         4 for (my $i = 0; $i < length($pwdin); $i += 16) {
235 1         24 $lastround = substr($pwdin, $i, 16)
236             ^ Digest::MD5::md5($secret . $lastround);
237 1         5 $pwdout .= $lastround;
238             }
239 1         3 $self->set_attr($attribute => $pwdout, 1);
240             }
241              
242             # Set response authenticator in binary packet
243             sub auth_resp {
244 2     2 1 5 my $new = $_[0];
245 2 50       10 if ($_[2])
246 0         0 { substr($new, 4, 16) = Digest::MD5::md5(substr($_[0], 0, 4) .
247             "\x0" x 16 . substr($_[0], 20) .
248             $_[1]); }
249 2         27 else { substr($new, 4, 16) = Digest::MD5::md5($_[0] . $_[1]); }
250 2         9 return $new;
251             }
252              
253             # Verify the authenticator in a packet
254 2     2 1 14 sub auth_acct_verify { auth_req_verify(@_, "\x0" x 16); }
255             sub auth_req_verify
256             {
257 4     4 1 12 my ($packet, $secret, $prauth) = @_;
258              
259 4 100       54 return 1 if Digest::MD5::md5(substr($packet, 0, 4) . $prauth
260             . substr($packet, 20) . $secret)
261             eq substr($packet, 4, 16);
262 2         13 return;
263             }
264              
265             # Utility functions for printing/debugging
266 1280 100   1280 0 3795 sub pdef { defined $_[0] ? $_[0] : "UNDEF"; }
267             sub pclean {
268 768     768 0 866 my $str = $_[0];
269 768         1185 $str =~ s/([\044-\051\133-\136\140\173-\175])/'\\' . $1/ge;
  15         68  
270 768         1583 $str =~ s/([\000-\037\177-\377])/sprintf('\x{%x}', ord($1))/ge;
  2721         9529  
271 768         3203 return $str;
272             }
273              
274             sub dump {
275 0     0 1 0 print str_dump(@_);
276             }
277              
278             sub str_dump {
279 256     256 0 1007 my $self = shift;
280 256         335 my $ret = '';
281 256         814 $ret .= "*** DUMP OF RADIUS PACKET ($self)\n";
282 256         600 $ret .= "Code: ". pdef($self->{Code}). "\n";
283 256         706 $ret .= "Identifier: ". pdef($self->{Identifier}). "\n";
284 256         586 $ret .= "Authentic: ". pclean(pdef($self->{Authenticator})). "\n";
285 256         495 $ret .= "Attributes:\n";
286              
287 256         672 for (my $i = 0; $i < $self->attr_slots; ++$i)
288             {
289 512         964 $ret .= sprintf(" %-20s %s\n", $self->attr_slot_name($i) . ":" ,
290             pclean(pdef($self->attr_slot_val($i))));
291             }
292              
293 256         493 foreach my $vendor ($self->vendors) {
294 0         0 $ret .= "VSA for vendor $vendor\n";
295 0         0 foreach my $attr ($self->vsattributes($vendor)) {
296 0         0 $ret .= sprintf(" %-20s %s\n", $attr . ":" ,
297 0         0 pclean(join("|", @{$self->vsattr($vendor, $attr)})));
298             }
299             }
300 256         375 $ret .= "*** END DUMP\n";
301 256         796 return $ret;
302             }
303              
304             sub pack {
305 6     6 1 26 my $self = shift;
306 6         10 my $hdrlen = 1 + 1 + 2 + 16; # Size of packet header
307 6         14 my $p_hdr = "C C n a16 a*"; # Pack template for header
308 6         10 my $p_attr = "C C a*"; # Pack template for attribute
309 6         16 my $p_vsa = "C C N C C a*"; # VSA
310              
311             # XXX - The spec says that a
312             # 'Vendor-Type' must be included
313             # but there are no documented definitions
314             # for this! We'll simply skip this value
315              
316 6         12 my $p_vsa_3com = "C C N N a*";
317              
318 6         36 my %codes = $self->{Dict}->packet_numbers();
319 6         33 my $attstr = ""; # To hold attribute structure
320             # Define a hash of subroutine references to pack the various data types
321             my %packer = (
322 0     0   0 "octets" => sub { return $_[0]; },
323 13     13   23 "string" => sub { return $_[0]; },
324 0     0   0 "ipv6addr" => sub { return $_[0]; },
325 0     0   0 "date" => sub { return $_[0]; },
326 0     0   0 "ifid" => sub { return $_[0]; },
327             "integer" => sub {
328 10 100 66 10   37 return pack "N",
329             (
330             defined $self->{Dict}->attr_has_val($_[1]) &&
331             defined $self->{Dict}->val_num(@_[1, 0])
332             )
333             ? $self->{Dict}->val_num(@_[1, 0])
334             : $_[0];
335             },
336             "ipaddr" => sub {
337 2     2   26 return inet_aton($_[0]);
338             },
339             "time" => sub {
340 0     0   0 return pack "N", $_[0];
341             },
342             "date" => sub {
343 0     0   0 return pack "N", $_[0];
344             },
345             "tagged-string" => sub {
346 0     0   0 return $_[0];
347             },
348             "tagged-integer" => sub {
349 0     0   0 return $_[0];
350             },
351             "tagged-ipaddr" => sub {
352 0     0   0 my ($tag,$val)=unpack "C a*",$_[0];
353 0         0 return pack "C N" , $tag , inet_aton($val);
354 6         602 });
355              
356             my %vsapacker = (
357 0     0   0 "octets" => sub { return $_[0]; },
358 2     2   4 "string" => sub { return $_[0]; },
359 0     0   0 "ipv6addr" => sub { return $_[0]; },
360 0     0   0 "date" => sub { return $_[0]; },
361 0     0   0 "ifid" => sub { return $_[0]; },
362             "integer" => sub {
363 1   33 1   4 my $vid = $self->{Dict}->vendor_num($_[2]) || $_[2];
364 1 50 33     6 return pack "N",
365             (defined $self->{Dict}->vsattr_has_val($vid, $_[1])
366             && defined $self->{Dict}->vsaval_num($vid, @_[1, 0])
367             ) ? $self->{Dict}->vsaval_num($vid, @_[1, 0]) : $_[0];
368             },
369             "ipaddr" => sub {
370 0     0   0 return inet_aton($_[0]);
371             },
372             "time" => sub {
373 0     0   0 return pack "N", $_[0];
374             },
375             "date" => sub {
376 0     0   0 return pack "N", $_[0];
377             },
378             "tagged-string" => sub {
379 0     0   0 return $_[0];
380             },
381             "tagged-integer" => sub {
382 0     0   0 return $_[0];
383             },
384             "tagged-ipaddr" => sub {
385 0     0   0 my ($tag,$val)=unpack "C a*",$_[0];
386 0         0 return pack "C a*" , $tag , inet_aton($val);
387 6         211 });
388            
389             # Pack the attributes
390 6         52 for (my $i = 0; $i < $self->attr_slots; ++$i)
391             {
392 25         53 my $attr = $self->attr_slot_name($i);
393 25 50       86 if (! defined $self->{Dict}->attr_num($attr))
394             {
395 0 0       0 carp("Unknown RADIUS tuple $attr => " . $self->attr_slot_val($i)
396             . "\n")
397             if ($self->{unknown_entries});
398 0         0 next;
399             }
400            
401 25 50       84 next unless ref($packer{$self->{Dict}->attr_type($attr)}) eq 'CODE';
402              
403 25         55 my $val = &{$packer{$self->{Dict}->attr_type($attr)}}
  25         65  
404             ($self->attr_slot_val($i), $self->{Dict} ->attr_num($attr));
405              
406 25         88 $attstr .= pack $p_attr, $self->{Dict}->attr_num($attr),
407             length($val)+2, $val;
408             }
409              
410             # Pack the Vendor-Specific Attributes
411              
412 6         23 foreach my $vendor ($self->vendors)
413             {
414 2   66     8 my $vid = $self->{Dict}->vendor_num($vendor) || $vendor;
415 2         6 foreach my $attr ($self->vsattributes($vendor)) {
416 3 50       11 next unless ref($vsapacker{$self->{Dict}
417             ->vsattr_type($vid, $attr)})
418             eq 'CODE';
419 3         4 foreach my $datum (@{$self->vsattr($vendor, $attr)}) {
  3         7  
420 3         11 my $vval = &{$vsapacker{$self->{'Dict'}->vsattr_type($vid, $attr)}}
  3         10  
421             ($datum, $self->{'Dict'}->vsattr_num($vid, $attr), $vendor);
422            
423 3 50       10 if ($vid == 429) {
424              
425             # As pointed out by Quan Choi,
426             # we need special code to handle the
427             # 3Com case - See RFC-2882, sec 2.3.1
428              
429 0         0 $attstr .= pack $p_vsa_3com, 26,
430             length($vval) + 10, $vid,
431             $self->{'Dict'}->vsattr_num($vid, $attr),
432             $vval;
433             }
434             else
435             {
436 3         11 $attstr .= pack $p_vsa, 26, length($vval) + 8, $vid,
437             $self->{'Dict'}->vsattr_num($vid, $attr),
438             length($vval) + 2, $vval;
439             }
440             }
441             }
442             }
443              
444             # Prepend the header and return the complete binary packet
445 6         27 return pack $p_hdr, $codes{$self->code}, $self->identifier,
446             length($attstr) + $hdrlen, $self->authenticator,
447             $attstr;
448             }
449              
450             sub unpack {
451 18     18 1 36 my ($self, $data) = @_;
452 18         37 my $dict = $self->{Dict};
453 18         34 my $p_hdr = "C C n a16 a*"; # Pack template for header
454 18         38 my $p_attr = "C C a*"; # Pack template for attribute
455 18         24 my $p_taggedattr = "C C C a*"; # Pack template for tagged-attribute
456 18         86 my %rcodes = $dict->packet_names();
457              
458             # Decode the header
459 18         228 my ($code, $id, $len, $auth, $attrdat) = unpack $p_hdr, $data;
460              
461             # Generate a skeleton data structure to be filled in
462 18         98 $self->set_code($rcodes{$code});
463 18         64 $self->set_identifier($id);
464 18         65 $self->set_authenticator($auth);
465              
466             # Functions for the various data types
467             my %unpacker =
468             (
469             "string" => sub {
470 49     49   87 return $_[0];
471             },
472 0     0   0 "ipv6addr" => sub { return $_[0]; },
473 0     0   0 "date" => sub { return $_[0]; },
474 0     0   0 "ifid" => sub { return $_[0]; },
475             "octets" => sub {
476 0     0   0 return $_[0];
477             },
478             "integer" => sub {
479 39     39   81 my $num=unpack("N", $_[0]);
480 39 100 100     127 return ( defined $dict->val_has_name($_[1]) &&
481             defined $dict->val_name($_[1],$num) ) ?
482             $dict->val_name($_[1],$num) : $num ;
483             },
484             "ipaddr" => sub {
485 13 50   13   140 return length($_[0]) == 4 ? inet_ntoa($_[0]) : $_[0];
486             },
487             "address" => sub {
488 0 0   0   0 return length($_[0]) == 4 ? inet_ntoa($_[0]) : $_[0];
489             },
490             "time" => sub {
491 0     0   0 return unpack "N", $_[0];
492             },
493             "date" => sub {
494 2     2   7 return unpack "N", $_[0];
495             },
496             "tagged-string" => sub {
497 0     0   0 my ($tag,$val) = unpack "a a*", $_[0];
498 0         0 return $val, $tag;
499             },
500             "tagged-integer" => sub {
501 0     0   0 my ($tag,$num) = unpack "a a*", $_[0];
502 0 0 0     0 return ( defined $dict->val_has_name($_[1]) &&
503             defined $dict->val_name($_[1],$num) ) ?
504             $dict->val_name($_[1],$num) : $num
505             ,$tag ;
506             },
507             "tagged-ipaddr" => sub {
508 0     0   0 my ( $tag, $num ) = unpack "a a*", $_[0];
509 0         0 return inet_ntoa($num), $tag;
510 18         646 });
511              
512             my %vsaunpacker =
513             (
514             "octets" => sub {
515 0     0   0 return $_[0];
516             },
517             "string" => sub {
518 24     24   49 return $_[0];
519             },
520 0     0   0 "ipv6addr" => sub { return $_[0]; },
521 0     0   0 "date" => sub { return $_[0]; },
522 0     0   0 "ifid" => sub { return $_[0]; },
523             "integer" => sub {
524 16     16   34 my $num=unpack("N", $_[0]);
525 16 50 33     64 return ( $dict->vsaval_has_name($_[2], $_[1])
526             && $dict->vsaval_name($_[2], $_[1],$num) )
527             ? $dict->vsaval_name($_[2], $_[1], $num )
528             : $num;
529             },
530             "ipaddr" => sub {
531 0 0   0   0 return length($_[0]) == 4 ? inet_ntoa($_[0]) : $_[0];
532             },
533             "address" => sub {
534 0 0   0   0 return length($_[0]) == 4 ? inet_ntoa($_[0]) : $_[0];
535             },
536             "time" => sub {
537 0     0   0 return unpack "N", $_[0];
538             },
539             "date" => sub {
540 0     0   0 return unpack "N", $_[0];
541             },
542             "tagged-string" => sub {
543 0     0   0 my ($tag,$val) = unpack "a a*", $_[0];
544 0         0 return $val, $tag;
545             },
546             "tagged-integer" => sub {
547 0     0   0 my ( $tag, $num ) = unpack "a a*", $_[0];
548 0 0 0     0 return ($dict->vsaval_has_name($_[2], $_[1])
549             && $dict->vsaval_name($_[2], $_[1],$num)
550             )?$dict->vsaval_name($_[2], $_[1],$num):$num
551             , $tag ;
552            
553             },
554             "tagged-ipaddr" => sub {
555 0     0   0 my ( $tag, $num ) = unpack "a a*", $_[0];
556 0         0 return inet_ntoa($num), $tag;
557 18         582 });
558            
559             # Unpack the attributes
560 18         102 while (length($attrdat))
561             {
562 112         217 my $length = unpack "x C", $attrdat;
563 112         139 my ($type, $value) = unpack "C x a${\($length-2)}", $attrdat;
  112         492  
564 112 100       248 if ($type == $VSA) { # Vendor-Specific Attribute
565 9         27 my ($vid) = unpack "N", $value;
566 9         19 substr ($value, 0, 4) = "";
567            
568 9         27 while (length($value))
569             {
570 40         82 my ($vtype, $vlength) = unpack "C C", $value;
571            
572             # XXX - How do we calculate the length
573             # of the VSA? It's not defined!
574            
575             # XXX - 3COM seems to do things a bit differently.
576             # The IF below takes care of that. This was contributed by
577             # Ian Smith. Check the file CHANGES on this distribution for
578             # more information.
579              
580 40         41 my $vvalue;
581 40 50       71 if ($vid == 429)
582             {
583 0         0 ($vtype) = unpack "N", $value;
584 0         0 $vvalue = unpack "xxxx a${\($length-10)}", $value;
  0         0  
585             }
586             else
587             {
588 40         47 $vvalue = unpack "x x a${\($vlength-2)}", $value;
  40         183  
589             }
590              
591 40 50 33     132 if ((not defined $dict->vsattr_numtype($vid, $vtype)) or
592             (ref $vsaunpacker{$dict->vsattr_numtype($vid, $vtype)}
593             ne 'CODE'))
594             {
595 0 0       0 my $whicherr
596             = (defined $dict->vsattr_numtype($vid, $vtype)) ?
597             "Garbled":"Unknown";
598 0 0       0 warn "$whicherr vendor attribute $vid/$vtype for unpack()\n"
599             unless $unkvprinted{"$vid/$vtype"};
600 0         0 $unkvprinted{"$vid/$vtype"} = 1;
601 0         0 substr($value, 0, $vlength) = ""; # Skip this section
602 0         0 next;
603             }
604 40         99 my ($val, $tag) =
605 40         63 &{$vsaunpacker{$dict->vsattr_numtype($vid, $vtype)}}($vvalue,
606             $vtype,
607             $vid);
608 40 50       82 if ( defined $tag )
609             {
610 0 0       0 $val = "-emtpy-" unless defined $val;
611 0         0 $self->set_taggedvsattr($vid,
612             $dict->vsattr_name($vid, $vtype),
613             $val,
614             $tag);
615             }
616             else
617             {
618 40         110 $self->set_vsattr($vid, $dict->vsattr_name($vid, $vtype),
619             $val);
620             }
621 40         118 substr($value, 0, $vlength) = "";
622             }
623             }
624             else
625             { # Normal attribute
626 103 50 33     300 if ((not defined $dict->attr_numtype($type)) or
627             (ref ($unpacker{$dict->attr_numtype($type)}) ne 'CODE'))
628             {
629 0 0       0 my $whicherr = (defined $dict->attr_numtype($type)) ?
630             "Garbled":"Unknown";
631 0 0       0 warn "$whicherr general attribute $type for unpack()\n"
632             unless $unkgprinted{$type};
633 0         0 $unkgprinted{$type} = 1;
634 0         0 substr($attrdat, 0, $length) = ""; # Skip this section
635 0         0 next;
636             }
637 103         329 my ($val,$tag) = &{$unpacker{$dict->attr_numtype($type)}}($value,
  103         252  
638             $type);
639 103 50       210 if ( defined $tag ) {
640 0 0       0 if ( ! defined $val ) { $val = "-emtpy-" };
  0         0  
641 0         0 $self->set_taggedattr($dict->attr_name($type), $val , $tag);
642             }
643             else {
644 103         256 $self->set_attr($dict->attr_name($type), $val);
645             }
646             }
647 112         1223 substr($attrdat, 0, $length) = ""; # Skip this section
648             }
649             }
650              
651             1;
652             __END__