abol 0.1.0

A high-performance, asynchronous, dictionary-driven RADIUS framework.
Documentation
use abol::codegen::rfc2865::Rfc2865Ext;
use abol::core::{Cidr, Code, Request, Response};
use abol::rt::Runtime;
use abol::server::{BoxError, HandlerFn, SecretManager, SecretSource, Server};
use abol_util::rt::tokio::TokioRuntime;

use std::net::SocketAddr;
use std::sync::Arc;

/// A simple "Global Password" provider for your RADIUS server.
///
/// Use this if you want every single client (NAS) to use the same shared secret,
/// regardless of their IP address. It is the easiest way to get started.
pub struct StaticSecretSource {
    /// The shared secret (password) used to authenticate RADIUS packets.
    pub secret: Vec<u8>,
}

impl SecretSource for StaticSecretSource {
    /// Tells the server to use the same secret for the entire internet.
    async fn get_all_secrets(&self) -> Result<Vec<(Cidr, Vec<u8>)>, BoxError> {
        // Define a "Catch-All" range.
        // 0.0.0.0 with a prefix of 0 matches ANY incoming IPv4 address.
        let cidr = Cidr {
            ip: "0.0.0.0".parse()?,
            prefix: 0,
        };

        // Return the mapping: (Everywhere on IPv4) -> (Our Secret)
        Ok(vec![(cidr, self.secret.clone())])
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr: SocketAddr = "0.0.0.0:1812".parse()?;

    // 1. Setup the Secret Manager
    let source = Arc::new(StaticSecretSource {
        secret: b"testing123".to_vec(),
    });
    let secret_manager = SecretManager::new(source, 3600);

    // 2. Define the Request Handler
    let handler = HandlerFn(|request: Request| async move {
        let name = request
            .packet
            .get_user_name()
            .unwrap_or_else(|| "Guest".to_string());

        match request.packet.get_user_password() {
            Some(p) if p.as_bytes() == b"supersecretpassword" => {
                let mut res = request.packet.create_response_packet(Code::AccessAccept);
                res.set_reply_message(format!("Hello {}, access granted!", name));
                Ok(Response { packet: res })
            }
            _ => {
                let res = request.packet.create_response_packet(Code::AccessReject);
                Ok(Response { packet: res })
            }
        }
    });

    // 3. Initialize the Specific Runtime
    let runtime = TokioRuntime::new();
    let socket = runtime.bind(addr).await?;

    // 4. Create and start the server
    let server = Server::new(runtime, socket, secret_manager, handler);

    server.listen_and_serve().await?;
    println!("Abol (Tokio) listening on {}", addr);

    Ok(())
}