line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package AnyEvent::Radius::Server; |
2
|
|
|
|
|
|
|
# AnyEvent-based radius server |
3
|
1
|
|
|
1
|
|
1296
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
4
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
24
|
|
5
|
1
|
|
|
1
|
|
5
|
use AnyEvent; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
18
|
|
6
|
1
|
|
|
1
|
|
5
|
use AnyEvent::Handle::UDP; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
4
|
use base qw(Class::Accessor::Fast); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
133
|
|
9
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(qw(handler packer)); |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
7
|
use Data::Radius::Constants qw(:all); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
187
|
|
12
|
1
|
|
|
1
|
|
8
|
use Data::Radius::Dictionary (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
12
|
|
13
|
1
|
|
|
1
|
|
5
|
use Data::Radius::Packet (); |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
27
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use constant { |
16
|
1
|
|
|
|
|
676
|
READ_TIMEOUT_SEC => 5, |
17
|
|
|
|
|
|
|
WRITE_TIMEOUT_SEC => 5, |
18
|
|
|
|
|
|
|
RADIUS_PORT => 1812, |
19
|
1
|
|
|
1
|
|
6
|
}; |
|
1
|
|
|
|
|
2
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my %DEFAUL_REPLY = ( |
22
|
|
|
|
|
|
|
&ACCESS_REQUEST => ACCESS_REJECT, |
23
|
|
|
|
|
|
|
&ACCOUNTING_REQUEST => ACCOUNTING_RESPONSE, |
24
|
|
|
|
|
|
|
&DISCONNECT_REQUEST => DISCONNECT_REJECT, |
25
|
|
|
|
|
|
|
&COA_REQUEST => COA_REJECT, |
26
|
|
|
|
|
|
|
); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# new 'server' |
29
|
|
|
|
|
|
|
# args: |
30
|
|
|
|
|
|
|
# ip |
31
|
|
|
|
|
|
|
# port |
32
|
|
|
|
|
|
|
# secret |
33
|
|
|
|
|
|
|
# dictionary |
34
|
|
|
|
|
|
|
#- callbacks: |
35
|
|
|
|
|
|
|
# on_read |
36
|
|
|
|
|
|
|
# on_read_raw |
37
|
|
|
|
|
|
|
# on_wrong_request |
38
|
|
|
|
|
|
|
# on_error |
39
|
|
|
|
|
|
|
sub new { |
40
|
0
|
|
|
0
|
1
|
|
my ($class, %h) = @_; |
41
|
|
|
|
|
|
|
|
42
|
0
|
0
|
|
|
|
|
die "No IP argument" if (! $h{ip}); |
43
|
|
|
|
|
|
|
# either pre-created packer obect, or need radius secret to create new one |
44
|
|
|
|
|
|
|
# dictionary is optional |
45
|
0
|
0
|
0
|
|
|
|
die "No radius secret" if (! $h{packer} && ! $h{secret}); |
46
|
|
|
|
|
|
|
|
47
|
0
|
|
|
|
|
|
my $obj = bless {}, $class; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my $on_read_cb = sub { |
50
|
0
|
|
|
0
|
|
|
my ($data, $handle, $from) = @_; |
51
|
|
|
|
|
|
|
|
52
|
0
|
0
|
|
|
|
|
if ($h{on_read_raw}) { |
53
|
|
|
|
|
|
|
# dump raw data |
54
|
0
|
|
|
|
|
|
$h{on_read_raw}->($obj, $data, $from); |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# how to decoded $from |
58
|
|
|
|
|
|
|
# my($port, $host) = AnyEvent::Socket::unpack_sockaddr($from); |
59
|
|
|
|
|
|
|
# my $ip = format_ipv4($host); |
60
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
|
my ($type, $req_id, $authenticator, $av_list) = $obj->packer()->parse($data); |
62
|
|
|
|
|
|
|
|
63
|
0
|
0
|
|
|
|
|
if (! $obj->packer()->is_request($type)) { |
64
|
|
|
|
|
|
|
# we expect only requests in server |
65
|
0
|
0
|
|
|
|
|
if ($h{on_wrong_request}) { |
66
|
0
|
|
|
|
|
|
$h{on_wrong_request}->($obj, { |
67
|
|
|
|
|
|
|
type => $type, |
68
|
|
|
|
|
|
|
request_id => $req_id, |
69
|
|
|
|
|
|
|
av_list => $av_list, |
70
|
|
|
|
|
|
|
# from is sockaddr binary data |
71
|
|
|
|
|
|
|
from => $from, |
72
|
|
|
|
|
|
|
}); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# Do not reply |
76
|
0
|
|
|
|
|
|
warn "Ignore wrong request type " . $type; |
77
|
|
|
|
|
|
|
return |
78
|
0
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
0
|
|
|
|
|
|
my ($reply_type, $reply_av_list) = (); |
81
|
|
|
|
|
|
|
|
82
|
0
|
0
|
|
|
|
|
if($h{on_read}) { |
83
|
|
|
|
|
|
|
# custom-reply |
84
|
0
|
|
|
|
|
|
($reply_type, $reply_av_list) = $h{on_read}->($obj, { |
85
|
|
|
|
|
|
|
type => $type, |
86
|
|
|
|
|
|
|
request_id => $req_id, |
87
|
|
|
|
|
|
|
av_list => $av_list, |
88
|
|
|
|
|
|
|
# from is sockaddr binary data |
89
|
|
|
|
|
|
|
from => $from, |
90
|
|
|
|
|
|
|
}); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
0
|
0
|
|
|
|
|
if (! $reply_type) { |
94
|
|
|
|
|
|
|
# reject by default |
95
|
0
|
|
|
|
|
|
$reply_type = $DEFAUL_REPLY{ $type }; |
96
|
0
|
|
|
|
|
|
$reply_av_list = [{Name => 'Reply-Message', Value => 'Default rule: reject'}]; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
|
my ($reply, $r_id, $r_auth) = $obj->packer()->build( |
100
|
|
|
|
|
|
|
type => $reply_type, |
101
|
|
|
|
|
|
|
av_list => $reply_av_list, |
102
|
|
|
|
|
|
|
authenticator => $authenticator, |
103
|
|
|
|
|
|
|
request_id => $req_id, |
104
|
|
|
|
|
|
|
with_msg_auth => 1, |
105
|
|
|
|
|
|
|
); |
106
|
0
|
0
|
|
|
|
|
if(! $reply) { |
107
|
0
|
|
|
|
|
|
warn "Failed to build reply"; |
108
|
|
|
|
|
|
|
return |
109
|
0
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
|
$obj->handler()->push_send($reply, $from); |
112
|
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
|
return; |
114
|
0
|
|
|
|
|
|
}; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# low-level socket errors |
117
|
|
|
|
|
|
|
my $on_error_cb = sub { |
118
|
0
|
|
|
0
|
|
|
my ($handle, $fatal, $error) = @_; |
119
|
0
|
0
|
|
|
|
|
if ($h{on_error}) { |
120
|
0
|
|
|
|
|
|
$h{on_error}->($obj, $error); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
else { |
123
|
0
|
|
|
|
|
|
warn "Error occured: $error"; |
124
|
|
|
|
|
|
|
} |
125
|
0
|
|
|
|
|
|
}; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
my $server = AnyEvent::Handle::UDP->new( |
128
|
0
|
|
0
|
|
|
|
bind => [$h{ip}, $h{port} // RADIUS_PORT ], |
129
|
|
|
|
|
|
|
on_recv => $on_read_cb, |
130
|
|
|
|
|
|
|
on_error => $on_error_cb, |
131
|
|
|
|
|
|
|
); |
132
|
0
|
|
|
|
|
|
$obj->handler($server); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# allow to pass custom object |
135
|
0
|
|
0
|
|
|
|
my $packer = $h{packer} || Data::Radius::Packet->new(dict => $h{dictionary}, secret => $h{secret}); |
136
|
0
|
|
|
|
|
|
$obj->packer($packer); |
137
|
|
|
|
|
|
|
|
138
|
0
|
|
|
|
|
|
return $obj; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub load_dictionary { |
142
|
0
|
|
|
0
|
1
|
|
my ($class, $path) = @_; |
143
|
0
|
|
|
|
|
|
my $dict = Data::Radius::Dictionary->load_file($path); |
144
|
|
|
|
|
|
|
|
145
|
0
|
0
|
|
|
|
|
if(ref($class)) { |
146
|
0
|
|
|
|
|
|
$class->packer()->dict($dict); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
0
|
|
|
|
|
|
return $dict; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
1; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
__END__ |