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    // version: u8,
82    // message_length: u32, // 24 bits
83    // command_flag: u8,
84    // command_code: CommandCode, // 24 bits
85    // application_id: ApplicationId,
86    // hop_by_hop: u32,
87    // end_to_end: u32,
88    
89    pub fn get_version(&self) -> u8 {
90        self.header.version
91    }
92
93    pub fn get_length(&self) -> u32 {
94        self.header.message_length
95    }
96    
97    pub fn get_command_flag(&self) -> u8 {
98        self.header.command_flag
99    }
100    
101    pub fn get_command_code(&self) -> &CommandCode {
102        &self.header.command_code
103    }
104    
105    pub fn get_application_id(&self) -> &ApplicationId {
106        &self.header.application_id
107    }
108    
109    pub fn get_hop_by_hop(&self) -> u32 {
110        self.header.hop_by_hop
111    }
112    
113    pub fn get_end_to_end(&self) -> u32 {
114        self.header.end_to_end
115    }
116    
117    pub fn get_avps(&self) -> &Vec<Avp> {
118        &self.avps
119    }
120    
121    pub fn get_avp(&self, code: u32) -> Option<&Avp> {
122        self.avps.iter().find(|avp| avp.get_code() == code)
123    }
124
125    pub fn add(&mut self, avp: Avp) {
126        self.header.message_length += avp.get_length() + avp.get_padding();
127        self.avps.push(avp);
128    }
129
130    pub fn add_avp<T: Into<AvpValue>>(
131        &mut self,
132        code: u32,
133        flags: AvpFlags,
134        vendor_id: Option<u32>,
135        value: T,
136    ) {
137        let avp: Avp = Avp::new(code, flags, vendor_id, value);
138        self.add(avp);
139    }
140
141    pub fn encode_to<W: Write>(&mut self, writer: &mut W) -> DiameterResult<()> {
142        writer.write(&self.header.version.to_be_bytes())?;
143        writer.write(&self.header.message_length.to_be_bytes()[1..])?;
144        writer.write(&self.header.command_flag.to_be_bytes())?;
145        writer.write(&self.header.command_code.get_code().to_be_bytes()[1..])?;
146        writer.write(&self.header.application_id.value().to_be_bytes())?;
147        writer.write(&self.header.hop_by_hop.to_be_bytes())?;
148        writer.write(&self.header.end_to_end.to_be_bytes())?;
149        for avp in self.avps.iter_mut() {
150            avp.encode_to(writer)?;
151        }
152        Ok(())
153    }
154
155    pub fn decode_from<R: Read>(
156        reader: &mut R,
157        dict: Arc<Dictionary>,
158    ) -> DiameterResult<DiameterMessage> {
159        let mut b = [0u8; 20];
160        reader.read_exact(&mut b)?;
161
162        let version = b[0];
163        let mut message_length = u32::from_be_bytes([0, b[1], b[2], b[3]]);
164        let command_flag = b[4];
165        let command_code = u32::from_be_bytes([b[4], b[5], b[6], b[7]]);
166        let application_id = u32::from_be_bytes([b[8], b[9], b[10], b[11]]);
167        let hop_by_hop = u32::from_be_bytes([b[12], b[13], b[14], b[15]]);
168        let end_to_end = u32::from_be_bytes([b[16], b[17], b[18], b[19]]);
169
170        let header = DiameterHeader {
171            version,
172            message_length,
173            command_flag,
174            application_id: ApplicationId::try_from(application_id)?,
175            command_code: CommandCode::try_from(command_code)?,
176            hop_by_hop,
177            end_to_end,
178        };
179
180        let mut message = DiameterMessage {
181            header,
182            avps: vec![],
183        };
184
185        message_length -= 20;
186        while message_length > 0 {
187            let avp = Avp::decode_from(reader, Arc::clone(&dict))?;
188            message_length = message_length - avp.get_length() - avp.get_padding();
189            message.add(avp);
190        }
191        Ok(message)
192    }
193}