File Coverage

blib/lib/Net/Radius/PacketOrdered.pm
Criterion Covered Total %
statement 18 242 7.4
branch 0 64 0.0
condition 0 12 0.0
subroutine 6 59 10.1
pod 23 29 79.3
total 47 406 11.5


line stmt bran cond sub pod time code
1             package Net::Radius::PacketOrdered;
2              
3 1     1   961 use strict;
  1         2  
  1         48  
4             require Exporter;
5 1     1   6 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $VSA);
  1         2  
  1         105  
6             @ISA = qw(Exporter);
7             @EXPORT = qw(auth_resp acct_request_auth acct_response_auth);
8             @EXPORT_OK = qw( );
9              
10             $VERSION = '1.53';
11              
12             $VSA = 26; # Type assigned in RFC2138 to the
13             # Vendor-Specific Attributes
14              
15             # Be shure our dictionaries are current
16 1     1   1039 use Net::Radius::Dictionary 1.1;
  1         2662  
  1         28  
17 1     1   8 use Carp;
  1         2  
  1         78  
18 1     1   1410 use Socket;
  1         4537  
  1         597  
19 1     1   12 use Digest::MD5;
  1         2  
  1         3698  
20              
21             =head1 NAME
22              
23             Net::Radius::PacketOrdered - interface to RADIUS packets with proxy states
24              
25             =head1 SYNOPSIS
26              
27             use Net::Radius::PacketOrdered;
28             use Net::Radius::Dictionary;
29              
30             my $d = new Net::Radius::Dictionary "/etc/radius/dictionary";
31              
32             my $p = new Net::Radius::PacketOrdered $d, $data;
33             $p->dump;
34              
35             if ($p->attr('User-Name' eq "lwall") {
36             my $resp = new Net::Radius::PacketOrdered $d;
37             $resp->set_code('Access-Accept');
38             $resp->set_identifier($p->identifier);
39             $resp->set_authenticator($p->authenticator);
40             $resp->set_attr('Reply-Message' => "Welcome, Larry!\r\n");
41             my $respdat = auth_resp($resp->pack, "mysecret");
42             ...
43              
44             =head1 DESCRIPTION
45              
46             RADIUS (RFC2865) specifies a binary packet format which contains
47             various values and attributes. Net::Radius::PacketOrdered provides an
48             interface to turn RADIUS packets into Perl data structures and
49             vice-versa.
50              
51             Net::Radius::PacketOrdered does not provide functions for obtaining
52             RADIUS packets from the network. A simple network RADIUS server is
53             provided as an example at the end of this document.
54              
55             =head2 Proxy-State, RFC specification
56              
57             from RFC 2865 - ftp://ftp.rfc-editor.org/in-notes/rfc2865.txt
58              
59             2. Operation
60              
61             If any Proxy-State attributes were present in the Access-Request,
62             they MUST be copied unmodified and in order into the response packet.
63             Other Attributes can be placed before, after, or even between the
64             Proxy-State attributes.
65              
66             2.3 Proxy
67              
68             The forwarding server MUST treat any Proxy-State attributes already
69             in the packet as opaque data. Its operation MUST NOT depend on the
70             content of Proxy-State attributes added by previous servers.
71              
72             If there are any Proxy-State attributes in the request received from
73             the client, the forwarding server MUST include those Proxy-State
74             attributes in its reply to the client. The forwarding server MAY
75             include the Proxy-State attributes in the access-request when it
76             forwards the request, or MAY omit them in the forwarded request. If
77             the forwarding server omits the Proxy-State attributes in the
78             forwarded access-request, it MUST attach them to the response before
79             sending it to the client.
80              
81             =head2 Proxy-State, Implementation
82              
83             Proxy-State attributes are stored in an array, and when copied from
84             one Net::Radius::PacketOrdered to another - using method I with
85             packet data as attribute - they retain their order.
86              
87             I method always returns last attribute inserted.
88              
89             I method pushed name attribute onto the Attributes stack, or
90             overwrites it in specific circumnstances, as described in method
91             documentation.
92              
93             =head2 PACKAGE METHODS
94              
95             =over 4
96              
97             =item I Net::Radius::PacketOrdered $dictionary, $data
98              
99             Returns a new Net::Radius::PacketOrdered object. $dictionary is an
100             optional reference to a Net::Radius::Dictionary object. If not
101             supplied, you must call B. If $data is supplied, B
102             will be called for you to initialize the object.
103              
104             =back
105              
106             =cut
107              
108             my (%unkvprinted,%unkgprinted);
109             sub new {
110 0     0 1   my ($class, $dict, $data) = @_;
111 0           my $self = { unknown_entries => 1 };
112 0           bless $self, $class;
113 0 0         $self->set_dict($dict) if defined($dict);
114 0 0         $self->unpack($data) if defined($data);
115 0           return $self;
116             }
117              
118             =head2 OBJECT METHODS
119              
120             There are actually two families of object methods. The ones described
121             below deal with standard RADIUS attributes. An additional set of methods
122             handle the Vendor-Specific attributes as defined in the RADIUS protocol.
123             Those methods behave in much the same way as the ones below with the
124             exception that the prefix I must be applied before the I in most
125             of the names. The vendor code must also be included as the first parameter
126             of the call.
127              
128             The I and I methods, used to query and set
129             Vendor-Specific attributes return an array reference with the values
130             of each instance of the particular attribute in the packet. This
131             difference is required to support multiple VSAs with different
132             parameters in the same packet.
133              
134             =over 4
135              
136             =item -EI($dictionary)
137              
138             Net::Radius::PacketOrdered needs access to a Net::Radius::Dictionary object to do
139             packing and unpacking. set_dict must be called with an appropriate
140             dictionary reference (see L) before you can
141             use ->B or ->B.
142              
143             =cut
144              
145             sub set_dict {
146 0     0 1   my ($self, $dict) = @_;
147 0           $self->{Dict} = $dict;
148             }
149              
150             =item -EI
151              
152             Returns the Code field as a string. As of this writing, the following
153             codes are defined:
154              
155             Access-Request Access-Accept
156             Access-Reject Accounting-Request
157             Accounting-Response Access-Challenge
158             Status-Server Status-Client
159              
160             =item ->($code)
161              
162             Sets the Code field to the string supplied.
163              
164             =item -EI
165              
166             Returns the one-byte Identifier used to match requests with responses,
167             as a character value.
168              
169             =item -EI
170              
171             Sets the Identifier byte to the character supplied.
172              
173             =item -EI
174              
175             Returns the 16-byte Authenticator field as a character string.
176              
177             =item -EI
178              
179             Sets the Authenticator field to the character string supplied.
180              
181             =cut
182              
183             # Functions for accessing data structures
184 0     0 1   sub code { $_[0]->{Code}; }
185 0     0 1   sub identifier { $_[0]->{Identifier}; }
186 0     0 1   sub authenticator { $_[0]->{Authenticator}; }
187              
188 0     0 1   sub set_code { $_[0]->{Code} = $_[1]; }
189 0     0 1   sub set_identifier { $_[0]->{Identifier} = $_[1]; }
190 0     0 1   sub set_authenticator { $_[0]->{Authenticator} = $_[1]; }
191              
192             =item -EI($name, $val, $rewrite_flag)
193              
194             Sets the named Attributes to the given value. Values should be
195             supplied as they would be returned from the B method. If
196             rewrite_flag is set, and a single attribute with such name already
197             exists on the Attributes stack, its value will be overwriten with the
198             supplied one. In all other cases (if there are more than one
199             attributes with such name already on the stack, there are no
200             attributes with such name, rewrite_flag is omitted) name/pair array
201             will be pushed onto the stack.
202              
203             =cut
204              
205             sub set_attr {
206 0     0 1   my ($self, $name, $value, $rewrite_flag ) = @_;
207 0           my ( $push, $pos );
208              
209 0 0         $push = 1 unless $rewrite_flag;
210              
211 0 0         if ($rewrite_flag) {
212 0           my $found = 0;
213 0           my @attr = $self->_attributes;
214              
215 0           for (my $i = 0; $i <= $#attr; $i++ ) {
216 0 0         if ($attr[$i][0] eq $name) {
217 0           $found++;
218 0           $pos = $i;
219             }
220             }
221              
222 0 0         if ($found > 1) {
    0          
223 0           $push = 1;
224             } elsif ($found) {
225 0           $attr[$pos][0] = $name;
226 0           $attr[$pos][1] = $value;
227 0           $self->_set_attributes( \@attr );
228 0           return;
229             } else {
230 0           $push = 1;
231             }
232             }
233              
234 0 0         $self->_push_attr( $name, $value ) if $push;
235              
236             }
237              
238             =item -EI
239              
240             Retrieves a list of attribute names present within the packet.
241              
242             =cut
243              
244             sub attributes {
245 0     0 1   my ($self) = @_;
246              
247 0           my @attr = $self->_attributes;
248 0           my @attriblist = ();
249 0           for (my $i = $#attr; $i >= 0; $i-- ) {
250 0           push @attriblist, $attr[$i][0];
251             }
252 0           return @attriblist;
253             }
254              
255             =item -EI($name)
256              
257             Retrieves the value of the named Attribute. If there are multiple
258             values for the Attribute, last one inserted will be returned. This is
259             behaviour is crucial for correct implementation of Proxy-State.
260              
261             =cut
262              
263             sub attr {
264 0     0 1   my ($self, $name ) = @_;
265              
266 0           my @attr = $self->_attributes;
267              
268 0           for (my $i = $#attr; $i >= 0; $i-- ) {
269 0 0         return $attr[$i][1] if $attr[$i][0] eq $name;
270             }
271 0           return;
272             }
273              
274             =item -EI($name,$value)
275              
276             Removes given Attribute with given value from the Attributes stack.
277              
278             =cut
279              
280             sub unset_attr {
281 0     0 1   my ($self, $name, $value ) = @_;
282              
283 0           my $found;
284 0           my @attr = $self->_attributes;
285              
286 0           for (my $i = 0; $i <= $#attr; $i++ ) {
287 0 0 0       if ( $name eq $attr[$i][0] && $value eq pclean(pdef($attr[$i][1]))) {
288 0           $found = 1;
289 0 0         if ( $#attr == 0 ) {
290             # no more attributes left on the stack
291 0           $self->_set_attributes( [ ] );
292             } else {
293 0           splice @attr, $i, 1;
294 0           $self->_set_attributes( \@attr );
295             }
296 0           return 1;
297             }
298             }
299              
300 0           return 0;
301             }
302              
303             =item -EI($integer)
304              
305             Retrieves the attribute value of the given slot number from the
306             Attributes stack.
307              
308             =cut
309              
310 0     0 1   sub attr_slot { ($_[0]->_attributes)[ $_[1] ]->[1]; }
311              
312              
313             =item -EI($integer)
314              
315             Removes given stack position from the Attributes stack.
316              
317             =cut
318              
319             sub unset_attr_slot {
320 0     0 1   my ($self, $position ) = @_;
321              
322 0           my @attr = $self->_attributes;
323              
324 0 0         if ( not $position > $#attr ) {
325 0           splice @attr, $position, 1;
326 0           $self->_set_attributes( \@attr );
327 0           return 1;
328             } else {
329 0           return;
330             }
331              
332             }
333              
334             =item -EI($secret)
335              
336             The RADIUS User-Password attribute is encoded with a shared secret.
337             Use this method to return the decoded version. This also works when
338             the attribute name is 'Password' for compatibility reasons.
339              
340             =item -EI($passwd, $secret)
341              
342             The RADIUS User-Password attribute is encoded with a shared secret.
343             Use this method to prepare the encoded version. Note that this method
344             always stores the encrypted password in the 'User-Password'
345             attribute. Some servers have been reported on insisting on this
346             attribute to be 'Password' instead.
347              
348             =item -EI
349              
350             Controls the generation of a C whenever an unknown tuple is seen.
351              
352             =cut
353              
354 0     0 0   sub vendors { keys %{$_[0]->{VSAttributes}}; }
  0            
355 0     0 0   sub vsattributes { keys %{$_[0]->{VSAttributes}->{$_[1]}}; }
  0            
356 0     0 0   sub vsattr { $_[0]->{VSAttributes}->{$_[1]}->{$_[2]}; }
357 0     0 0   sub set_vsattr { push @{$_[0]->{VSAttributes}->{$_[1]}->{$_[2]}}, $_[3]; }
  0            
358              
359 0     0 1   sub show_unknown_entries { $_[0]->{unknown_entries} = $_[1]; }
360              
361             # Decode the password
362             sub password {
363 0     0 1   my ($self, $secret) = @_;
364 0           my $lastround = $self->authenticator;
365 0   0       my $pwdin = $self->attr("User-Password") || $self->attr("Password");
366 0           my $pwdout = ""; # avoid possible undef warning
367 0           for (my $i = 0; $i < length($pwdin); $i += 16) {
368 0           $pwdout .= substr($pwdin, $i, 16) ^ Digest::MD5::md5($secret . $lastround);
369 0           $lastround = substr($pwdin, $i, 16);
370             }
371 0 0         $pwdout =~ s/\000*$// if $pwdout;
372 0 0         substr($pwdout,length($pwdin)) = ""
373             unless length($pwdout) <= length($pwdin);
374 0           return $pwdout;
375             }
376              
377             # Encode the password
378             sub set_password {
379 0     0 1   my ($self, $pwdin, $secret) = @_;
380 0           my $lastround = $self->authenticator;
381 0           my $pwdout = ""; # avoid possible undef warning
382 0           $pwdin .= "\000" x (15-(15 + length $pwdin)%16); # pad to 16n bytes
383              
384 0           for (my $i = 0; $i < length($pwdin); $i += 16) {
385 0           $lastround = substr($pwdin, $i, 16)
386             ^ Digest::MD5::md5($secret . $lastround);
387 0           $pwdout .= $lastround;
388             }
389 0           $self->set_attr("User-Password", $pwdout, 1);
390             }
391              
392             =item -EI($packet, $secret)
393              
394             Set request authenticator in binary packet, for accounting request
395             authentication.
396              
397             =cut
398              
399             sub acct_request_auth {
400 0     0 1   my $new = $_[0];
401 0           substr($new, 4, 16) = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
402 0           substr($new, 4, 16) = Digest::MD5::md5($new . $_[1]);
403 0           return $new;
404             }
405              
406             =item -EI($packet, $secret, request-auth)
407              
408             Set reponse authenticator in binary packet, for accounting response
409             authentication.
410              
411             =cut
412              
413             sub acct_response_auth {
414 0     0 1   my $new = $_[0];
415 0           substr($new, 4, 16) = $_[2];
416 0           substr($new, 4, 16) = Digest::MD5::md5($new . $_[1]);
417 0           return $new;
418             }
419              
420             # Set response authenticator in binary packet
421             sub auth_resp {
422 0     0 1   my $new = $_[0];
423 0           substr($new, 4, 16) = Digest::MD5::md5($_[0] . $_[1]);
424 0           return $new;
425             }
426              
427             # Utility functions for printing/debugging
428 0 0   0 0   sub pdef { defined $_[0] ? $_[0] : "UNDEF"; }
429             sub pclean {
430 0     0 0   my $str = $_[0];
431 0           $str =~ s/([\000-\037\177-\377])/<${\ord($1)}>/g;
  0            
432 0           return $str;
433             }
434              
435             =item -EI
436              
437             Prints the content of the packet to STDOUT.
438              
439             =cut
440              
441             sub dump {
442 0     0 1   print _str_dump(@_);
443             }
444              
445             =item -EI
446              
447             Returns a raw RADIUS packet suitable for sending to a RADIUS client
448             or server.
449              
450             =cut
451              
452             sub pack {
453 0     0 1   my $self = shift;
454 0           my $hdrlen = 1 + 1 + 2 + 16; # Size of packet header
455 0           my $p_hdr = "C C n a16 a*"; # Pack template for header
456 0           my $p_attr = "C C a*"; # Pack template for attribute
457 0           my $p_vsa = "C C N C C a*";
458              
459             # XXX - The spec says that a
460             # 'Vendor-Type' must be included
461             # but there are no documented definitions
462             # for this! We'll simply skip this value
463              
464 0           my $p_vsa_3com = "C C N N a*";
465              
466 0           my %codes = ('Access-Request' => 1, 'Access-Accept' => 2,
467             'Access-Reject' => 3, 'Accounting-Request' => 4,
468             'Accounting-Response' => 5, 'Access-Challenge' => 11,
469             'Status-Server' => 12, 'Status-Client' => 13);
470 0           my $attstr = ""; # To hold attribute structure
471             # Define a hash of subroutine references to pack the various data types
472             my %packer = (
473             "string" => sub {
474 0     0     return $_[0];
475             },
476             "integer" => sub {
477 0 0   0     return pack "N", $self->{Dict}->attr_has_val($_[1]) ?
478             $self->{Dict}->val_num(@_[1, 0]) : $_[0];
479             },
480             "ipaddr" => sub {
481 0     0     return inet_aton($_[0]);
482             },
483             "time" => sub {
484 0     0     return pack "N", $_[0];
485             },
486             "date" => sub {
487 0     0     return pack "N", $_[0];
488 0           });
489              
490 0     0     my %vsapacker = ("string" => sub { return $_[0]; },
491             "integer" => sub {
492 0 0   0     return pack "N",
493             $self->{Dict}->vsattr_has_val($_[2], $_[1]) ?
494             $self->{Dict}->vsaval_num(@_[2, 1, 0]) : $_[0];
495             },
496             "ipaddr" => sub {
497 0     0     return inet_aton($_[0]);
498             },
499             "time" => sub {
500 0     0     return pack "N", $_[0];
501             },
502             "date" => sub {
503 0     0     return pack "N", $_[0];
504 0           });
505              
506             # Pack the attributes
507 0           foreach my $attr ($self->_attributes) {
508              
509 0           my $attr_name = $attr->[0];
510 0           my $attr_value = $attr->[1];
511              
512 0 0         if (! defined $self->{Dict}->attr_num($attr_name))
513             {
514 0 0         carp("Unknown RADIUS tuple $attr_name\n")
515             if ($self->{unknown_entries});
516 0           next;
517             }
518              
519 0 0         next unless ref($packer{$self->{Dict}->attr_type($attr_name)}) eq 'CODE';
520              
521 0           my $val = &{$packer{ $self->{Dict}->attr_type($attr_name) } }
  0            
522             ($attr_value, $self->{Dict} ->attr_num($attr_name));
523              
524 0           $attstr .= pack $p_attr, $self->{Dict}->attr_num($attr_name),
525             length($val)+2, $val;
526             }
527              
528             # Pack the Vendor-Specific Attributes
529              
530 0           foreach my $vendor ($self->vendors) {
531 0           foreach my $attr ($self->vsattributes($vendor)) {
532 0 0         next unless ref($vsapacker{$self->{Dict}->vsattr_type($vendor, $attr)})
533             eq 'CODE';
534 0           foreach my $datum (@{$self->vsattr($vendor, $attr)}) {
  0            
535 0           my $vval = &{$vsapacker{$self->{'Dict'}
  0            
536             ->vsattr_type($vendor, $attr)}}
537             ($datum, $self->{'Dict'}->vsattr_num($vendor, $attr), $vendor);
538              
539 0 0         if ($vendor == 429) {
540              
541             # XXX - As pointed out by Quan Choi,
542             # we need special code to handle the
543             # 3Com case
544              
545 0           $attstr .= pack $p_vsa_3com, 26,
546             length($vval) + 10, $vendor,
547             $self->{'Dict'}->vsattr_num($vendor, $attr),
548             $vval;
549             } else {
550 0           $attstr .= pack $p_vsa, 26, length($vval) + 8, $vendor,
551             $self->{'Dict'}->vsattr_num($vendor, $attr),
552             length($vval) + 2, $vval;
553             }
554             }
555             }
556             }
557              
558             # Prepend the header and return the complete binary packet
559 0           return pack $p_hdr, $codes{$self->code}, $self->identifier,
560             length($attstr) + $hdrlen, $self->authenticator,
561             $attstr;
562             }
563              
564             =item -EI($data)
565              
566             Given a raw RADIUS packet $data, unpacks its contents so that they
567             can be retrieved with the other methods (B, B, etc.).
568              
569             =back
570              
571             =cut
572              
573             sub unpack {
574 0     0 1   my ($self, $data) = @_;
575 0           my $dict = $self->{Dict};
576 0           my $p_hdr = "C C n a16 a*"; # Pack template for header
577 0           my $p_attr = "C C a*"; # Pack template for attribute
578 0           my %rcodes = (1 => 'Access-Request', 2 => 'Access-Accept',
579             3 => 'Access-Reject', 4 => 'Accounting-Request',
580             5 => 'Accounting-Response', 11 => 'Access-Challenge',
581             12 => 'Status-Server', 13 => 'Status-Client');
582              
583             # Decode the header
584 0           my ($code, $id, $len, $auth, $attrdat) = unpack $p_hdr, $data;
585              
586             # Generate a skeleton data structure to be filled in
587 0           $self->set_code($rcodes{$code});
588 0           $self->set_identifier($id);
589 0           $self->set_authenticator($auth);
590              
591             # Functions for the various data types
592             my %unpacker =
593             (
594             "string" => sub {
595 0     0     return $_[0];
596             },
597             "integer" => sub {
598 0 0   0     return $dict->val_has_name($_[1]) ?
599             $dict->val_name($_[1],
600             unpack("N", $_[0]))
601             : unpack("N", $_[0]);
602             },
603             "ipaddr" => sub {
604 0     0     return inet_ntoa($_[0]);
605             },
606             "time" => sub {
607 0     0     return unpack "N", $_[0];
608             },
609             "date" => sub {
610 0     0     return unpack "N", $_[0];
611 0           });
612              
613             my %vsaunpacker =
614             ( "string" => sub {
615 0     0     return $_[0];
616             },
617             "integer" => sub {
618 0 0   0     $dict->vsaval_has_name($_[2], $_[1])
619             ? $dict->vsaval_name($_[2], $_[1], unpack("N", $_[0]))
620             : unpack("N", $_[0]);
621             },
622             "ipaddr" => sub {
623 0     0     return inet_ntoa($_[0]);
624             },
625             "time" => sub {
626 0     0     return unpack "N", $_[0];
627             },
628             "date" => sub {
629 0     0     return unpack "N", $_[0];
630 0           });
631              
632              
633 0           my $i = 0;
634             # Unpack the attributes
635 0           while (length($attrdat)) {
636 0           my $length = unpack "x C", $attrdat;
637 0           my ($type, $value) = unpack "C x a${\($length-2)}", $attrdat;
  0            
638 0 0         if ($type == $VSA) { # Vendor-Specific Attribute
639 0           my ($vid, $vtype, $vlength) = unpack "N C C", $value;
640              
641             # XXX - How do we calculate the length
642             # of the VSA? It's not defined!
643              
644             # XXX - 3COM seems to do things a bit differently.
645             # The IF below takes care of that. This was contributed by
646             # Ian Smith. Check the file CHANGES on this distribution for
647             # more information.
648              
649 0           my $vvalue;
650 0 0         if ($vid == 429) {
651 0           ($vid, $vtype) = unpack "N N", $value;
652 0           $vvalue = unpack "xxxx xxxx a${\($length-10)}", $value;
  0            
653             } else {
654 0           $vvalue = unpack "xxxx x x a${\($vlength-2)}", $value;
  0            
655             }
656              
657 0 0 0       if ((not defined $dict->vsattr_numtype($vid, $vtype)) or
658             (ref $vsaunpacker{$dict->vsattr_numtype($vid, $vtype)} ne 'CODE')) {
659 0 0         my $whicherr = (defined $dict->vsattr_numtype($vid, $vtype)) ?
660             "Garbled":"Unknown";
661 0 0         warn "$whicherr vendor attribute $vid/$vtype for unpack()\n"
662             unless $unkvprinted{"$vid/$vtype"};
663 0           $unkvprinted{"$vid/$vtype"} = 1;
664 0           substr($attrdat, 0, $length) = ""; # Skip this section
665 0           next;
666             }
667 0           my $val =
668 0           &{$vsaunpacker{$dict->vsattr_numtype($vid, $vtype)}}($vvalue,
669             $vtype,
670             $vid);
671 0           $self->set_vsattr($vid,
672             $dict->vsattr_name($vid, $vtype),
673             $val);
674             } else { # Normal attribute
675 0 0 0       if ((not defined $dict->attr_numtype($type)) or
676             (ref ($unpacker{$dict->attr_numtype($type)}) ne 'CODE')) {
677 0 0         my $whicherr = (defined $dict->attr_numtype($type)) ?
678             "Garbled":"Unknown";
679 0 0         warn "$whicherr general attribute $type for unpack()\n"
680             unless $unkgprinted{$type};
681 0           $unkgprinted{$type} = 1;
682 0           substr($attrdat, 0, $length) = ""; # Skip this section
683 0           next;
684             }
685 0           my $val = &{$unpacker{$dict->attr_numtype($type)}}($value, $type);
  0            
686 0           $self->set_attr($dict->attr_name($type), $val);
687              
688             }
689 0           substr($attrdat, 0, $length) = ""; # Skip this section
690             }
691             }
692              
693             #================================================================
694             # *** PRIVATE METHODS ***
695             #================================================================
696              
697             # 'Attributes' is now array of arrays, so that we can have multiple
698             # Proxy-State values in the order in which they were added,
699             # as specified in RFC 2865
700 0     0     sub _attributes { @{ $_[0]->{Attributes} }; }
  0            
701 0     0     sub _set_attributes { $_[0]->{Attributes} = $_[1]; }
702 0     0     sub _push_attr { push @{ $_[0]->{Attributes} }, [ $_[1], $_[2] ]; }
  0            
703              
704             sub _str_dump {
705 0     0     my $self = shift;
706 0           my $ret = '';
707 0           my $i = 0;
708              
709 0           $ret .= "--- DUMP OF RADIUS PACKET ($self) ---\n";
710 0           $ret .= "Code: ". pdef($self->{Code}). "\n";
711 0           $ret .= "Identifier: ". pdef($self->{Identifier}). "\n";
712 0           $ret .= "Authentic: ". pclean(pdef($self->{Authenticator})). "\n";
713 0           $ret .= "Attributes stack:\n";
714              
715 0           foreach my $attr ( $self->_attributes ) {
716 0           $ret .= sprintf(" %s %-20s %s\n", "[$i]", $attr->[0] . ":" ,
717             pclean( pdef($attr->[1]) )
718             );
719 0           $i++;
720             }
721 0           foreach my $vendor ($self->vendors) {
722 0           $ret .= "VSA for vendor $vendor\n";
723 0           foreach my $attr ($self->vsattributes($vendor)) {
724 0           $ret .= sprintf(" %-20s %s\n", $attr . ":" ,
725 0           pclean(join("|", @{$self->vsattr($vendor, $attr)})));
726             }
727             }
728 0           $ret .= "--- END DUMP -------------------------\n";
729 0           return $ret;
730             }
731              
732             1;
733              
734             __END__