Skip to main content

smpp_codec/pdus/session_pdus/
bind_request.rs

1use crate::common::{BindMode, Npi, PduError, Ton, HEADER_LEN};
2use std::io::{Cursor, Read, Write};
3
4/// Represents a Bind Request PDU (Receiver, Transmitter, or Transceiver).
5///
6/// This PDU is used to initiate a session with the SMSC.
7#[derive(Debug, Clone)]
8pub struct BindRequest {
9    pub sequence_number: u32,
10    pub mode: BindMode,
11    pub system_id: String,
12    pub password: String,
13    pub system_type: String,
14    pub interface_version: u8,
15    pub addr_ton: Ton,
16    pub addr_npi: Npi,
17    pub address_range: String,
18}
19
20impl BindRequest {
21    /// Create a new Bind Request with defaults.
22    ///
23    /// # Examples
24    ///
25    /// ```
26    /// use smpp_codec::pdus::BindRequest;
27    /// use smpp_codec::common::BindMode;
28    ///
29    /// let sequence_number: u32 = 1;
30    /// let bind_req = BindRequest::new(
31    ///     sequence_number,
32    ///     BindMode::Transceiver,
33    ///     "system_id".to_string(),
34    ///     "password".to_string(),
35    /// );
36    /// ```
37    pub fn new(sequence_number: u32, mode: BindMode, system_id: String, password: String) -> Self {
38        Self {
39            sequence_number,
40            mode,
41            system_id,
42            password,
43            system_type: String::new(),
44            interface_version: 0x34, // SMPP 3.4
45            addr_ton: Ton::Unknown,
46            addr_npi: Npi::Unknown,
47            address_range: String::new(),
48        }
49    }
50
51    pub fn with_address_range(mut self, ton: Ton, npi: Npi, range: String) -> Self {
52        self.addr_ton = ton;
53        self.addr_npi = npi;
54        self.address_range = range;
55        self
56    }
57
58    /// Encode the struct into raw bytes for the network.
59    ///
60    /// # Errors
61    ///
62    /// Returns a [`PduError`] if:
63    /// * `system_id` exceeds 16 characters.
64    /// * `password` exceeds 9 characters.
65    /// * `system_type` exceeds 13 characters.
66    /// * `address_range` exceeds 41 characters.
67    /// * An I/O error occurs while writing.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// # use smpp_codec::pdus::BindRequest;
73    /// # use smpp_codec::common::BindMode;
74    /// # let sequence_number: u32 = 1;
75    /// # let bind_req = BindRequest::new(sequence_number, BindMode::Transmitter, "id".into(), "pwd".into());
76    /// let mut buffer = Vec::new();
77    /// bind_req.encode(&mut buffer).expect("Encoding failed");
78    /// ```
79    pub fn encode(&self, writer: &mut impl Write) -> Result<(), PduError> {
80        // 1. Validate Constraints
81        if self.system_id.len() > 16 {
82            return Err(PduError::StringTooLong("system_id".into(), 16));
83        }
84        if self.password.len() > 9 {
85            return Err(PduError::StringTooLong("password".into(), 9));
86        }
87        if self.system_type.len() > 13 {
88            return Err(PduError::StringTooLong("system_type".into(), 13));
89        }
90
91        if self.address_range.len() > 41 {
92            return Err(PduError::StringTooLong("address_range".into(), 41));
93        }
94
95        // 2. Calculate Length Upfront
96        // Header (16) + SystemID (N+1) + Password (N+1) + SystemType (N+1) + Ver(1) + Ton(1) + Npi(1) + Range(N+1)
97        let body_len = self.system_id.len() + 1 +
98                       self.password.len() + 1 +
99                       self.system_type.len() + 1 +
100                       1 + // interface_version
101                       1 + // addr_ton
102                       1 + // addr_npi
103                       self.address_range.len() + 1;
104
105        let command_len = (HEADER_LEN + body_len) as u32;
106
107        // 3. Write Header
108        writer.write_all(&command_len.to_be_bytes())?;
109        writer.write_all(&self.mode.command_id().to_be_bytes())?;
110        writer.write_all(&0u32.to_be_bytes())?; // Command Status
111        writer.write_all(&self.sequence_number.to_be_bytes())?;
112
113        // 4. Write Body
114        write_c_string(writer, &self.system_id)?;
115        write_c_string(writer, &self.password)?;
116        write_c_string(writer, &self.system_type)?;
117        writer.write_all(&[self.interface_version])?;
118        writer.write_all(&[self.addr_ton as u8, self.addr_npi as u8])?;
119        write_c_string(writer, &self.address_range)?;
120
121        Ok(())
122    }
123
124    /// Decode raw bytes from the network into the struct.
125    ///
126    /// # Errors
127    ///
128    /// Returns a [`PduError`] if:
129    /// * The buffer is too short to contain a valid header.
130    /// * The command ID does not correspond to a Bind Request.
131    /// * The buffer data is malformed.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// # use smpp_codec::pdus::BindRequest;
137    /// # use smpp_codec::common::BindMode;
138    /// # let bind_req = BindRequest::new(1, BindMode::Transmitter, "id".into(), "pwd".into());
139    /// # let mut buffer = Vec::new();
140    /// # bind_req.encode(&mut buffer).unwrap();
141    /// let decoded = BindRequest::decode(&buffer).expect("Decoding failed");
142    /// assert_eq!(decoded.system_id, "id");
143    /// ```
144    pub fn decode(buffer: &[u8]) -> Result<Self, PduError> {
145        // 1. Validate total length
146        if buffer.len() < HEADER_LEN {
147            return Err(PduError::BufferTooShort);
148        }
149
150        let mut cursor = Cursor::new(buffer);
151
152        // 2. Read Header
153        let mut bytes = [0u8; 4];
154
155        // Command Length
156        cursor.read_exact(&mut bytes)?;
157        let command_len = u32::from_be_bytes(bytes) as usize;
158
159        if buffer.len() != command_len {
160            // We can be strict or loose here. Strict is safer for libraries.
161            // return Err(PduError::InvalidLength);
162        }
163
164        // Command ID
165        cursor.read_exact(&mut bytes)?;
166        let command_id = u32::from_be_bytes(bytes);
167
168        // Map Command ID to BindMode
169        let mode = match command_id {
170            0x00000001 => BindMode::Receiver,
171            0x00000002 => BindMode::Transmitter,
172            0x00000009 => BindMode::Transceiver,
173            _ => return Err(PduError::InvalidCommandId(command_id)),
174        };
175
176        // Command Status
177        cursor.read_exact(&mut bytes)?;
178        let _command_status = u32::from_be_bytes(bytes);
179
180        // Sequence Number
181        cursor.read_exact(&mut bytes)?;
182        let sequence_number = u32::from_be_bytes(bytes);
183
184        // 3. Read Body (C-Strings and u8s)
185        let system_id = crate::common::read_c_string(&mut cursor)?;
186        let password = crate::common::read_c_string(&mut cursor)?;
187        let system_type = crate::common::read_c_string(&mut cursor)?;
188
189        // Simple u8 reads
190        let mut u8_buf = [0u8; 1];
191        cursor.read_exact(&mut u8_buf)?;
192        let interface_version = u8_buf[0];
193
194        cursor.read_exact(&mut u8_buf)?;
195        let addr_ton = Ton::from(u8_buf[0]); // Convert byte -> Enum
196
197        cursor.read_exact(&mut u8_buf)?;
198        let addr_npi = Npi::from(u8_buf[0]); // Convert byte -> Enum
199
200        let address_range = crate::common::read_c_string(&mut cursor)?;
201
202        Ok(Self {
203            sequence_number,
204            mode,
205            system_id,
206            password,
207            system_type,
208            interface_version,
209            addr_ton,
210            addr_npi,
211            address_range,
212        })
213    }
214}
215
216// --- Helpers (Private to this module) ---
217
218fn write_c_string(w: &mut impl Write, s: &str) -> std::io::Result<()> {
219    w.write_all(s.as_bytes())?;
220    w.write_all(&[0])
221}