| Crates.io | radius-server |
| lib.rs | radius-server |
| version | 0.2.3 |
| created_at | 2025-07-08 13:51:24.286682+00 |
| updated_at | 2025-08-24 19:12:49.152916+00 |
| description | A minimal async multi-threaded RADIUS server framework with dictionary support. |
| homepage | https://github.com/SetliteWeb/radius-server |
| repository | https://github.com/SetliteWeb/radius-server |
| max_upload_size | |
| id | 1742798 |
| size | 82,758 |
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"
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
}
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.
Supports FreeRADIUS-style dictionaries (e.g., dictionary, dictionary.rfc2865, dictionary.vsa).
You can load:
Dictionary::load_embedded()?;
// or
Dictionary::from_file("path/to/dictionary")?;
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
Use Acct-Status-Type to differentiate events:
StartStopInterim-UpdateAccounting-OnAccounting-Offserve_async(...) โ Access (Auth) handlerserve_accounting_async(...) โ Accounting handlertokioMIT 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.