1use std::sync::Arc;
2use crate::{
3 dictionary::Dictionary,
4 packet::{RadiusAttribute, RadiusPacket},
5};
6use md5;
7use tokio::net::UdpSocket;
8
9pub fn build_response_with_auth(
11 mut packet: RadiusPacket,
12 request_authenticator: [u8; 16],
13 secret: &str,
14) -> RadiusPacket {
15 let mut buf = Vec::new();
16 buf.push(packet.code);
17 buf.push(packet.identifier);
18 buf.extend_from_slice(&[0x00, 0x00]); buf.extend_from_slice(&request_authenticator);
20
21 for attr in &packet.attributes {
22 buf.push(attr.typ);
23 buf.push(attr.len);
24 buf.extend_from_slice(&attr.value);
25 }
26
27 let length = buf.len() as u16;
28 buf[2] = (length >> 8) as u8;
29 buf[3] = (length & 0xFF) as u8;
30
31 buf.extend_from_slice(secret.as_bytes());
32
33 let hash = md5::compute(&buf);
34 let mut authenticator = [0u8; 16];
35 authenticator.copy_from_slice(&hash.0);
36
37 packet.length = length;
38 packet.authenticator = authenticator;
39
40 packet
41}
42
43pub fn handle(packet: RadiusPacket, dict: Arc<Dictionary>) -> Result<RadiusPacket, String> {
45 println!("🔍 Handling RADIUS packet ID: {}", packet.identifier);
46
47 for attr in &packet.attributes {
48 let attr_code = attr.typ as u32;
49 match dict.attributes.get(&attr_code) {
50 Some(def) => {
51 let name = &def.name;
52 match std::str::from_utf8(&attr.value) {
53 Ok(s) => println!("→ {}: {}", name, s.trim()),
54 Err(_) => println!("→ {}: {:?}", name, attr.value),
55 }
56 }
57 None => println!("→ Unknown Attribute Type {}: {:?}", attr.typ, attr.value),
58 }
59 }
60
61 let mut attributes = vec![
62 RadiusAttribute::reply_message("Access granted via Rust RADIUS server."),
63 RadiusAttribute::session_timeout(3600),
64 RadiusAttribute::idle_timeout(300),
65 RadiusAttribute::wispr_bandwidth_max_up(512_000),
66 RadiusAttribute::wispr_bandwidth_max_down(1_000_000),
67 ];
68
69 if let Some(user_attr) = packet.attributes.iter().find(|a| a.typ == 1) {
71 if let Ok(username) = std::str::from_utf8(&user_attr.value) {
72 attributes.push(RadiusAttribute::user_name(username));
73 }
74 }
75
76 let accept = RadiusPacket::access_accept(packet.identifier, attributes);
77 let response = build_response_with_auth(accept, packet.authenticator, "test123");
78 Ok(response)
79}
80
81
82
83
84pub fn verify_accounting_request_authenticator(
86 packet: &[u8],
87 secret: &str,
88 received_auth: [u8; 16],
89) -> bool {
90 if packet.len() < 20 {
91 return false;
92 }
93
94 let mut data = Vec::from(&packet[0..4]);
95data.extend_from_slice(&[0u8; 16]); data.extend_from_slice(&packet[20..]);
97 data.extend_from_slice(secret.as_bytes());
98
99 let hash = md5::compute(&data);
100 hash.0 == received_auth
101}
102
103pub fn build_accounting_response(identifier: u8, request_auth: [u8; 16], secret: &str) -> Vec<u8> {
105 let mut buf = vec![5, identifier, 0x00, 0x14]; let mut temp = buf.clone();
107 temp.extend_from_slice(&request_auth);
108 temp.extend_from_slice(secret.as_bytes());
109
110 let hash = md5::compute(&temp);
111 buf.extend_from_slice(&hash.0);
112 buf
113}
114
115pub async fn serve_accounting_async<F, Fut>(
117 addr: &str,
118 dict: Arc<Dictionary>,
119 secret: &str,
120 handler: F,
121) -> Result<(), Box<dyn std::error::Error>>
122where
123 F: Fn(RadiusPacket) -> Fut + Send + Sync + 'static,
124 Fut: std::future::Future<Output = Result<(), String>> + Send,
125{
126 let socket = UdpSocket::bind(addr).await?;
127 println!("📡 Accounting server listening on {addr}");
128
129 let mut buf = [0u8; 1024];
130 loop {
131 let (len, src) = socket.recv_from(&mut buf).await?;
132 let raw_packet = &buf[..len];
133
134 let packet = match RadiusPacket::from_bytes(raw_packet) {
135 Ok(p) => p,
136 Err(e) => {
137 eprintln!("❌ Failed to parse: {e}");
138 continue;
139 }
140 };
141
142 if !verify_accounting_request_authenticator(raw_packet, secret, packet.authenticator) {
143 eprintln!("🚫 Invalid accounting authenticator.");
144 continue;
145 }
146
147 if let Err(e) = handler(packet.clone()).await {
148 eprintln!("⚠️ Handler error: {e}");
149 }
150
151 let response = build_accounting_response(packet.identifier, packet.authenticator, secret);
152 socket.send_to(&response, src).await?;
153 }
154}