Sure! Here's the **full raw markdown** for your updated `README.md`, with both **Access** and **Accounting** support documented cleanly:
---
````markdown
# 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)
```rust
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
```rust
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:
```rust
Dictionary::load_embedded()?;
// or
Dictionary::from_file("path/to/dictionary")?;
```
---
## ๐งช Testing Tools
You can test with `radclient`:
```bash
Or test authentication:
```bash
---
## ๐ 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.
```