Skip to main content

smpp_codec/
tlv.rs

1//! # Tagged Length Value (TLV) Parameters
2//!
3//! This module handles the encoding and decoding of SMPP Optional Parameters (TLVs).
4//! It provides a registry of standard tags and mechanisms to parse and serialize them.
5
6use crate::common::PduError;
7use std::io::{Cursor, Read, Write};
8
9// --- Standard SMPP Optional Parameter Tags ---
10/// Standard SMPP Optional Parameter Tags as defined in SMPP Spec (v3.4 / v5.0).
11pub mod tags {
12    /// Destination Address Subunit
13    pub const DEST_ADDR_SUBUNIT: u16 = 0x0005;
14    /// Destination Network Type
15    pub const DEST_NETWORK_TYPE: u16 = 0x0006;
16    /// Destination Bearer Type
17    pub const DEST_BEARER_TYPE: u16 = 0x0007;
18    /// Destination Telematics ID
19    pub const DEST_TELEMATICS_ID: u16 = 0x0008;
20    /// Source Address Subunit
21    pub const SOURCE_ADDR_SUBUNIT: u16 = 0x000D;
22    /// Source Network Type
23    pub const SOURCE_NETWORK_TYPE: u16 = 0x000E;
24    /// Source Bearer Type
25    pub const SOURCE_BEARER_TYPE: u16 = 0x000F;
26    /// Source Telematics ID
27    pub const SOURCE_TELEMATICS_ID: u16 = 0x0010;
28    /// QoS Time To Live
29    pub const QOS_TIME_TO_LIVE: u16 = 0x0017;
30    /// Payload Type
31    pub const PAYLOAD_TYPE: u16 = 0x0019;
32    /// Additional Status Info Text
33    pub const ADDITIONAL_STATUS_INFO_TEXT: u16 = 0x001D;
34    /// Receipted Message ID
35    pub const RECEIPTED_MESSAGE_ID: u16 = 0x001E;
36    /// MS Message Wait Facilities
37    pub const MS_MSG_WAIT_FACILITIES: u16 = 0x0030;
38    /// Privacy Indicator
39    pub const PRIVACY_INDICATOR: u16 = 0x0201;
40    /// Source Subaddress
41    pub const SOURCE_SUBADDRESS: u16 = 0x0202;
42    /// Destination Subaddress
43    pub const DEST_SUBADDRESS: u16 = 0x0203;
44    /// User Message Reference
45    pub const USER_MESSAGE_REFERENCE: u16 = 0x0204;
46    /// User Response Code
47    pub const USER_RESPONSE_CODE: u16 = 0x0205;
48    /// Source Port
49    pub const SOURCE_PORT: u16 = 0x020A;
50    /// Destination Port
51    pub const DESTINATION_PORT: u16 = 0x020B;
52    /// SAR Message Reference Number
53    pub const SAR_MSG_REF_NUM: u16 = 0x020C;
54    /// Language Indicator
55    pub const LANGUAGE_INDICATOR: u16 = 0x020D;
56    /// SAR Total Segments
57    pub const SAR_TOTAL_SEGMENTS: u16 = 0x020E;
58    /// SAR Segment Sequence Number
59    pub const SAR_SEGMENT_SEQNUM: u16 = 0x020F;
60    /// SC Interface Version
61    pub const SC_INTERFACE_VERSION: u16 = 0x0210;
62    /// Callback Number Presentation Indicator
63    pub const CALLBACK_NUM_PRES_IND: u16 = 0x0302;
64    /// Callback Number Alphanumeric Tag
65    pub const CALLBACK_NUM_ATAG: u16 = 0x0303;
66    /// Number of Messages
67    pub const NUMBER_OF_MESSAGES: u16 = 0x0304;
68    /// Callback Number
69    pub const CALLBACK_NUM: u16 = 0x0381;
70    /// DPF Result
71    pub const DPF_RESULT: u16 = 0x0420;
72    /// Set DPF
73    pub const SET_DPF: u16 = 0x0421;
74    /// MS Availability Status
75    pub const MS_AVAILABILITY_STATUS: u16 = 0x0422;
76    /// Network Error Code
77    pub const NETWORK_ERROR_CODE: u16 = 0x0423;
78    /// Message Payload
79    pub const MESSAGE_PAYLOAD: u16 = 0x0424;
80    /// Delivery Failure Reason
81    pub const DELIVERY_FAILURE_REASON: u16 = 0x0425;
82    /// More Messages To Send
83    pub const MORE_MESSAGES_TO_SEND: u16 = 0x0426;
84    /// Message State
85    pub const MESSAGE_STATE: u16 = 0x0427;
86    /// Congestion State
87    pub const CONGESTION_STATE: u16 = 0x0428;
88    /// USSD Service Op
89    pub const USSD_SERVICE_OP: u16 = 0x0501;
90    /// Display Time
91    pub const DISPLAY_TIME: u16 = 0x1201;
92    /// SMS Signal
93    pub const SMS_SIGNAL: u16 = 0x1203;
94    /// MS Validity
95    pub const MS_VALIDITY: u16 = 0x1204;
96    /// Alert On Message Delivery
97    pub const ALERT_ON_MESSAGE_DELIVERY: u16 = 0x130C;
98    /// ITS Reply Type
99    pub const ITS_REPLY_TYPE: u16 = 0x1380;
100    /// ITS Session Info
101    pub const ITS_SESSION_INFO: u16 = 0x1383;
102    // --- Broadcast / Multicast specific (SMPP v5.0) ---
103    /// Broadcast Area Identifier
104    pub const BROADCAST_AREA_IDENTIFIER: u16 = 0x0606;
105    /// Broadcast Content Type
106    pub const BROADCAST_CONTENT_TYPE: u16 = 0x0601;
107    /// Broadcast Repetition Number
108    pub const BROADCAST_REP_NUM: u16 = 0x0602;
109    /// Broadcast Frequency Interval
110    pub const BROADCAST_FREQUENCY_INTERVAL: u16 = 0x0603;
111    /// Broadcast Area Success
112    pub const BROADCAST_AREA_SUCCESS: u16 = 0x0608;
113    /// Broadcast End Time
114    pub const BROADCAST_END_TIME: u16 = 0x0609;
115    /// Broadcast Service Group
116    pub const BROADCAST_SERVICE_GROUP: u16 = 0x060A;
117    /// Broadcast Channel Indicator
118    pub const BROADCAST_CHANNEL_INDICATOR: u16 = 0x0600;
119}
120
121/// Helper function to get tag hex code from name (e.g., "SAR_MSG_REF_NUM" -> 0x020C)
122/// Returns 0 if not found.
123pub fn get_tag_by_name(name: &str) -> u16 {
124    match name {
125        "dest_addr_subunit" | "DEST_ADDR_SUBUNIT" => tags::DEST_ADDR_SUBUNIT,
126        "dest_network_type" | "DEST_NETWORK_TYPE" => tags::DEST_NETWORK_TYPE,
127        "dest_bearer_type" | "DEST_BEARER_TYPE" => tags::DEST_BEARER_TYPE,
128        "dest_telematics_id" | "DEST_TELEMATICS_ID" => tags::DEST_TELEMATICS_ID,
129        "source_addr_subunit" | "SOURCE_ADDR_SUBUNIT" => tags::SOURCE_ADDR_SUBUNIT,
130        "source_network_type" | "SOURCE_NETWORK_TYPE" => tags::SOURCE_NETWORK_TYPE,
131        "source_bearer_type" | "SOURCE_BEARER_TYPE" => tags::SOURCE_BEARER_TYPE,
132        "source_telematics_id" | "SOURCE_TELEMATICS_ID" => tags::SOURCE_TELEMATICS_ID,
133        "qos_time_to_live" | "QOS_TIME_TO_LIVE" => tags::QOS_TIME_TO_LIVE,
134        "payload_type" | "PAYLOAD_TYPE" => tags::PAYLOAD_TYPE,
135        "additional_status_info_text" | "ADDITIONAL_STATUS_INFO_TEXT" => {
136            tags::ADDITIONAL_STATUS_INFO_TEXT
137        }
138        "receipted_message_id" | "RECEIPTED_MESSAGE_ID" => tags::RECEIPTED_MESSAGE_ID,
139        "ms_msg_wait_facilities" | "MS_MSG_WAIT_FACILITIES" => tags::MS_MSG_WAIT_FACILITIES,
140        "privacy_indicator" | "PRIVACY_INDICATOR" => tags::PRIVACY_INDICATOR,
141        "source_subaddress" | "SOURCE_SUBADDRESS" => tags::SOURCE_SUBADDRESS,
142        "dest_subaddress" | "DEST_SUBADDRESS" => tags::DEST_SUBADDRESS,
143        "user_message_reference" | "USER_MESSAGE_REFERENCE" => tags::USER_MESSAGE_REFERENCE,
144        "user_response_code" | "USER_RESPONSE_CODE" => tags::USER_RESPONSE_CODE,
145        "source_port" | "SOURCE_PORT" => tags::SOURCE_PORT,
146        "destination_port" | "DESTINATION_PORT" => tags::DESTINATION_PORT,
147        "sar_msg_ref_num" | "SAR_MSG_REF_NUM" => tags::SAR_MSG_REF_NUM,
148        "language_indicator" | "LANGUAGE_INDICATOR" => tags::LANGUAGE_INDICATOR,
149        "sar_total_segments" | "SAR_TOTAL_SEGMENTS" => tags::SAR_TOTAL_SEGMENTS,
150        "sar_segment_seqnum" | "SAR_SEGMENT_SEQNUM" => tags::SAR_SEGMENT_SEQNUM,
151        "sc_interface_version" | "SC_INTERFACE_VERSION" => tags::SC_INTERFACE_VERSION,
152        "callback_num_pres_ind" | "CALLBACK_NUM_PRES_IND" => tags::CALLBACK_NUM_PRES_IND,
153        "callback_num_atag" | "CALLBACK_NUM_ATAG" => tags::CALLBACK_NUM_ATAG,
154        "number_of_messages" | "NUMBER_OF_MESSAGES" => tags::NUMBER_OF_MESSAGES,
155        "callback_num" | "CALLBACK_NUM" => tags::CALLBACK_NUM,
156        "dpf_result" | "DPF_RESULT" => tags::DPF_RESULT,
157        "set_dpf" | "SET_DPF" => tags::SET_DPF,
158        "ms_availability_status" | "MS_AVAILABILITY_STATUS" => tags::MS_AVAILABILITY_STATUS,
159        "network_error_code" | "NETWORK_ERROR_CODE" => tags::NETWORK_ERROR_CODE,
160        "message_payload" | "MESSAGE_PAYLOAD" => tags::MESSAGE_PAYLOAD,
161        "delivery_failure_reason" | "DELIVERY_FAILURE_REASON" => tags::DELIVERY_FAILURE_REASON,
162        "more_messages_to_send" | "MORE_MESSAGES_TO_SEND" => tags::MORE_MESSAGES_TO_SEND,
163        "message_state" | "MESSAGE_STATE" => tags::MESSAGE_STATE,
164        "congestion_state" | "CONGESTION_STATE" => tags::CONGESTION_STATE,
165        "ussd_service_op" | "USSD_SERVICE_OP" => tags::USSD_SERVICE_OP,
166        "display_time" | "DISPLAY_TIME" => tags::DISPLAY_TIME,
167        "sms_signal" | "SMS_SIGNAL" => tags::SMS_SIGNAL,
168        "ms_validity" | "MS_VALIDITY" => tags::MS_VALIDITY,
169        "alert_on_message_delivery" | "ALERT_ON_MESSAGE_DELIVERY" => {
170            tags::ALERT_ON_MESSAGE_DELIVERY
171        }
172        "its_reply_type" | "ITS_REPLY_TYPE" => tags::ITS_REPLY_TYPE,
173        "its_session_info" | "ITS_SESSION_INFO" => tags::ITS_SESSION_INFO,
174        // --- Broadcast / Multicast specific (SMPP v5.0) ---
175        "broadcast_area_identifier" | "BROADCAST_AREA_IDENTIFIER" => {
176            tags::BROADCAST_AREA_IDENTIFIER
177        }
178        "broadcast_content_type" | "BROADCAST_CONTENT_TYPE" => tags::BROADCAST_CONTENT_TYPE,
179        "broadcast_rep_num" | "BROADCAST_REP_NUM" => tags::BROADCAST_REP_NUM,
180        "broadcast_frequency_interval" | "BROADCAST_FREQUENCY_INTERVAL" => {
181            tags::BROADCAST_FREQUENCY_INTERVAL
182        }
183        "broadcast_area_success" | "BROADCAST_AREA_SUCCESS" => tags::BROADCAST_AREA_SUCCESS,
184        "broadcast_end_time" | "BROADCAST_END_TIME" => tags::BROADCAST_END_TIME,
185        "broadcast_service_group" | "BROADCAST_SERVICE_GROUP" => tags::BROADCAST_SERVICE_GROUP,
186        "broadcast_channel_indicator" | "BROADCAST_CHANNEL_INDICATOR" => {
187            tags::BROADCAST_CHANNEL_INDICATOR
188        }
189        _ => 0,
190    }
191}
192
193/// Tag-Length-Value (TLV) Parameter
194#[derive(Debug, Clone, PartialEq)]
195pub struct Tlv {
196    /// The tag identifier for the parameter
197    pub tag: u16,
198    /// The length of the value field
199    pub length: u16,
200    /// The value of the parameter
201    pub value: Vec<u8>,
202}
203
204impl Tlv {
205    /// Creates a new TLV with the given tag and value.
206    pub fn new(tag: u16, value: Vec<u8>) -> Self {
207        Self {
208            tag,
209            length: value.len() as u16,
210            value,
211        }
212    }
213
214    /// Convenience: create a TLV using a string name for the tag
215    pub fn new_from_name(name: &str, value: Vec<u8>) -> Self {
216        let tag = get_tag_by_name(name);
217        Self::new(tag, value)
218    }
219
220    /// Creates a new TLV with a single u8 value.
221    pub fn new_u8(tag: u16, val: u8) -> Self {
222        Self::new(tag, vec![val])
223    }
224
225    /// Convenience: create u8 TLV from name
226    pub fn new_u8_from_name(name: &str, val: u8) -> Self {
227        Self::new(get_tag_by_name(name), vec![val])
228    }
229
230    /// Creates a new TLV with a u16 value (Big Endian).
231    pub fn new_u16(tag: u16, val: u16) -> Self {
232        Self::new(tag, val.to_be_bytes().to_vec())
233    }
234
235    /// Convenience: create u16 TLV from name
236    pub fn new_u16_from_name(name: &str, val: u16) -> Self {
237        Self::new(get_tag_by_name(name), val.to_be_bytes().to_vec())
238    }
239
240    /// Creates a new TLV with a C-style string value (null-terminated).
241    pub fn new_string(tag: u16, val: &str) -> Self {
242        let mut v = val.as_bytes().to_vec();
243        v.push(0); // Null terminator
244        Self::new(tag, v)
245    }
246
247    /// Creates a new TLV with a raw payload.
248    pub fn new_payload(tag: u16, val: Vec<u8>) -> Self {
249        Self::new(tag, val)
250    }
251
252    /// Encodes the TLV into the writer.
253    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
254        writer.write_all(&self.tag.to_be_bytes())?;
255        writer.write_all(&self.length.to_be_bytes())?;
256        writer.write_all(&self.value)?;
257        Ok(())
258    }
259
260    /// Decodes a TLV from the cursor.
261    pub fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Option<Self>, PduError> {
262        let pos = cursor.position();
263        let len = cursor.get_ref().len() as u64;
264
265        if pos + 4 > len {
266            return Ok(None);
267        }
268
269        let mut tag_bytes = [0u8; 2];
270        cursor.read_exact(&mut tag_bytes)?;
271        let tag = u16::from_be_bytes(tag_bytes);
272
273        let mut len_bytes = [0u8; 2];
274        cursor.read_exact(&mut len_bytes)?;
275        let length = u16::from_be_bytes(len_bytes);
276
277        let current_pos = cursor.position();
278        if current_pos + length as u64 > len {
279            return Err(PduError::BufferTooShort);
280        }
281
282        let mut value = vec![0u8; length as usize];
283        cursor.read_exact(&mut value)?;
284
285        Ok(Some(Self { tag, length, value }))
286    }
287
288    // --- Getters ---
289    /// Returns the value as a u8 (if length is 1).
290    pub fn value_as_u8(&self) -> Result<u8, PduError> {
291        if self.value.len() != 1 {
292            return Err(PduError::InvalidLength);
293        } // Assuming InvalidLength is in common
294        Ok(self.value[0])
295    }
296
297    /// Returns the value as a u16 (if length is 2).
298    pub fn value_as_u16(&self) -> Result<u16, PduError> {
299        if self.value.len() != 2 {
300            return Err(PduError::InvalidLength);
301        }
302        Ok(u16::from_be_bytes([self.value[0], self.value[1]]))
303    }
304
305    /// Returns the value as a String (removes trailing null if present).
306    pub fn value_as_string(&self) -> Result<String, PduError> {
307        let v = if !self.value.is_empty() && self.value[self.value.len() - 1] == 0 {
308            &self.value[..self.value.len() - 1]
309        } else {
310            &self.value
311        };
312        String::from_utf8(v.to_vec()).map_err(PduError::Utf8)
313    }
314}