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}