Sure! Here's the full raw markdown for your updated README.md, with both Access and Accounting support documented cleanly:
# radius-server
A lightweight, async RADIUS server library written in Rust. Built for performance, extensibility, and compatibility with FreeRADIUS-style dictionaries.
---
## โจ Features
- โ
Parses and builds RADIUS packets
- ๐ Loads FreeRADIUS-style dictionaries
- ๐ Shared secret authenticator validation
- โ๏ธ Custom packet handlers via async closures
- ๐งฉ Vendor-Specific Attribute (VSA) support
- ๐ก Built-in support for **Access** and **Accounting**
- ๐ Fully async with `tokio`
---
## ๐ฆ Usage
### Add to your `Cargo.toml`:
```toml
radius-server = "0.2.0"
๐ Access Server Example (Auth)
use std::sync::Arc;
use radius_server::{
dictionary::Dictionary,
packet::RadiusAttribute,
serve_async,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let dict = Arc::new(Dictionary::load_embedded()?);
let secret = "test123";
serve_async("0.0.0.0:1812", dict, secret, move |packet| async move {
println!("๐ Incoming ID {} from {:?}", packet.identifier, packet.username());
if let Some(username) = packet.username() {
if username.trim() == "ec:30:b3:6d:24:6a" {
Ok(packet.reply_accept(vec![
RadiusAttribute::session_timeout(3600),
RadiusAttribute::reply_message("Welcome, admin."),
]))
} else {
Ok(packet.reply_reject("User not allowed"))
}
} else {
Ok(packet.reply_reject("Missing username"))
}
}).await
}
๐ Accounting Server Example
use std::sync::Arc;
use radius_server::{
dictionary::Dictionary,
handler::serve_accounting_async,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let dict = Arc::new(Dictionary::load_embedded()?);
let secret = "test123";
serve_accounting_async("0.0.0.0:1813", dict, secret, move |packet| async move {
println!("๐จ Accounting ID {} from {:?}", packet.identifier, packet.username());
for attr in packet.attributes {
println!(" โ Type {}: {:?}", attr.typ, attr.value);
}
Ok(())
}).await
}
Accounting-Request packets (Code 4) are validated against RFC 2866 and automatically responded to with a proper Accounting-Response (Code 5) including a correct MD5 authenticator.
๐ Dictionary Support
Supports FreeRADIUS-style dictionaries (e.g., dictionary, dictionary.rfc2865, dictionary.vsa).
You can load:
Dictionary::load_embedded()?;
Dictionary::from_file("path/to/dictionary")?;
๐งช Testing Tools
You can test with radclient:
echo "User-Name = test, Acct-Status-Type = Start" | radclient -x 127.0.0.1:1813 acct test123
Or test authentication:
echo "User-Name = ec:30:b3:6d:24:6a, User-Password = secret" | radclient -x 127.0.0.1 auth test123
๐ Status Types You Can Handle
Use Acct-Status-Type to differentiate events:
Start
Stop
Interim-Update
Accounting-On
Accounting-Off
๐ Architecture
serve_async(...) โ Access (Auth) handler
serve_accounting_async(...) โ Accounting handler
- Use closures for custom logic
- Runs fully async on
tokio
๐ License
MIT License. Contributions welcome.
---
Let me know if you'd like to:
- Add a badge section (crates.io, docs.rs, license)
- Add examples for custom dictionaries
- Generate it as an `mdbook` or `docs.rs` doc
Happy to help.