1use nom::bytes::streaming::take;
2use nom::combinator::{map, map_parser, verify};
3use nom::number::streaming::{be_u32, be_u8};
4use nom::{Err, IResult, Needed};
5use std::net::Ipv4Addr;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub struct RadiusAttributeType(pub u8);
9
10#[allow(non_upper_case_globals)]
11impl RadiusAttributeType {
12 pub const UserName: RadiusAttributeType = RadiusAttributeType(1);
13 pub const UserPassword: RadiusAttributeType = RadiusAttributeType(2);
14 pub const ChapPassword: RadiusAttributeType = RadiusAttributeType(3);
15 pub const NasIPAddress: RadiusAttributeType = RadiusAttributeType(4);
16 pub const NasPort: RadiusAttributeType = RadiusAttributeType(5);
17 pub const ServiceType: RadiusAttributeType = RadiusAttributeType(6);
18 pub const FramedProtocol: RadiusAttributeType = RadiusAttributeType(7);
19 pub const FramedIPAddress: RadiusAttributeType = RadiusAttributeType(8);
20 pub const FramedIPNetmask: RadiusAttributeType = RadiusAttributeType(9);
21 pub const FramedRouting: RadiusAttributeType = RadiusAttributeType(10);
22 pub const FilterId: RadiusAttributeType = RadiusAttributeType(11);
23 pub const FramedMTU: RadiusAttributeType = RadiusAttributeType(12);
24 pub const FramedCompression: RadiusAttributeType = RadiusAttributeType(13);
25 pub const VendorSpecific: RadiusAttributeType = RadiusAttributeType(26);
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub struct ServiceType(pub u32);
30
31#[allow(non_upper_case_globals)]
32impl ServiceType {
33 pub const Login: ServiceType = ServiceType(1);
34 pub const Framed: ServiceType = ServiceType(2);
35 pub const CallbackLogin: ServiceType = ServiceType(3);
36 pub const CallbackFramed: ServiceType = ServiceType(4);
37 pub const Outbound: ServiceType = ServiceType(5);
38 pub const Administrative: ServiceType = ServiceType(6);
39 pub const NasPrompt: ServiceType = ServiceType(7);
40 pub const AuthenticateOnly: ServiceType = ServiceType(8);
41 pub const CallbackNasPrompt: ServiceType = ServiceType(9);
42 pub const CallCheck: ServiceType = ServiceType(10);
43 pub const CallbackAdministrative: ServiceType = ServiceType(11);
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub struct FramedRouting(pub u32);
48
49#[allow(non_upper_case_globals)]
50impl FramedRouting {
51 pub const None: FramedRouting = FramedRouting(0);
52 pub const Send: FramedRouting = FramedRouting(1);
53 pub const Receive: FramedRouting = FramedRouting(2);
54 pub const SendReceive: FramedRouting = FramedRouting(3);
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub struct FramedProtocol(pub u32);
59
60#[allow(non_upper_case_globals)]
61impl FramedProtocol {
62 pub const Ppp: FramedProtocol = FramedProtocol(1);
63 pub const Slip: FramedProtocol = FramedProtocol(2);
64 pub const Arap: FramedProtocol = FramedProtocol(3);
66 pub const Gandalf: FramedProtocol = FramedProtocol(4);
68 pub const Xylogics: FramedProtocol = FramedProtocol(5);
70 pub const X75: FramedProtocol = FramedProtocol(6);
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub struct FramedCompression(pub u32);
85
86#[allow(non_upper_case_globals)]
87impl FramedCompression {
88 pub const None: FramedCompression = FramedCompression(0);
90 pub const TcpIp: FramedCompression = FramedCompression(1);
92 pub const Ipx: FramedCompression = FramedCompression(2);
94 pub const StaticLzs: FramedCompression = FramedCompression(3);
96}
97
98#[derive(Clone, Debug, PartialEq)]
99pub enum RadiusAttribute<'a> {
100 UserName(&'a [u8]),
101 UserPassword(&'a [u8]),
102 ChapPassword(u8, &'a [u8]),
103 NasIPAddress(Ipv4Addr),
104 NasPort(u32),
105 ServiceType(ServiceType),
106 FramedProtocol(FramedProtocol),
107 FramedIPAddress(Ipv4Addr),
108 FramedIPNetmask(Ipv4Addr),
109 FramedRouting(FramedRouting),
110 FilterId(&'a [u8]),
111 FramedMTU(u32),
112 FramedCompression(FramedCompression),
113 VendorSpecific(u32, &'a [u8]),
114 CalledStationId(&'a [u8]),
115 CallingStationId(&'a [u8]),
116
117 Unknown(u8, &'a [u8]),
118}
119
120fn parse_attribute_content(i: &[u8], t: u8) -> IResult<&[u8], RadiusAttribute> {
121 const EMPTY: &[u8] = &[];
122 match t {
123 1 => Ok((EMPTY, RadiusAttribute::UserName(i))),
124 2 => Ok((EMPTY, RadiusAttribute::UserPassword(i))),
125 3 => {
126 if i.len() < 2 {
127 return Err(Err::Incomplete(Needed::new(2)));
128 }
129 Ok((EMPTY, RadiusAttribute::ChapPassword(i[0], &i[1..])))
130 }
131 4 => map(take(4usize), |v: &[u8]| {
132 RadiusAttribute::NasIPAddress(Ipv4Addr::new(v[0], v[1], v[2], v[3]))
133 })(i),
134 5 => map(be_u32, RadiusAttribute::NasPort)(i),
135 6 => map(be_u32, |v| RadiusAttribute::ServiceType(ServiceType(v)))(i),
136 7 => map(be_u32, |v| {
137 RadiusAttribute::FramedProtocol(FramedProtocol(v))
138 })(i),
139 8 => map(take(4usize), |v: &[u8]| {
140 RadiusAttribute::FramedIPAddress(Ipv4Addr::new(v[0], v[1], v[2], v[3]))
141 })(i),
142 9 => map(take(4usize), |v: &[u8]| {
143 RadiusAttribute::FramedIPNetmask(Ipv4Addr::new(v[0], v[1], v[2], v[3]))
144 })(i),
145 10 => map(be_u32, |v| RadiusAttribute::FramedRouting(FramedRouting(v)))(i),
146 11 => Ok((EMPTY, RadiusAttribute::FilterId(i))),
147 12 => map(be_u32, RadiusAttribute::FramedMTU)(i),
148 13 => map(be_u32, |v| {
149 RadiusAttribute::FramedCompression(FramedCompression(v))
150 })(i),
151 26 => {
152 if i.len() < 5 {
153 return Err(Err::Incomplete(Needed::new(5)));
154 }
155 let (i, vendorid) = be_u32(i)?;
156 let vendordata = i;
157 Ok((EMPTY, RadiusAttribute::VendorSpecific(vendorid, vendordata)))
158 }
159 30 => Ok((EMPTY, RadiusAttribute::CalledStationId(i))),
160 31 => Ok((EMPTY, RadiusAttribute::CallingStationId(i))),
161 _ => Ok((EMPTY, RadiusAttribute::Unknown(t, i))),
162 }
163}
164
165pub fn parse_radius_attribute(i: &[u8]) -> IResult<&[u8], RadiusAttribute> {
166 let (i, t) = be_u8(i)?;
167 let (i, l) = verify(be_u8, |&n| n >= 2)(i)?;
168 let (i, v) = map_parser(take(l - 2), |d| parse_attribute_content(d, t))(i)?;
169 Ok((i, v))
170}
171
172#[cfg(test)]
173mod tests {
174 use crate::radius_attr::*;
175 use nom::error::{make_error, ErrorKind};
176 use nom::Err;
177
178 #[test]
179 fn test_attribute_invalid() {
180 let data = &[255, 0, 2, 2];
181 assert_eq!(
182 parse_radius_attribute(data),
183 Err(Err::Error(make_error(&data[1..], ErrorKind::Verify)))
184 );
185 }
186
187 #[test]
188 fn test_attribute_empty() {
189 let data = &[255, 2, 2, 2];
190 assert_eq!(
191 parse_radius_attribute(data),
192 Ok((&data[2..], RadiusAttribute::Unknown(255, &[])))
193 );
194 }
195
196 #[test]
197 fn test_attribute() {
198 let data = &[255, 4, 2, 2];
199 assert_eq!(
200 parse_radius_attribute(data),
201 Ok((&b""[..], RadiusAttribute::Unknown(255, &[2, 2])))
202 );
203 }
204
205 #[test]
206 fn test_parse_vendor_specific() {
207 {
208 let data = &[26, 7, 0, 1, 2, 3, 120];
209 assert_eq!(
210 parse_radius_attribute(data),
211 Ok((
212 &b""[..],
213 RadiusAttribute::VendorSpecific(66051, "x".as_bytes())
214 ))
215 )
216 }
217 {
218 let data = &[26, 6, 0, 1, 2, 3];
219 assert_eq!(
220 parse_radius_attribute(data),
221 Err(Err::Incomplete(Needed::new(5)))
222 )
223 }
224 }
225
226 #[test]
227 fn test_parse_called_station_id() {
228 {
229 let data = &[
230 30, 19, 97, 97, 45, 98, 98, 45, 99, 99, 45, 100, 100, 45, 101, 101, 45, 102, 102,
231 ];
232 assert_eq!(
233 parse_radius_attribute(data),
234 Ok((
235 &b""[..],
236 RadiusAttribute::CalledStationId("aa-bb-cc-dd-ee-ff".as_bytes())
237 ))
238 )
239 }
240 }
241
242 #[test]
243 fn test_parse_calling_station_id() {
244 {
245 let data = &[
246 31, 19, 97, 97, 45, 98, 98, 45, 99, 99, 45, 100, 100, 45, 101, 101, 45, 102, 102,
247 ];
248 assert_eq!(
249 parse_radius_attribute(data),
250 Ok((
251 &b""[..],
252 RadiusAttribute::CallingStationId("aa-bb-cc-dd-ee-ff".as_bytes())
253 ))
254 )
255 }
256 }
257}