File Coverage

blib/lib/Net/Radius/Server/Set/Proxy.pm
Criterion Covered Total %
statement 27 27 100.0
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 36 36 100.0


line stmt bran cond sub pod time code
1             #! /usr/bin/perl
2             #
3             #
4             # $Id: Proxy.pm 75 2009-08-12 22:08:28Z lem $
5              
6             package Net::Radius::Server::Set::Proxy;
7              
8 1     1   1791 use 5.008;
  1         4  
  1         42  
9 1     1   6 use strict;
  1         2  
  1         27  
10 1     1   4 use warnings;
  1         1  
  1         61  
11              
12             our $VERSION = do { sprintf "%0.3f", 1+(q$Revision: 75 $ =~ /\d+/g)[0]/1000 };
13              
14 1     1   5 use IO::Select;
  1         2  
  1         54  
15 1     1   4 use IO::Socket::INET;
  1         2  
  1         15  
16 1     1   830 use Net::Radius::Packet 1.51;
  1         22  
  1         71  
17 1     1   6 use Net::Radius::Dictionary;
  1         1  
  1         21  
18 1     1   5 use Net::Radius::Server::Base qw/:set/;
  1         1  
  1         7  
19 1     1   24 use base qw/Net::Radius::Server::Set/;
  1         1  
  1         90  
20             __PACKAGE__->mk_accessors(qw/
21             server port secret dictionary result
22             timeout tries
23             /);
24              
25             sub _proxy
26             {
27             my $self = shift;
28             my $r_data = shift;
29             my $secret = shift;
30             my $dict = shift;
31              
32             my $req = $r_data->{request};
33             my $pass = $req->password($r_data->{secret});
34              
35             # Construct a packet for our server, passing all the attributes
36             # from the original packet - Note that the dict may be different
37             # XXX - It may be more efficient to take the chance and use
38             # ->{request} instead of re-decoding the packet
39             my $p = new Net::Radius::Packet $dict, $r_data->{packet};
40              
41             # Send password protected with our shared secret
42             $p->set_password($pass, $secret) if $p->attr('User-Password');
43              
44             my $packet = undef;
45             my $tries = 0;
46             my $reply = undef;
47              
48             # Format packet properly according to type
49             if ($req->code =~ m/Accounting-Request/)
50             {
51             $p->set_authenticator("\x0" x 16);
52             $packet = auth_resp($p->pack, $secret);
53             }
54             else
55             {
56             $packet = $p->pack();
57             }
58              
59             # Attempt to send the request to the real RADIUS server
60             while ($tries < $self->tries)
61             {
62             if ($self->{_socket}->send($packet))
63             {
64             if ($self->{_select}->can_read($self->timeout))
65             {
66             last if $self->{_socket}->recv($reply, 1024);
67             $self->log(2, "[$tries] Failed to recv(): $!");
68             }
69             else
70             {
71             $self->log(2, "[$tries] Timeout waiting for server response");
72             }
73             }
74             else
75             {
76             $self->log(1, "[$tries] Send failed: $!");
77             }
78             $tries ++;
79             }
80              
81             # No reply - Simply drop this packet and wait
82             unless (defined $reply and length($reply) > 0)
83             {
84             $self->log(2, "Server reply is undef or empty");
85             return;
86             }
87              
88             # Compose reply to the client depending on the packet type
89             $r_data->{response} = new Net::Radius::Packet $dict, $reply;
90              
91             unless ($r_data->{response})
92             {
93             $self->log(2, "Failed to parse response packet from server");
94             return;
95             }
96              
97             # Adjust authenticators according to the response type
98             my $res = $r_data->{response};
99             if ($res->code =~ m/
100             Access-Accept
101             |Access-Reject
102             |Access-Challenge
103             |Accounting-Response/x)
104             {
105             $res->set_authenticator($req->authenticator);
106             }
107             elsif ($res->code =~ m/Accounting-Request/)
108             {
109             $res->set_authenticator("\x0" x 16);
110             }
111            
112             # Copy response packet back to our client
113             $self->log(4, "Copying packet to my response");
114             return 1;
115             }
116              
117             sub _set
118             {
119             my $self = shift;
120             my $r = $self->set_server(@_);
121             unless ($r)
122             {
123             $self->log(3, "Failure: Return CONTINUE by default");
124             return NRS_SET_CONTINUE;
125             }
126              
127             if ($self->can('result'))
128             {
129             my $r = $self->result;
130             $self->log(4, "Return $r as given result");
131             return $r;
132             }
133             else
134             {
135             $self->log(4, "Return CONTINUE | RESPOND as given result");
136             return NRS_SET_CONTINUE | NRS_SET_RESPOND;
137             }
138             }
139              
140             sub set_server
141             {
142             my $self = shift;
143             my $r_data = shift;
144              
145             $self->timeout(3) unless $self->timeout;
146             $self->tries(2) unless $self->tries;
147            
148             my $secret = $self->secret || $r_data->{secret};
149             my $port = $self->port || $r_data->{port};
150             my $dict = defined $self->dictionary
151             ? Net::Radius::Dictionary->new($self->dictionary)
152             : $r_data->{dict};
153              
154             $self->log(4, "Creating udp socket to " . $self->server . ":$port");
155             $self->{_socket} = IO::Socket::INET->new
156             (
157             PeerAddr => $self->server,
158             PeerPort => $port,
159             Proto => 'udp',
160             );
161              
162             unless ($self->{_socket})
163             {
164             $self->log(2, "Failed to create socket: $!");
165             return;
166             }
167              
168             $self->{_select} = new IO::Select $self->{_socket};
169             unless ($self->{_select})
170             {
171             $self->log(2, "Failed to select object: $!");
172             return;
173             }
174            
175             $self->log(4, "Proxying request to "
176             . $self->server . ":$port");
177             $self->_proxy($r_data, $secret, $dict);
178             }
179              
180             42;
181              
182             __END__