Skip to main content

smpp_codec/
common.rs

1//! # Common Definitions
2//!
3//! This module contains common constants, error types, and helper functions used throughout the library.
4
5// --- Command IDs ---
6// These constants define the Command ID for each SMPP PDU.
7
8/// Command ID for Bind Receiver.
9pub const CMD_BIND_RECEIVER: u32 = 0x00000001;
10/// Command ID for Bind Receiver Response.
11pub const CMD_BIND_RECEIVER_RESP: u32 = 0x80000001;
12
13/// Command ID for Bind Transmitter.
14pub const CMD_BIND_TRANSMITTER: u32 = 0x00000002;
15/// Command ID for Bind Transmitter Response.
16pub const CMD_BIND_TRANSMITTER_RESP: u32 = 0x80000002;
17
18/// Command ID for Bind Transceiver.
19pub const CMD_BIND_TRANSCEIVER: u32 = 0x00000009;
20/// Command ID for Bind Transceiver Response.
21pub const CMD_BIND_TRANSCEIVER_RESP: u32 = 0x80000009;
22
23/// Command ID for Outbind.
24pub const CMD_OUTBIND: u32 = 0x0000000B;
25
26/// Command ID for Enquire Link.
27pub const CMD_ENQUIRE_LINK: u32 = 0x00000015;
28/// Command ID for Enquire Link Response.
29pub const CMD_ENQUIRE_LINK_RESP: u32 = 0x80000015;
30
31/// Command ID for Submit SM.
32pub const CMD_SUBMIT_SM: u32 = 0x00000004;
33/// Command ID for Submit SM Response.
34pub const CMD_SUBMIT_SM_RESP: u32 = 0x80000004;
35
36/// Command ID for Deliver SM.
37pub const CMD_DELIVER_SM: u32 = 0x00000005;
38/// Command ID for Deliver SM Response.
39pub const CMD_DELIVER_SM_RESP: u32 = 0x80000005;
40
41/// Command ID for Unbind.
42pub const CMD_UNBIND: u32 = 0x00000006;
43/// Command ID for Unbind Response.
44pub const CMD_UNBIND_RESP: u32 = 0x80000006;
45
46/// Command ID for Submit Multi SM.
47pub const CMD_SUBMIT_MULTI_SM: u32 = 0x00000021;
48/// Command ID for Submit Multi SM Response.
49pub const CMD_SUBMIT_MULTI_SM_RESP: u32 = 0x80000021;
50
51/// Command ID for Query SM.
52pub const CMD_QUERY_SM: u32 = 0x00000022;
53/// Command ID for Query SM Response.
54pub const CMD_QUERY_SM_RESP: u32 = 0x80000022;
55
56/// Command ID for Cancel SM.
57pub const CMD_CANCEL_SM: u32 = 0x00000023;
58/// Command ID for Cancel SM Response.
59pub const CMD_CANCEL_SM_RESP: u32 = 0x80000023;
60
61/// Command ID for Replace SM.
62pub const CMD_REPLACE_SM: u32 = 0x00000024;
63/// Command ID for Replace SM Response.
64pub const CMD_REPLACE_SM_RESP: u32 = 0x80000024;
65
66/// Command ID for Submit SM Multi (Reserved).
67pub const CMD_SUBMIT_SM_MULTI: u32 = 0x00000025;
68/// Command ID for Submit SM Multi Response (Reserved).
69pub const CMD_SUBMIT_SM_MULTI_RESP: u32 = 0x80000025;
70
71/// Command ID for Data SM.
72pub const CMD_DATA_SM: u32 = 0x00000103;
73/// Command ID for Data SM Response.
74pub const CMD_DATA_SM_RESP: u32 = 0x80000103;
75
76/// Command ID for Alert Notification.
77pub const CMD_ALERT_NOTIFICATION: u32 = 0x00000102;
78/// Command ID for Alert Notification Response (Note: Alert Notification does not have a standard response, but some implementations might use one).
79pub const CMD_ALERT_NOTIFICATION_RESP: u32 = 0x80000102;
80
81/// Generic NACK.
82pub const GENERIC_NACK: u32 = 0x80000000;
83
84// Standard Header Length
85pub const HEADER_LEN: usize = 16;
86
87/// SMPP Interface Version 3.4
88pub const SMPP_INTERFACE_VERSION: u8 = 0x34;
89
90/// Address Type of Number (TON)
91#[derive(Debug, Clone, Copy, PartialEq)]
92#[repr(u8)]
93pub enum Ton {
94    Unknown = 0x00,
95    International = 0x01,
96    National = 0x02,
97    NetworkSpecific = 0x03,
98    SubscriberNumber = 0x04,
99    Alphanumeric = 0x05,
100    Abbreviated = 0x06,
101}
102
103impl From<u8> for Ton {
104    fn from(value: u8) -> Self {
105        match value {
106            0x01 => Ton::International,
107            0x02 => Ton::National,
108            0x03 => Ton::NetworkSpecific,
109            0x04 => Ton::SubscriberNumber,
110            0x05 => Ton::Alphanumeric,
111            0x06 => Ton::Abbreviated,
112            _ => Ton::Unknown,
113        }
114    }
115}
116
117/// Address Numbering Plan Indicator (NPI)
118#[derive(Debug, Clone, Copy, PartialEq)]
119#[repr(u8)]
120pub enum Npi {
121    Unknown = 0x00,
122    Isdn = 0x01,
123    Data = 0x03,
124    Telex = 0x04,
125    LandMobile = 0x06,
126    National = 0x08,
127    Private = 0x09,
128    Ermes = 0x0A,
129    Internet = 0x0E,
130    Wap = 0x12,
131}
132
133impl From<u8> for Npi {
134    fn from(value: u8) -> Self {
135        match value {
136            0x01 => Npi::Isdn,
137            0x03 => Npi::Data,
138            0x04 => Npi::Telex,
139            0x06 => Npi::LandMobile,
140            0x08 => Npi::National,
141            0x09 => Npi::Private,
142            0x0A => Npi::Ermes,
143            0x0E => Npi::Internet,
144            0x12 => Npi::Wap,
145            _ => Npi::Unknown,
146        }
147    }
148}
149
150/// Command Status
151pub const COMMAND_STATUS_OK: u32 = 0x00000000;
152
153/// Helper function to get error description from status code
154pub fn get_status_description(status: u32) -> String {
155    match status {
156        0x00000000 => "ESME_ROK".to_string(),
157        0x00000001 => "ESME_RINVMSGLEN".to_string(),
158        0x00000002 => "ESME_RINVCMDLEN".to_string(),
159        0x00000003 => "ESME_RINVCMDID".to_string(),
160        0x00000004 => "ESME_RINVBNDSTS".to_string(),
161        0x00000005 => "ESME_RALYBND".to_string(),
162        0x00000006 => "ESME_RINVPRTFLG".to_string(),
163        0x00000007 => "ESME_RINVREGDLVFLG".to_string(),
164        0x00000008 => "ESME_RSYSERR".to_string(),
165        0x0000000A => "ESME_RINVSRCADR".to_string(),
166        0x0000000B => "ESME_RINVDSTADR".to_string(),
167        0x0000000C => "ESME_RINVMSGID".to_string(),
168        0x0000000D => "ESME_RBINDFAIL".to_string(),
169        0x0000000E => "ESME_RINVPASWD".to_string(),
170        0x0000000F => "ESME_RINVSYSID".to_string(),
171        0x00000011 => "ESME_RCANCELFAIL".to_string(),
172        0x00000013 => "ESME_RREPLACEFAIL".to_string(),
173        0x00000014 => "ESME_RMSGQFUL".to_string(),
174        0x00000015 => "ESME_RINVSERVICETYPE".to_string(),
175        0x00000033 => "ESME_RINVNUMDESTS".to_string(),
176        0x00000034 => "ESME_RINVDLNAME".to_string(),
177        0x00000040 => "ESME_RINVDESTFLAG".to_string(),
178        0x00000042 => "ESME_RINVSUBREP".to_string(),
179        0x00000043 => "ESME_RINVESMCLASS".to_string(),
180        0x00000044 => "ESME_RCNTSUBDL".to_string(),
181        0x00000045 => "ESME_RSUBMITFAIL".to_string(),
182        0x00000048 => "ESME_RINVSRCTON".to_string(),
183        0x00000049 => "ESME_RINVSRCNPI".to_string(),
184        0x00000050 => "ESME_RINVDSTTON".to_string(),
185        0x00000051 => "ESME_RINVDSTNPI".to_string(),
186        0x00000053 => "ESME_RINVSYSTYP".to_string(),
187        0x00000054 => "ESME_RINVREPFLAG".to_string(),
188        0x00000055 => "ESME_RINVNUMMSGS".to_string(),
189        0x00000058 => "ESME_RTHROTTLED".to_string(),
190        0x00000061 => "ESME_RINVSCHED".to_string(),
191        0x00000062 => "ESME_RINVEXPIRY".to_string(),
192        0x00000063 => "ESME_RINVDFTMSGID".to_string(),
193        0x00000064 => "ESME_RX_T_APPN".to_string(),
194        0x00000065 => "ESME_RX_P_APPN".to_string(),
195        0x00000066 => "ESME_RX_R_APPN".to_string(),
196        0x00000067 => "ESME_RQUERYFAIL".to_string(),
197        0x000000C0 => "ESME_RINVOPTPARSTREAM".to_string(),
198        0x000000C1 => "ESME_ROPTPARNOTALLWD".to_string(),
199        0x000000C2 => "ESME_RINVPARLEN".to_string(),
200        0x000000C3 => "ESME_RMISSINGOPTPARAM".to_string(),
201        0x000000C4 => "ESME_RINVOPTPARAMVAL".to_string(),
202        0x000000FE => "ESME_RDELIVERYFAILURE".to_string(),
203        0x000000FF => "ESME_RUNKNOWNERR".to_string(),
204        _ => format!("Unknown Error: 0x{:08X}", status),
205    }
206}
207
208/// Helper function to get status code from description (Reverse lookup)
209pub fn get_status_code(name: &str) -> u32 {
210    match name {
211        "ESME_ROK" => 0x00000000,
212        "ESME_RINVMSGLEN" => 0x00000001,
213        "ESME_RINVCMDLEN" => 0x00000002,
214        "ESME_RINVCMDID" => 0x00000003,
215        "ESME_RINVBNDSTS" => 0x00000004,
216        "ESME_RALYBND" => 0x00000005,
217        "ESME_RINVPRTFLG" => 0x00000006,
218        "ESME_RINVREGDLVFLG" => 0x00000007,
219        "ESME_RSYSERR" => 0x00000008,
220        "ESME_RINVSRCADR" => 0x0000000A,
221        "ESME_RINVDSTADR" => 0x0000000B,
222        "ESME_RINVMSGID" => 0x0000000C,
223        "ESME_RBINDFAIL" => 0x0000000D,
224        "ESME_RINVPASWD" => 0x0000000E,
225        "ESME_RINVSYSID" => 0x0000000F,
226        "ESME_RCANCELFAIL" => 0x00000011,
227        "ESME_RREPLACEFAIL" => 0x00000013,
228        "ESME_RMSGQFUL" => 0x00000014,
229        "ESME_RINVSERVICETYPE" => 0x00000015,
230        "ESME_RINVNUMDESTS" => 0x00000033,
231        "ESME_RINVDLNAME" => 0x00000034,
232        "ESME_RINVDESTFLAG" => 0x00000040,
233        "ESME_RINVSUBREP" => 0x00000042,
234        "ESME_RINVESMCLASS" => 0x00000043,
235        "ESME_RCNTSUBDL" => 0x00000044,
236        "ESME_RSUBMITFAIL" => 0x00000045,
237        "ESME_RINVSRCTON" => 0x00000048,
238        "ESME_RINVSRCNPI" => 0x00000049,
239        "ESME_RINVDSTTON" => 0x00000050,
240        "ESME_RINVDSTNPI" => 0x00000051,
241        "ESME_RINVSYSTYP" => 0x00000053,
242        "ESME_RINVREPFLAG" => 0x00000054,
243        "ESME_RINVNUMMSGS" => 0x00000055,
244        "ESME_RTHROTTLED" => 0x00000058,
245        "ESME_RINVSCHED" => 0x00000061,
246        "ESME_RINVEXPIRY" => 0x00000062,
247        "ESME_RINVDFTMSGID" => 0x00000063,
248        "ESME_RX_T_APPN" => 0x00000064,
249        "ESME_RX_P_APPN" => 0x00000065,
250        "ESME_RX_R_APPN" => 0x00000066,
251        "ESME_RQUERYFAIL" => 0x00000067,
252        "ESME_RINVOPTPARSTREAM" => 0x000000C0,
253        "ESME_ROPTPARNOTALLWD" => 0x000000C1,
254        "ESME_RINVPARLEN" => 0x000000C2,
255        "ESME_RMISSINGOPTPARAM" => 0x000000C3,
256        "ESME_RINVOPTPARAMVAL" => 0x000000C4,
257        "ESME_RDELIVERYFAILURE" => 0x000000FE,
258        "ESME_RUNKNOWNERR" => 0x000000FF,
259        _ => 0x000000FF, // Default to Unknown Error if string not found
260    }
261}
262
263#[derive(Debug, Clone, Copy, PartialEq)]
264pub enum BindMode {
265    Receiver,
266    Transmitter,
267    Transceiver,
268}
269
270impl BindMode {
271    pub fn command_id(&self) -> u32 {
272        match self {
273            BindMode::Receiver => CMD_BIND_RECEIVER,
274            BindMode::Transmitter => CMD_BIND_TRANSMITTER,
275            BindMode::Transceiver => CMD_BIND_TRANSCEIVER,
276        }
277    }
278}
279
280// Custom Error for PDU operations
281#[derive(Debug)]
282pub enum PduError {
283    Io(std::io::Error),
284    Utf8(std::string::FromUtf8Error),
285    BufferTooShort,
286    InvalidCommandId(u32),
287    StringTooLong(String, usize), // Field name, Max len
288    InvalidLength,
289}
290
291// Convert IO errors to PduError
292impl From<std::io::Error> for PduError {
293    fn from(err: std::io::Error) -> Self {
294        PduError::Io(err)
295    }
296}
297
298/// optimized helper to read a C-Style string from a Cursor<&[u8]>
299pub fn read_c_string(cursor: &mut std::io::Cursor<&[u8]>) -> Result<String, PduError> {
300    let current_pos = cursor.position() as usize;
301    let inner = cursor.get_ref();
302
303    if current_pos >= inner.len() {
304        return Err(PduError::Io(std::io::Error::from(
305            std::io::ErrorKind::UnexpectedEof,
306        )));
307    }
308
309    let remaining = &inner[current_pos..];
310
311    // Find null byte efficiently using slice iter
312    match remaining.iter().position(|&b| b == 0) {
313        Some(null_idx) => {
314            let s_bytes = &remaining[..null_idx];
315            let s = String::from_utf8(s_bytes.to_vec()).map_err(PduError::Utf8)?;
316            cursor.set_position((current_pos + null_idx + 1) as u64);
317            Ok(s)
318        }
319        None => Err(PduError::Io(std::io::Error::from(
320            std::io::ErrorKind::UnexpectedEof,
321        ))),
322    }
323}
324
325/// Helper to write a C-Style string (null terminated)
326pub fn write_c_string(w: &mut impl std::io::Write, s: &str) -> std::io::Result<()> {
327    w.write_all(s.as_bytes())?;
328    w.write_all(&[0])
329}