17 releases (7 breaking)

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

#1230 in Network programming


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

~4–15MB
~195K SLoC