diameter_interface/modeling/
diameter.rs

1//! # Diameter Protocol Message
2//! This crate provides functionalities for handling Diameter protocol messages as defined in RFC 6733.
3//!
4//!
5//! ## Raw Packet Format
6//! The diagram below illustrates the raw packet format for the Diameter header:
7//! ```text
8//!   0                   1                   2                   3
9//!   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
10//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11//!  |    Version    |                 Message Length                |
12//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13//!  | command flags |                  Command-Code                 |
14//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15//!  |                         Application-ID                        |
16//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17//!  |                      Hop-by-Hop Identifier                    |
18//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19//!  |                      End-to-End Identifier                    |
20//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21//!  |                              AVPs                             |
22//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23//!  |                              ...                              |
24//!  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25//!
26//!  Command Flags:
27//!    0 1 2 3 4 5 6 7
28//!   +-+-+-+-+-+-+-+-+  R(equest), P(roxyable), E(rror)
29//!   |R P E T r r r r|  T(Potentially re-transmitted message), r(eserved)
30//!   +-+-+-+-+-+-+-+-+
31//! ```
32
33use crate::errors::DiameterResult;
34use crate::modeling::avp::avp::{Avp, AvpFlags, AvpValue};
35use crate::modeling::message::application_id::ApplicationId;
36use crate::modeling::message::command_code::CommandCode;
37use crate::modeling::message::command_flags::CommandFlag;
38use crate::modeling::message::dictionary::Dictionary;
39use std::io::{Read, Write};
40use std::sync::Arc;
41
42#[derive(Debug)]
43pub struct DiameterMessage {
44    header: DiameterHeader,
45    avps: Vec<Avp>,
46}
47
48#[derive(Debug)]
49pub struct DiameterHeader {
50    version: u8,
51    message_length: u32, // 24 bits
52    command_flag: u8,
53    command_code: CommandCode, // 24 bits
54    application_id: ApplicationId,
55    hop_by_hop: u32,
56    end_to_end: u32,
57}
58
59impl DiameterMessage {
60    pub fn new(
61        command_flag: CommandFlag,
62        command_code: CommandCode,
63        application_id: ApplicationId,
64        hop_by_hop: u32,
65        end_to_end: u32,
66    ) -> Self {
67        Self {
68            header: DiameterHeader {
69                version: 1,
70                message_length: 20,
71                command_flag: command_flag.value(),
72                command_code,
73                application_id,
74                hop_by_hop,
75                end_to_end,
76            },
77            avps: vec![],
78        }
79    }
80
81    pub fn add(&mut self, avp: Avp) {
82        self.header.message_length += avp.get_length() + avp.get_padding();
83        self.avps.push(avp);
84    }
85
86    pub fn add_avp<T: Into<AvpValue>>(
87        &mut self,
88        code: u32,
89        flags: AvpFlags,
90        vendor_id: Option<u32>,
91        value: T,
92    ) {
93        let avp: Avp = Avp::new(code, flags, vendor_id, value);
94        self.add(avp);
95    }
96
97    pub fn encode_to<W: Write>(&mut self, writer: &mut W) -> DiameterResult<()> {
98        writer.write(&self.header.version.to_be_bytes())?;
99        writer.write(&self.header.message_length.to_be_bytes()[1..])?;
100        writer.write(&self.header.command_flag.to_be_bytes())?;
101        writer.write(&self.header.command_code.get_code().to_be_bytes()[1..])?;
102        writer.write(&self.header.application_id.value().to_be_bytes())?;
103        writer.write(&self.header.hop_by_hop.to_be_bytes())?;
104        writer.write(&self.header.end_to_end.to_be_bytes())?;
105        for avp in self.avps.iter_mut() {
106            avp.encode_to(writer)?;
107        }
108        Ok(())
109    }
110
111    pub fn decode_from<R: Read>(
112        reader: &mut R,
113        dict: Arc<Dictionary>,
114    ) -> DiameterResult<DiameterMessage> {
115        let mut b = [0u8; 20];
116        reader.read_exact(&mut b)?;
117
118        let version = b[0];
119        let mut message_length = u32::from_be_bytes([0, b[1], b[2], b[3]]);
120        let command_flag = b[4];
121        let command_code = u32::from_be_bytes([b[4], b[5], b[6], b[7]]);
122        let application_id = u32::from_be_bytes([b[8], b[9], b[10], b[11]]);
123        let hop_by_hop = u32::from_be_bytes([b[12], b[13], b[14], b[15]]);
124        let end_to_end = u32::from_be_bytes([b[16], b[17], b[18], b[19]]);
125
126        let header = DiameterHeader {
127            version,
128            message_length,
129            command_flag,
130            application_id: ApplicationId::try_from(application_id)?,
131            command_code: CommandCode::try_from(command_code)?,
132            hop_by_hop,
133            end_to_end,
134        };
135
136        let mut message = DiameterMessage {
137            header,
138            avps: vec![],
139        };
140
141        message_length -= 20;
142        while message_length > 0 {
143            let avp = Avp::decode_from(reader, Arc::clone(&dict))?;
144            message_length = message_length - avp.get_length() - avp.get_padding();
145            message.add(avp);
146        }
147        Ok(message)
148    }
149}