diameter

Rust Implementation of the Diameter Protocol

17 releases (7 breaking)

0.7.1 Jul 21, 2024
0.6.0 May 4, 2024
0.4.0 Mar 24, 2024

29 downloads per month
Used in petrel

MIT license

160KB
4K SLoC

Diameter

Rust Implementation of the Diameter Protocol.

Crates.io MIT licensed Build Status

Overview

This library provides a Rust implementation of the Diameter protocol, as defined by RFC 6733.

Getting Started

Installation

Add this crate to your Rust project by adding the following to your Cargo.toml:

[dependencies]
diameter-rs = "^0.7"

Usage

Diameter Client Example

Below is an example of creating a Diameter client that sends a Credit-Control-Request (CCR) message to a server and waits for a response.

use diameter::avp::flags::M;
use diameter::avp::Enumerated;
use diameter::avp::Identity;
use diameter::avp::UTF8String;
use diameter::avp::Unsigned32;
use diameter::dictionary::{self, Dictionary};
use diameter::flags;
use diameter::transport::DiameterClient;
use diameter::transport::DiameterClientConfig;
use diameter::{ApplicationId, CommandCode, DiameterMessage};
use std::sync::Arc;

#[tokio::main]
async fn main() {
    // Diameter Dictionary
    let dict = Dictionary::new(&[&dictionary::DEFAULT_DICT_XML]);
    let dict = Arc::new(dict);

    // Initialize a Diameter client and connect it to the server
    let client_config = DiameterClientConfig {
        use_tls: false,
        verify_cert: false,
    };
    let mut client = DiameterClient::new("localhost:3868", client_config);
    let mut handler = client.connect().await.unwrap();
    let dict_ref = Arc::clone(&dict);
    tokio::spawn(async move {
        DiameterClient::handle(&mut handler, dict_ref).await;
    });

    // Create a Credit-Control-Request (CCR) Diameter message
    let mut ccr = DiameterMessage::new(
        CommandCode::CreditControl,
        ApplicationId::CreditControl,
        flags::REQUEST,
        1123158611,
        3102381851,
        dict,
    );
    ccr.add_avp(264, None, M, Identity::new("host.example.com").into());
    ccr.add_avp(296, None, M, Identity::new("realm.example.com").into());
    ccr.add_avp(263, None, M, UTF8String::new("ses;12345888").into());
    ccr.add_avp(416, None, M, Enumerated::new(1).into());
    ccr.add_avp(415, None, M, Unsigned32::new(1000).into());

    // Send the CCR message to the server and wait for a response
    let response = client.send_message(ccr).await.unwrap();
    let cca = response.await.unwrap();
    println!("Received response: {}", cca);
}

Diameter Server Example

Below is an example of setting up a Diameter server that listens for incoming requests

use diameter::avp::flags::M;
use diameter::avp::Enumerated;
use diameter::avp::Identity;
use diameter::avp::UTF8String;
use diameter::avp::Unsigned32;
use diameter::dictionary::{self, Dictionary};
use diameter::flags;
use diameter::transport::DiameterServer;
use diameter::transport::DiameterServerConfig;
use diameter::DiameterMessage;
use std::sync::Arc;

#[tokio::main]
async fn main() {
    // Diameter Dictionary
    let dict = Dictionary::new(&[&dictionary::DEFAULT_DICT_XML]);
    let dict = Arc::new(dict);

    // Set up a Diameter server listening on a specific port
    let mut server = DiameterServer::new("0.0.0.0:3868", DiameterServerConfig { native_tls: None })
        .await
        .unwrap();

    // Asynchronously handle incoming requests to the server
    let dict_ref = Arc::clone(&dict);
    server
        .listen(
            move |req| {
                let dict_ref2 = Arc::clone(&dict);
                async move {
                    println!("Received request: {}", req);

                    // Create a response message based on the received request
                    let mut res = DiameterMessage::new(
                        req.get_command_code(),
                        req.get_application_id(),
                        req.get_flags() ^ flags::REQUEST,
                        req.get_hop_by_hop_id(),
                        req.get_end_to_end_id(),
                        dict_ref2,
                    );

                    // Add various Attribute-Value Pairs (AVPs) to the response
                    res.add_avp(264, None, M, Identity::new("host.example.com").into());
                    res.add_avp(296, None, M, Identity::new("realm.example.com").into());
                    res.add_avp(263, None, M, UTF8String::new("ses;123458890").into());
                    res.add_avp(416, None, M, Enumerated::new(1).into());
                    res.add_avp(415, None, M, Unsigned32::new(1000).into());
                    res.add_avp(268, None, M, Unsigned32::new(2001).into());

                    // Return the response
                    Ok(res)
                }
            },
            dict_ref,
        )
        .await
        .unwrap();
}

TLS

Below are examples of how to set up TLS for both the server and the client.

Server Configuration with TLS

    let mut cert_file = File::open("server.crt").unwrap();
    let mut certs = vec![];
    cert_file.read_to_end(&mut certs).unwrap();

    let mut key_file = File::open("server.key").unwrap();
    let mut key = vec![];
    key_file.read_to_end(&mut key).unwrap();

    let pkcs8 = native_tls::Identity::from_pkcs8(&certs, &key).unwrap();
    let config = DiameterServerConfig {
        native_tls: Some(pkcs8),
    };

Client Configuration with TLS

    let client_config = DiameterClientConfig {
        use_tls: true,
        verify_cert: false,
    };

Dependencies

~6–16MB
~223K SLoC