mod commands;
pub mod tlv;
use std::net::SocketAddr;
use chrono::DateTime;
use chrono::Utc;
use downcast_rs::impl_downcast;
use downcast_rs::DowncastSync;
use log::error;
pub use commands::alert_notification::*;
pub use commands::bind_receiver::*;
pub use commands::bind_transceiver::*;
pub use commands::bind_transmitter::*;
pub use commands::cancel_sm::*;
pub use commands::data_sm::*;
pub use commands::deliver_sm::*;
pub use commands::enquire_link::*;
pub use commands::generic_nack::*;
pub use commands::outbind::*;
pub use commands::query_sm::*;
pub use commands::replace_sm::*;
pub use commands::submit_sm::*;
pub use commands::submit_sm_multi::*;
pub use commands::unbind::*;
use nom::{
self,
bytes::complete::{take, take_until},
IResult,
};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommandHeader {
pub command_length: u32,
pub command_id: u32,
pub command_status: u32,
pub sequence_number: u32,
}
impl CommandHeader {
pub fn decode(pdu: &[u8]) -> Result<CommandHeader, SmppError> {
if pdu.len() < 16 {
error!("PDU can not contain a valid SMPP header as it's shorter than 16 bytes");
Err(SmppError::ESME_RINVCMDLEN)
} else {
let command_header = CommandHeader {
command_length: u32::from_be_bytes(
pdu[0..4]
.try_into()
.map_err(|_| SmppError::ESME_RINVCMDLEN)?,
),
command_id: u32::from_be_bytes(
pdu[4..8]
.try_into()
.map_err(|_| SmppError::ESME_RINVCMDLEN)?,
),
command_status: u32::from_be_bytes(
pdu[8..12]
.try_into()
.map_err(|_| SmppError::ESME_RINVCMDLEN)?,
),
sequence_number: u32::from_be_bytes(
pdu[12..16]
.try_into()
.map_err(|_| SmppError::ESME_RINVCMDLEN)?,
),
};
if pdu.len() != command_header.command_length as usize {
error!(
"PDU length {} does not match command_length {}",
pdu.len(),
command_header.command_length
);
Err(SmppError::ESME_RINVMSGLEN)
} else {
Ok(command_header)
}
}
}
pub fn encode(self) -> Vec<u8> {
let mut buffer = Vec::with_capacity(16);
buffer.append(&mut self.command_length.to_be_bytes().to_vec());
buffer.append(&mut self.command_id.to_be_bytes().to_vec());
buffer.append(&mut self.command_status.to_be_bytes().to_vec());
buffer.append(&mut self.sequence_number.to_be_bytes().to_vec());
buffer
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DeliveryReceipt {
pub id: String,
pub sub: u8,
pub dlvrd: u8,
pub submit_date: String,
pub done_date: String,
pub stat: String,
pub err: String,
pub text: String,
}
impl DeliveryReceipt {
pub fn new(
id: String,
sub: u8,
dlvrd: u8,
submit_date: DateTime<Utc>,
done_date: DateTime<Utc>,
stat: String,
err: String,
text: String,
) -> DeliveryReceipt {
DeliveryReceipt {
id,
sub,
dlvrd,
submit_date: submit_date.format("%y%m%d%H%M").to_string(),
done_date: done_date.format("%y%m%d%H%M").to_string(),
stat,
err,
text,
}
}
pub fn encode(&self) -> String {
format!(
"id:{} sub:{} dlvrd:{} submit date:{} done date:{} stat:{} err:{} text:{}",
self.id,
self.sub,
self.dlvrd,
self.submit_date,
self.done_date,
self.stat,
self.err,
self.text
)
}
}
#[repr(u32)]
#[derive(Debug, PartialEq, FromPrimitive)]
pub(crate) enum CommandId {
generic_nack = 0x80000000,
bind_receiver = 0x00000001,
bind_receiver_resp = 0x80000001,
bind_transmitter = 0x00000002,
bind_transmitter_resp = 0x80000002,
query_sm = 0x00000003,
query_sm_resp = 0x80000003,
submit_sm = 0x00000004,
submit_sm_resp = 0x80000004,
deliver_sm = 0x00000005,
deliver_sm_resp = 0x80000005,
unbind = 0x00000006,
unbind_resp = 0x80000006,
replace_sm = 0x00000007,
replace_sm_resp = 0x80000007,
cancel_sm = 0x00000008,
cancel_sm_resp = 0x80000008,
bind_transceiver = 0x00000009,
bind_transceiver_resp = 0x80000009,
outbind = 0x0000000B,
enquire_link = 0x00000015,
enquire_link_resp = 0x80000015,
submit_multi = 0x00000021,
submit_multi_resp = 0x80000021,
alert_notification = 0x00000102,
data_sm = 0x00000103,
data_sm_resp = 0x80000103,
}
#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, ToPrimitive)]
pub enum SmppError {
ESME_ROK = 0x00000000, ESME_RINVMSGLEN = 0x00000001, ESME_RINVCMDLEN = 0x00000002, ESME_RINVCMDID = 0x00000003, ESME_RINVBNDSTS = 0x00000004, ESME_RALYBND = 0x00000005, ESME_RINVPRTFLG = 0x00000006, ESME_RINVREGDLVFLG = 0x00000007, ESME_RSYSERR = 0x00000008,
ESME_RINVSRCADR = 0x0000000A, ESME_RINVDSTADR = 0x0000000B, ESME_RINVMSGID = 0x0000000C, ESME_RBINDFAIL = 0x0000000D, ESME_RINVPASWD = 0x0000000E, ESME_RINVSYSID = 0x0000000F,
ESME_RCANCELFAIL = 0x00000011,
ESME_RREPLACEFAIL = 0x00000013,
ESME_RMSGQFUL = 0x00000014, ESME_RINVSERTYP = 0x00000015,
ESME_RINVNUMDESTS = 0x00000033, ESME_RINVDLNAME = 0x00000034,
ESME_RINVDESTFLAG = 0x00000040,
ESME_RINVSUBREP = 0x00000042, ESME_RINVESMCLASS = 0x00000043, ESME_RCNTSUBDL = 0x00000044, ESME_RSUBMITFAIL = 0x00000045,
ESME_RINVSRCTON = 0x00000048, ESME_RINVSRCNPI = 0x00000049, ESME_RINVDSTTON = 0x00000050, ESME_RINVDSTNPI = 0x00000051,
ESME_RINVSYSTYP = 0x00000053, ESME_RINVREPFLAG = 0x00000054, ESME_RINVNUMMSGS = 0x00000055,
ESME_RTHROTTLED = 0x00000058,
ESME_RINVSCHED = 0x00000061, ESME_RINVEXPIRY = 0x00000062, ESME_RINVDFTMSGID = 0x00000063, ESME_RX_T_APPN = 0x00000064, ESME_RX_P_APPN = 0x00000065, ESME_RX_R_APPN = 0x00000066, ESME_RQUERYFAIL = 0x00000067,
ESME_RINVOPTPARSTREAM = 0x000000C0, ESME_ROPTPARNOTALLWD = 0x000000C1, ESME_RINVPARLEN = 0x000000C2, ESME_RMISSINGOPTPARAM = 0x000000C3, ESME_RINVOPTPARAMVAL = 0x000000C4,
ESME_RDELIVERYFAILURE = 0x000000FE, ESME_RUNKNOWNERR = 0x000000FF, }
fn encode_bind_request(
header: CommandHeader,
system_id: String,
password: String,
system_type: String,
interface_version: u8,
addr_ton: u8,
addr_npi: u8,
address_range: String,
) -> Vec<u8> {
let mut buffer: Vec<u8> = Vec::with_capacity(header.command_status as usize);
buffer.append(&mut header.encode());
buffer.append(&mut system_id.into_bytes());
buffer.push(0x00); buffer.append(&mut password.into_bytes());
buffer.push(0x00); buffer.append(&mut system_type.into_bytes());
buffer.push(0x00); buffer.push(interface_version);
buffer.push(addr_ton);
buffer.push(addr_npi);
buffer.append(&mut address_range.into_bytes());
buffer.push(0x00); buffer
}
fn encode_bind_response(
header: CommandHeader,
system_id: Option<String>,
sc_interface_version: Option<u8>,
) -> Vec<u8> {
let command_status = header.command_status;
let mut buffer: Vec<u8> = Vec::with_capacity(header.command_length as usize);
buffer.append(&mut header.encode());
if command_status == SmppError::ESME_ROK as u32 {
if let Some(id) = system_id {
buffer.append(&mut id.into_bytes());
}
buffer.push(0x00); }
if let Some(version) = sc_interface_version {
let version_tlv = tlv::Tlv::from_tag(tlv::TlvTag::ScInterfaceVersion, vec![version]);
buffer.extend_from_slice(&version_tlv.encode());
}
buffer
}
struct CommonBindRequestParameters {
header: CommandHeader,
system_id: String,
password: String,
system_type: String,
interface_version: u8,
addr_ton: u8,
addr_npi: u8,
address_range: String,
}
fn parse_c_octet_string(bytes: Vec<u8>, maximum_length: usize) -> Result<String, SmppError> {
if let Some(index) = bytes.iter().position(|&r| r == 0x00) {
if index <= maximum_length {
String::from_utf8(bytes[0..index].to_vec()).map_err(|_x| SmppError::ESME_RINVPARLEN)
} else {
error!("C-Octet-String is too long");
Err(SmppError::ESME_RINVPARLEN)
}
} else {
error!("Can not find null byte at all, C-Octet-String not terminated?!");
Err(SmppError::ESME_RINVPARLEN)
}
}
pub(crate) fn parse_c_octet_string_nom(input: &[u8]) -> IResult<&[u8], String> {
let (input, result) = take_until("\0")(input)?;
let (input, _) = take(1usize)(input)?; Ok((input, String::from_utf8_lossy(result).to_string()))
}
fn decode_bind_request(
header: CommandHeader,
pdu: &[u8],
) -> Result<CommonBindRequestParameters, SmppError> {
if pdu.len() < 16 {
return Err(SmppError::ESME_RINVCMDLEN);
}
let input = &pdu[16..];
let (input, system_id) =
parse_c_octet_string_nom(input).map_err(|_| SmppError::ESME_RINVPARLEN)?;
let (input, password) =
parse_c_octet_string_nom(input).map_err(|_| SmppError::ESME_RINVPARLEN)?;
let (input, system_type) =
parse_c_octet_string_nom(input).map_err(|_| SmppError::ESME_RINVPARLEN)?;
let (input, interface_version_bytes) =
take::<usize, &[u8], nom::error::Error<&[u8]>>(1usize)(input)
.map_err(|_| SmppError::ESME_RINVPARLEN)?;
let (input, addr_ton_bytes) = take::<usize, &[u8], nom::error::Error<&[u8]>>(1usize)(input)
.map_err(|_| SmppError::ESME_RINVPARLEN)?;
let (input, addr_npi_bytes) = take::<usize, &[u8], nom::error::Error<&[u8]>>(1usize)(input)
.map_err(|_| SmppError::ESME_RINVPARLEN)?;
let (_input, address_range) =
parse_c_octet_string_nom(input).map_err(|_| SmppError::ESME_RINVPARLEN)?;
Ok(CommonBindRequestParameters {
header,
system_id,
password,
system_type,
interface_version: interface_version_bytes[0],
addr_ton: addr_ton_bytes[0],
addr_npi: addr_npi_bytes[0],
address_range,
})
}
#[derive(Debug, Clone)]
pub struct SmppConnectionInformation {
pub server_address: SocketAddr,
pub client_address: SocketAddr,
}
pub trait SmppReply: DowncastSync {}
impl_downcast!(sync SmppReply);
pub(crate) struct WriteFrame {
pub(crate) our_sequence_number: Option<u32>,
pub(crate) pdu: Vec<u8>,
pub(crate) oneshot:
Option<tokio::sync::oneshot::Sender<Box<dyn SmppReply + Send + Sync + 'static>>>,
}