Skip to main content

smpp_codec/pdus/session_pdus/
bind_request.rs

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