<h1 align="center">SMPP Codec (Rust)</h1>
<p align="center"><b>A Rust library for encoding and decoding Short Message Peer-to-Peer (SMPP) v5.0 PDUs.</b></p>
<p align="center">
<a href="https://crates.io/crates/smpp-codec"><img alt="Crates.io" src="https://img.shields.io/crates/v/smpp-codec"></a>
<a href="https://sonarcloud.io/summary/new_code?id=J93d_smpp-codec"><img alt="Coverage" src="https://img.shields.io/sonar/coverage/J93d_smpp-codec?server=https%3A%2F%2Fsonarcloud.io"></a>
<a href="https://docs.rs/smpp-codec"><img alt="Documentation" src="https://img.shields.io/docsrs/smpp-codec"></a>
<a href="https://opensource.org/license/apache-2-0"><img alt="License: Apache 2.0" src="https://img.shields.io/github/license/j93d/smpp-codec"></a>
</p>
> [!WARNING]
> **Disclaimer**:Portions of this codebase were generated by Google's Gemini AI.
> While efforts have been made to ensure correctness, users should review and test the code thoroughly before using it in production environments.
## Features
- **PDU Support**: Supports all PDU's as per [SMPP v5.0](https://smpp.org/SMPP_v5.pdf) specifications.
- **TLV Support**: Includes a comprehensive list of Tag-Length-Value (TLV) optional parameters.
- **Dependency**: Built with standard library + [rand](https://crates.io/crates/rand/0.9.2).
## Compatibility
The minimum supported Rust version (MSRV) is **1.63.0**.
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
smpp-codec = 0.2.1
```
Or
```toml
[dependencies]
smpp-codec = { git = "https://github.com/J93d/smpp-codec.git" }
```
## [Examples:](https://github.com/J93d/smpp-codec/tree/master/examples)
### [Binding as Transceiver](https://github.com/J93d/smpp-codec/blob/master/examples/bind_transceiver.rs)
```rust
use smpp_codec::common::{BindMode, Ton, Npi};
use smpp_codec::pdus::BindRequest;
fn main() {
let bind_req = BindRequest::new(
1, // Sequence Number
BindMode::Transceiver,
"my_system_id".to_string(),
"password".to_string(),
).with_address_range(Ton::International, Npi::Isdn, "12345".to_string());
let mut buffer = Vec::new();
match bind_req.encode(&mut buffer) {
Ok(_) => {
bind_req.encode(&mut buffer);
// Send `buffer` over TCP stream...
}
Err(e) => {
eprintln!("Failed to encode: {:?}", e);
}
}
}
```
### [Submit Short Message](https://github.com/J93d/smpp-codec/blob/master/examples/submit_sm.rs)
```rust
use smpp_codec::pdus::{SubmitSmRequest, MessageSplitter, SplitMode, EncodingType};
fn main() {
let text = "Hello, world!".to_string();
// 1. Split message (handles encoding and valid chunking)
let (parts, data_coding) = MessageSplitter::split(
text,
EncodingType::Gsm7Bit,
SplitMode::Udh // Use UDH for concatenation
).expect("Failed to split message");
// Iterate over parts and send each PDU
for (i, part) in parts.into_iter().enumerate() {
let mut submit_req = SubmitSmRequest::new(
(i + 2) as u32, // Sequence Number
"source_addr".to_string(),
"dest_addr".to_string(),
part,
);
submit_req.data_coding = data_coding; // Important: Set correct encoding!
let mut buffer = Vec::new();
submit_req.encode(&mut buffer).expect("Failed to encode PDU");
// Send buffer...
}
}
```
### [Deliver Short Message](https://github.com/J93d/smpp-codec/blob/master/examples/deliver_sm.rs)
```rust
use smpp_codec::pdus::{DeliverSmRequest, MessageSplitter, SplitMode, EncodingType};
fn main() {
let text = "Incoming message...".to_string();
// Split message
let (parts, data_coding) = MessageSplitter::split(
text,
EncodingType::Gsm7Bit,
SplitMode::Udh
).expect("Failed to split message");
let parts_len = parts.len();
for (i, part) in parts.into_iter().enumerate() {
let mut deliver_req = DeliverSmRequest::new(
(i + 100) as u32,
"sender".to_string(),
"shortcode".to_string(),
part,
);
deliver_req.data_coding = data_coding;
// CRITICAL: Set UDHI bit (0x40) if using UDH
if parts_len > 1 {
deliver_req.esm_class |= 0x40;
}
let mut buffer = Vec::new();
deliver_req.encode(&mut buffer).expect("Failed to encode PDU");
// Send buffer...
}
}
```
### [Unbind](https://github.com/J93d/smpp-codec/blob/master/examples/unbind.rs)
```rust
use smpp_codec::pdus::UnbindRequest;
fn main() {
let unbind_req = UnbindRequest::new(3); // Sequence Number
let mut buffer = Vec::new();
unbind_req.encode(&mut buffer).expect("Failed to encode PDU");
}
```
## Benchmark (cargo test --test benchmarks --release -- --nocapture)
| BindRequest | 6.20ms | 16.14 | 73.24ms | 1.37 |
| BindResponse | 7.90ms | 12.66 | 11.48ms | 8.71 |
| OutbindRequest | 0.90ms | 111.26 | 13.22ms | 7.57 |
| UnbindRequest | 0.37ms | 272.55 | 0.23ms | 433.09 |
| UnbindResponse | 0.48ms | 206.83 | 4.90ms | 20.40 |
| EnquireLinkRequest | 0.44ms | 227.12 | 0.20ms | 500.25 |
| EnquireLinkResponse | 0.42ms | 240.50 | 4.95ms | 20.18 |
| GenericNack | 0.43ms | 231.37 | 30.21ms | 3.31 |
| AlertNotification | 1.36ms | 73.76 | 14.04ms | 7.12 |
| SubmitSmRequest | 7.94ms | 12.60 | 49.84ms | 2.01 |
| SubmitSmRequest (Multi) | 20.51ms | 14.62 | 74.64ms | 4.02 |
| SubmitSmResponse | 39.47ms | 2.53 | 38.77ms | 2.58 |
| SubmitMulti | 3.72ms | 26.90 | 37.38ms | 2.68 |
| SubmitMultiResp | 6.03ms | 16.59 | 13.08ms | 7.64 |
| DeliverSmRequest | 5.84ms | 17.12 | 48.42ms | 2.07 |
| DeliverSmResponse | 6.38ms | 15.67 | 6.07ms | 16.49 |
| DataSm | 2.33ms | 42.97 | 56.34ms | 1.78 |
| DataSmResp | 6.12ms | 16.33 | 12.74ms | 7.85 |
| CancelSmRequest | 2.67ms | 37.47 | 29.67ms | 3.37 |
| CancelSmResponse | 0.40ms | 249.94 | 7.27ms | 13.75 |
| QuerySmRequest | 1.17ms | 85.69 | 17.63ms | 5.67 |
| QuerySmResponse | 2.64ms | 37.86 | 16.16ms | 6.19 |
| BroadcastSm | 3.79ms | 26.37 | 63.79ms | 1.57 |
| BroadcastSmResp | 10.20ms | 9.80 | 16.99ms | 5.89 |
| ReplaceSm | 2.29ms | 43.62 | 55.82ms | 1.79 |
| ReplaceSmResp | 0.55ms | 180.28 | 6.36ms | 15.73 |
| QueryBroadcastSm | 1.35ms | 74.13 | 17.71ms | 5.65 |
| QueryBroadcastSmResp | 0.85ms | 117.84 | 12.60ms | 7.94 |
| CancelBroadcastSm | 1.69ms | 59.29 | 24.60ms | 4.06 |
| CancelBroadcastSmResp | 0.44ms | 229.67 | 6.11ms | 16.37 |
## License
This project is licensed under the Apache-2.0 license.