From a2d9a15aa1d57393b66966b2b7cd64210e5095c3 Mon Sep 17 00:00:00 2001 From: Sebastian Weddmark Olsson Date: Sun, 6 Oct 2024 22:32:08 +0200 Subject: [PATCH 1/6] [smpp] decode --- include/smpp.hrl | 101 +++++++ scripts/smpp_decode_msgs.awk | 120 +++++++++ src/otc.erl | 11 +- src/otc_smpp.erl | 496 +++++++++++++++++++++++++++++++++++ test/otc_smpp_tests.erl | 116 ++++++++ 5 files changed, 841 insertions(+), 3 deletions(-) create mode 100644 include/smpp.hrl create mode 100644 scripts/smpp_decode_msgs.awk create mode 100644 src/otc_smpp.erl create mode 100644 test/otc_smpp_tests.erl diff --git a/include/smpp.hrl b/include/smpp.hrl new file mode 100644 index 0000000..1055392 --- /dev/null +++ b/include/smpp.hrl @@ -0,0 +1,101 @@ +-define(SMPP_CMD_ID_BIND_RECEIVER, 16#00000001). +-define(SMPP_CMD_ID_BIND_TRANSMITTER, 16#00000002). +-define(SMPP_CMD_ID_QUERY_SM, 16#00000003). +-define(SMPP_CMD_ID_SUBMIT_SM, 16#00000004). +-define(SMPP_CMD_ID_DELIVER_SM, 16#00000005). +-define(SMPP_CMD_ID_UNBIND, 16#00000006). +-define(SMPP_CMD_ID_REPLACE_SM, 16#00000007). +-define(SMPP_CMD_ID_CANCEL_SM, 16#00000008). +-define(SMPP_CMD_ID_BIND_TRANSCEIVER, 16#00000009). +-define(SMPP_CMD_ID_OUTBIND, 16#0000000B). +-define(SMPP_CMD_ID_ENQUIRE_LINK, 16#00000015). +-define(SMPP_CMD_ID_SUBMIT_MULTI, 16#00000021). +-define(SMPP_CMD_ID_ALERT_NOTIFICATION, 16#00000102). +-define(SMPP_CMD_ID_DATA_SM, 16#00000103). +-define(SMPP_CMD_ID_BROADCAST_SM, 16#00000111). +-define(SMPP_CMD_ID_QUERY_BROADCAST_SM, 16#00000112). +-define(SMPP_CMD_ID_CANCEL_BROADCAST_SM, 16#00000113). +-define(SMPP_CMD_ID_GENERIC_NACK, 16#80000000). +-define(SMPP_CMD_ID_BIND_RECEIVER_RESP, 16#80000001). +-define(SMPP_CMD_ID_BIND_TRANSMITTER_RESP, 16#80000002). +-define(SMPP_CMD_ID_QUERY_SM_RESP, 16#80000003). +-define(SMPP_CMD_ID_SUBMIT_SM_RESP, 16#80000004). +-define(SMPP_CMD_ID_DELIVER_SM_RESP, 16#80000005). +-define(SMPP_CMD_ID_UNBIND_RESP, 16#80000006). +-define(SMPP_CMD_ID_REPLACE_SM_RESP, 16#80000007). +-define(SMPP_CMD_ID_CANCEL_SM_RESP, 16#80000008). +-define(SMPP_CMD_ID_BIND_TRANSCEIVER_RESP, 16#80000009). +-define(SMPP_CMD_ID_ENQUIRE_LINK_RESP, 16#80000015). +-define(SMPP_CMD_ID_SUBMIT_MULTI_RESP, 16#80000021). +-define(SMPP_CMD_ID_DATA_SM_RESP, 16#80000103). +-define(SMPP_CMD_ID_BROADCAST_SM_RESP, 16#80000111). +-define(SMPP_CMD_ID_QUERY_BROADCAST_SM_RESP, 16#80000112). +-define(SMPP_CMD_ID_CANCEL_BROADCAST_SM_RESP, 16#80000113). + +-define(SMPP_CMD_STATUS_ESME_ROK, 16#00000000). +-define(SMPP_CMD_STATUS_ESME_RINVMSGLEN, 16#00000001). +-define(SMPP_CMD_STATUS_ESME_RINVCMDLEN, 16#00000002). +-define(SMPP_CMD_STATUS_ESME_RINVCMDID, 16#00000003). +-define(SMPP_CMD_STATUS_ESME_RINVBNDSTS, 16#00000004). +-define(SMPP_CMD_STATUS_ESME_RALYBND, 16#00000005). +-define(SMPP_CMD_STATUS_ESME_RINVPRTFLG, 16#00000006). +-define(SMPP_CMD_STATUS_ESME_RINVREGDLVFLG, 16#00000007). +-define(SMPP_CMD_STATUS_ESME_RSYSERR, 16#00000008). +-define(SMPP_CMD_STATUS_ESME_RINVSRCADR, 16#0000000A). +-define(SMPP_CMD_STATUS_ESME_RINVDSTADR, 16#0000000B). +-define(SMPP_CMD_STATUS_ESME_RINVMSGID, 16#0000000C). +-define(SMPP_CMD_STATUS_ESME_RBINDFAIL, 16#0000000D). +-define(SMPP_CMD_STATUS_ESME_RINVPASWD, 16#0000000E). +-define(SMPP_CMD_STATUS_ESME_RINVSYSID, 16#0000000F). +-define(SMPP_CMD_STATUS_ESME_RCANCELFAIL, 16#00000011). +-define(SMPP_CMD_STATUS_ESME_RREPLACEFAIL, 16#00000013). +-define(SMPP_CMD_STATUS_ESME_RMSGQFUL, 16#00000014). +-define(SMPP_CMD_STATUS_ESME_RINVSERTYP, 16#00000015). +-define(SMPP_CMD_STATUS_ESME_RINVNUMDESTS, 16#00000033). +-define(SMPP_CMD_STATUS_ESME_RINVDLNAME, 16#00000034). +-define(SMPP_CMD_STATUS_ESME_RINVDESTFLAG, 16#00000040). +-define(SMPP_CMD_STATUS_ESME_RINVSUBREP, 16#00000042). +-define(SMPP_CMD_STATUS_ESME_RINVESMCLASS, 16#00000043). +-define(SMPP_CMD_STATUS_ESME_RCNTSUBDL, 16#00000044). +-define(SMPP_CMD_STATUS_ESME_RSUBMITFAIL, 16#00000045). +-define(SMPP_CMD_STATUS_ESME_RINVSRCTON, 16#00000048). +-define(SMPP_CMD_STATUS_ESME_RINVSRCNPI, 16#00000049). +-define(SMPP_CMD_STATUS_ESME_RINVDSTTON, 16#00000050). +-define(SMPP_CMD_STATUS_ESME_RINVDSTNPI, 16#00000051). +-define(SMPP_CMD_STATUS_ESME_RINVSYSTYP, 16#00000053). +-define(SMPP_CMD_STATUS_ESME_RINVREPFLAG, 16#00000054). +-define(SMPP_CMD_STATUS_ESME_RINVNUMMSGS, 16#00000055). +-define(SMPP_CMD_STATUS_ESME_RTHROTTLED, 16#00000058). +-define(SMPP_CMD_STATUS_ESME_RINVSCHED, 16#00000061). +-define(SMPP_CMD_STATUS_ESME_RINVEXPIRY, 16#00000062). +-define(SMPP_CMD_STATUS_ESME_RINVDFTMSGID, 16#00000063). +-define(SMPP_CMD_STATUS_ESME_RX_T_APPN, 16#00000064). +-define(SMPP_CMD_STATUS_ESME_RX_P_APPN, 16#00000065). +-define(SMPP_CMD_STATUS_ESME_RX_R_APPN, 16#00000066). +-define(SMPP_CMD_STATUS_ESME_RQUERYFAIL, 16#00000067). +-define(SMPP_CMD_STATUS_ESME_RINVTLVSTREAM, 16#000000C0). +-define(SMPP_CMD_STATUS_ESME_RTLVNOTALLWD, 16#000000C1). +-define(SMPP_CMD_STATUS_ESME_RINVTLVLEN, 16#000000C2). +-define(SMPP_CMD_STATUS_ESME_RMISSINGTLV, 16#000000C3). +-define(SMPP_CMD_STATUS_ESME_RINVTLVVAL, 16#000000C4). +-define(SMPP_CMD_STATUS_ESME_RDELIVERYFAILURE, 16#000000FE). +-define(SMPP_CMD_STATUS_ESME_RUNKNOWNERR, 16#000000FF). +-define(SMPP_CMD_STATUS_ESME_RSERTYPUNAUTH, 16#00000100). +-define(SMPP_CMD_STATUS_ESME_RPROHIBITED, 16#00000101). +-define(SMPP_CMD_STATUS_ESME_RSERTYPUNAVAIL, 16#00000102). +-define(SMPP_CMD_STATUS_ESME_RSERTYPDENIED, 16#00000103). +-define(SMPP_CMD_STATUS_ESME_RINVDCS, 16#00000104). +-define(SMPP_CMD_STATUS_ESME_RINVSRCADDRSUBUNIT, 16#00000105). +-define(SMPP_CMD_STATUS_ESME_RINVDSTADDRSUBUNIT, 16#00000106). +-define(SMPP_CMD_STATUS_ESME_RINVBCASTFREQINT, 16#00000107). +-define(SMPP_CMD_STATUS_ESME_RINVBCASTALIAS_NAME, 16#00000108). +-define(SMPP_CMD_STATUS_ESME_RINVBCASTAREAFMT, 16#00000109). +-define(SMPP_CMD_STATUS_ESME_RINVNUMBCAST_AREAS, 16#0000010A). +-define(SMPP_CMD_STATUS_ESME_RINVBCASTCNTTYPE, 16#0000010B). +-define(SMPP_CMD_STATUS_ESME_RINVBCASTMSGCLASS, 16#0000010C). +-define(SMPP_CMD_STATUS_ESME_RBCASTFAIL, 16#0000010D). +-define(SMPP_CMD_STATUS_ESME_RBCASTQUERYFAIL, 16#0000010E). +-define(SMPP_CMD_STATUS_ESME_RBCASTCANCELFAIL, 16#0000010F). +-define(SMPP_CMD_STATUS_ESME_RINVBCAST_REP, 16#00000110). +-define(SMPP_CMD_STATUS_ESME_RINVBCASTSRVGRP, 16#00000111). +-define(SMPP_CMD_STATUS_ESME_RINVBCASTCHANIND, 16#00000112). diff --git a/scripts/smpp_decode_msgs.awk b/scripts/smpp_decode_msgs.awk new file mode 100644 index 0000000..fbb0285 --- /dev/null +++ b/scripts/smpp_decode_msgs.awk @@ -0,0 +1,120 @@ +## Help functions ################### +function trim(s){ + sub(/^[ \t\r\n]+/, "", s) + sub(/[ \t\r\n]+$/, "", s) + return s +} + +function atom(s){ + s=trim(tolower(s)) + gsub("[()/']", "", s) + gsub("[ -]", "_", s) + if(s ~ /^5.*/) { + s = "'" s "'" + } + return s +} + +function atom_to_var(s){ + if(substr(s, 1, 3) == "'5g") { + s=substr(s, 4, length(s)-4) + } + split(s, arr, "_") + s="" + for(i = 1; i <= length(arr); i++) { + l=toupper(substr(arr[i], 1, 1)) substr(arr[i], 2) + s=s l + } + return s +} + +function field_type(s){ + if(s ~ /[iI]nteger/) { + return "integer" + } else if(s ~ /C-Octet String/) { + return "cstring" + } else if(s ~ /Octet String/){ + return "string" + } else if(s ~ /TLV/) { + return "tlv" + } else if(s ~ /Composite/) { + return "composite" + } else { + return "unknown("s")" + } + + sub(/-/, "", s) + return trim(tolower(s)) +} + +function hex(s){ + sub(/-/, "", s) + return trim(toupper(s)) +} + +function len(s){ + if(index(s, "-")){ + split(s, arr, "-") + return sprintf("{%s, %s}", trim(arr[1]), trim(tolower(arr[2]))) + } else if(trim(s) == "1/2") { + return "half" + } else { + return trim(s) + } +} + +## Execution ################### + +BEGIN { + +} + +/^\* 4 SMPP PDU/ { + section=1; +} +/^\* 4.8 PDU/ { + section=0; +} +!section { + next; +} + +/\*\*\* 4[^ ]+/ { + msg=$3; + FS="|"; + next; +} + +/^\| sequence_number/ && !start { + start=1; + next; +} + +/^$/ && start { + start=0; + params=substr(params, 26, length(params)-27) + if(params != "") { + params=sprintf("%s\n%-24s", params, " "); + } + fun=sprintf("decode_msg(#{command := %s}, Bin) ->\n AllowedParameters = [%s],\n decode_parameters(AllowedParameters, Bin);", msg, params); + msg=""; + params=""; + print fun; + FS=" "; + next; +} + +start { + field=atom(trim($2)); + size=trim($3); + type=field_type(trim($4)); + if(type == "tlv" && size == "-") { + size="undefined"; + } else { + size=len(size); + } + params=params sprintf("%-25s{%s, %s, %s},\n", " ", field, size, type); +} + +END { +} diff --git a/src/otc.erl b/src/otc.erl index cef2bb9..3af761b 100644 --- a/src/otc.erl +++ b/src/otc.erl @@ -19,7 +19,8 @@ nas_5gs_5gsm/1, gtpv1c/1, gtpv2c/1, - sgsap/1 + sgsap/1, + smpp/1 ]). %% General functions @@ -37,7 +38,9 @@ %% Supported protocols --------------------------------------------------------- --type protocol() :: sctp_ppi | m3ua | m2pa | mtp3 | sccp | sccp_mgmt | tcap | map | nas_eps | nas_eps_emm | nas_eps_esm | gtpv1c | gtpv2c | sgsap. +-type protocol() :: sctp_ppi | m3ua | m2pa | mtp3 | sccp | sccp_mgmt | + tcap | map | nas_eps | nas_eps_emm | nas_eps_esm | + gtpv1c | gtpv2c | sgsap | smpp. next({sctp_ppi, V}) -> otc_sctp_ppi:next(V); %% next({sctp, V}) -> otc_sctp:next(V); @@ -56,7 +59,8 @@ next({nas_5gs_5gmm, V}) -> otc_nas_5gs_5gmm:next(V); next({nas_5gs_5gsm, V}) -> otc_nas_5gs_5gsm:next(V); next({gtpv1c, V}) -> otc_gtpv1c:next(V); next({gtpv2c, V}) -> otc_gtpv2c:next(V); -next({sgsap, V}) -> otc_sgsap:next(V). +next({sgsap, V}) -> otc_sgsap:next(V); +next({smpp, V}) -> otc_smpp:next(V). sctp_ppi(PPI) -> otc_sctp_ppi:codec(PPI). %% sctp(D) -> otc_sctp:codec(D). @@ -76,6 +80,7 @@ nas_5gs_5gsm(D) -> otc_nas_5gs_5gsm:codec(D). gtpv1c(D) -> otc_gtpv1c:codec(D). gtpv2c(D) -> otc_gtpv2c:codec(D). sgsap(D) -> otc_sgsap:codec(D). +smpp(D) -> otc_smpp:codec(D). %% General functions ----------------------------------------------------------- diff --git a/src/otc_smpp.erl b/src/otc_smpp.erl new file mode 100644 index 0000000..9842ed3 --- /dev/null +++ b/src/otc_smpp.erl @@ -0,0 +1,496 @@ +-module(otc_smpp). +-behaviour(otc_codec). + +-export([spec/0, + codec/1, + next/1, + decode/1, + encode/1 + ]). + +-include("include/smpp.hrl"). +-include_lib("kernel/include/logger.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +spec() -> + "SMS Forum SMPP V5.0". + +codec(Bin) when is_binary(Bin) -> + decode(Bin); +codec(Map) when is_map(Map) -> + encode({Map, <<>>}); +codec({Map, PDU}) -> + encode({Map, PDU}). + +next(_) -> + '$stop'. + +decode(Bin) -> + {Header, Rest} = parse_header(Bin), + Msg = decode_msg(Header, Rest), + maps:merge(Header, Msg). + +encode({_, _}) -> + <<>>. + +parse_header(Bin) -> + <> = Bin, + Header = #{command => parse_command_id(CID), + command_status => parse_command_status(CS), + sequence_number => SN}, + {Header, Rest}. + +parse_command_id(?SMPP_CMD_ID_BIND_RECEIVER) -> bind_receiver; +parse_command_id(?SMPP_CMD_ID_BIND_TRANSMITTER) -> bind_transmitter; +parse_command_id(?SMPP_CMD_ID_QUERY_SM) -> query_sm; +parse_command_id(?SMPP_CMD_ID_SUBMIT_SM) -> submit_sm; +parse_command_id(?SMPP_CMD_ID_DELIVER_SM) -> deliver_sm; +parse_command_id(?SMPP_CMD_ID_UNBIND) -> unbind; +parse_command_id(?SMPP_CMD_ID_REPLACE_SM) -> replace_sm; +parse_command_id(?SMPP_CMD_ID_CANCEL_SM) -> cancel_sm; +parse_command_id(?SMPP_CMD_ID_BIND_TRANSCEIVER) -> bind_transceiver; +parse_command_id(?SMPP_CMD_ID_OUTBIND) -> outbind; +parse_command_id(?SMPP_CMD_ID_ENQUIRE_LINK) -> enquire_link; +parse_command_id(?SMPP_CMD_ID_SUBMIT_MULTI) -> submit_multi; +parse_command_id(?SMPP_CMD_ID_ALERT_NOTIFICATION) -> alert_notification; +parse_command_id(?SMPP_CMD_ID_DATA_SM) -> data_sm; +parse_command_id(?SMPP_CMD_ID_BROADCAST_SM) -> broadcast_sm; +parse_command_id(?SMPP_CMD_ID_QUERY_BROADCAST_SM) -> query_broadcast_sm; +parse_command_id(?SMPP_CMD_ID_CANCEL_BROADCAST_SM) -> cancel_broadcast_sm; +parse_command_id(?SMPP_CMD_ID_GENERIC_NACK) -> generic_nack; +parse_command_id(?SMPP_CMD_ID_BIND_RECEIVER_RESP) -> bind_receiver_resp; +parse_command_id(?SMPP_CMD_ID_BIND_TRANSMITTER_RESP) -> bind_transmitter_resp; +parse_command_id(?SMPP_CMD_ID_QUERY_SM_RESP) -> query_sm_resp; +parse_command_id(?SMPP_CMD_ID_SUBMIT_SM_RESP) -> submit_sm_resp; +parse_command_id(?SMPP_CMD_ID_DELIVER_SM_RESP) -> deliver_sm_resp; +parse_command_id(?SMPP_CMD_ID_UNBIND_RESP) -> unbind_resp; +parse_command_id(?SMPP_CMD_ID_REPLACE_SM_RESP) -> replace_sm_resp; +parse_command_id(?SMPP_CMD_ID_CANCEL_SM_RESP) -> cancel_sm_resp; +parse_command_id(?SMPP_CMD_ID_BIND_TRANSCEIVER_RESP) -> bind_transceiver_resp; +parse_command_id(?SMPP_CMD_ID_ENQUIRE_LINK_RESP) -> enquire_link_resp; +parse_command_id(?SMPP_CMD_ID_SUBMIT_MULTI_RESP) -> submit_multi_resp; +parse_command_id(?SMPP_CMD_ID_DATA_SM_RESP) -> data_sm_resp; +parse_command_id(?SMPP_CMD_ID_BROADCAST_SM_RESP) -> broadcast_sm_resp; +parse_command_id(?SMPP_CMD_ID_QUERY_BROADCAST_SM_RESP) -> query_broadcast_sm_resp; +parse_command_id(?SMPP_CMD_ID_CANCEL_BROADCAST_SM_RESP) -> cancel_broadcast_sm_resp. + +parse_command_status(?SMPP_CMD_STATUS_ESME_ROK) -> + esme_rok; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBNDSTS) -> + esme_rinvbndsts; +parse_command_status(?SMPP_CMD_STATUS_ESME_RALYBND) -> + esme_ralybnd; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVPRTFLG) -> + esme_rinvprtflg; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVREGDLVFLG) -> + esme_rinvregdlvflg; +parse_command_status(?SMPP_CMD_STATUS_ESME_RSYSERR) -> + esme_rsyserr; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSRCADR) -> + esme_rinvsrcadr; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDSTADR) -> + esme_rinvdstadr; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVMSGID) -> + esme_rinvmsgid; +parse_command_status(?SMPP_CMD_STATUS_ESME_RBINDFAIL) -> + esme_rbindfail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVPASWD) -> + esme_rinvpaswd; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSYSID) -> + esme_rinvsysid; +parse_command_status(?SMPP_CMD_STATUS_ESME_RCANCELFAIL) -> + esme_rcancelfail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RREPLACEFAIL) -> + esme_rreplacefail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RMSGQFUL) -> + esme_rmsgqful; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSERTYP) -> + esme_rinvsertyp; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVNUMDESTS) -> + esme_rinvnumdests; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDLNAME) -> + esme_rinvdlname; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDESTFLAG) -> + esme_rinvdestflag; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSUBREP) -> + esme_rinvsubrep; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVESMCLASS) -> + esme_rinvesmclass; +parse_command_status(?SMPP_CMD_STATUS_ESME_RCNTSUBDL) -> + esme_rcntsubdl; +parse_command_status(?SMPP_CMD_STATUS_ESME_RSUBMITFAIL) -> + esme_rsubmitfail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSRCTON) -> + esme_rinvsrcton; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSRCNPI) -> + esme_rinvsrcnpi; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDSTTON) -> + esme_rinvdstton; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDSTNPI) -> + esme_rinvdstnpi; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSYSTYP) -> + esme_rinvsystyp; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVREPFLAG) -> + esme_rinvrepflag; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVNUMMSGS) -> + esme_rinvnummsgs; +parse_command_status(?SMPP_CMD_STATUS_ESME_RTHROTTLED) -> + esme_rthrottled; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSCHED) -> + esme_rinvsched; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVEXPIRY) -> + esme_rinvexpiry; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDFTMSGID) -> + esme_rinvdftmsgid; +parse_command_status(?SMPP_CMD_STATUS_ESME_RX_T_APPN) -> + esme_rx_t_appn; +parse_command_status(?SMPP_CMD_STATUS_ESME_RX_P_APPN) -> + esme_rx_p_appn; +parse_command_status(?SMPP_CMD_STATUS_ESME_RX_R_APPN) -> + esme_rx_r_appn; +parse_command_status(?SMPP_CMD_STATUS_ESME_RQUERYFAIL) -> + esme_rqueryfail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVTLVSTREAM) -> + esme_rinvtlvstream; +parse_command_status(?SMPP_CMD_STATUS_ESME_RTLVNOTALLWD) -> + esme_rtlvnotallwd; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVTLVLEN) -> + esme_rinvtlvlen; +parse_command_status(?SMPP_CMD_STATUS_ESME_RMISSINGTLV) -> + esme_rmissingtlv; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVTLVVAL) -> + esme_rinvtlvval; +parse_command_status(?SMPP_CMD_STATUS_ESME_RDELIVERYFAILURE) -> + esme_rdeliveryfailure; +parse_command_status(?SMPP_CMD_STATUS_ESME_RUNKNOWNERR) -> + esme_runknownerr; +parse_command_status(?SMPP_CMD_STATUS_ESME_RSERTYPUNAUTH) -> + esme_rsertypunauth; +parse_command_status(?SMPP_CMD_STATUS_ESME_RPROHIBITED) -> + esme_rprohibited; +parse_command_status(?SMPP_CMD_STATUS_ESME_RSERTYPUNAVAIL) -> + esme_rsertypunavail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RSERTYPDENIED) -> + esme_rsertypdenied; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDCS) -> + esme_rinvdcs; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVSRCADDRSUBUNIT) -> + esme_rinvsrcaddrsubunit; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVDSTADDRSUBUNIT) -> + esme_rinvdstaddrsubunit; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTFREQINT) -> + esme_rinvbcastfreqint; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTALIAS_NAME) -> + esme_rinvbcastalias_name; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTAREAFMT) -> + esme_rinvbcastareafmt; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVNUMBCAST_AREAS) -> + esme_rinvnumbcast_areas; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTCNTTYPE) -> + esme_rinvbcastcnttype; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTMSGCLASS) -> + esme_rinvbcastmsgclass; +parse_command_status(?SMPP_CMD_STATUS_ESME_RBCASTFAIL) -> + esme_rbcastfail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RBCASTQUERYFAIL) -> + esme_rbcastqueryfail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RBCASTCANCELFAIL) -> + esme_rbcastcancelfail; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCAST_REP) -> + esme_rinvbcast_rep; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTSRVGRP) -> + esme_rinvbcastsrvgrp; +parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTCHANIND) -> + esme_rinvbcastchanind; +parse_command_status(V) when V >= 16#00000400; V =< 16#000004FF -> + {vendor_specific, V}. + + +decode_msg(#{command := bind_transmitter}, Bin) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring}, + {system_type, {0, 13}, cstring}, + {interface_version, 1, integer}, + {addr_ton, 1, integer}, + {addr_npi, 1, integer}, + {address_range, {0, 41}, cstring} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := bind_transmitter_resp}, Bin) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {sc_interface_version, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := bind_receiver}, Bin) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring}, + {system_type, {0, 13}, cstring}, + {interface_version, 1, integer}, + {addr_ton, 1, integer}, + {addr_npi, 1, integer}, + {address_range, {0, 41}, cstring} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := bind_receiver_resp}, Bin) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {sc_interface_version, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := bind_transceiver}, Bin) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring}, + {system_type, {0, 13}, cstring}, + {interface_version, 1, integer}, + {addr_ton, 1, integer}, + {addr_npi, 1, integer}, + {address_range, {0, 41}, cstring} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := bind_transceiver_resp}, Bin) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {sc_interface_version, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := outbind}, Bin) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := unbind}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := unbind_resp}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := enquire_link}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := enquire_link_resp}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := alert_notification}, Bin) -> + AllowedParameters = [{source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {esme_addr_ton, 1, integer}, + {esme_addr_npi, 1, integer}, + {esme_addr, {0, 65}, cstring}, + {ms_availability_status, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := generic_nack}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := submit_sm}, Bin) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring}, + {esm_class, 1, integer}, + {protocol_id, 1, integer}, + {priority_flag, 1, integer}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {replace_if_present_flag, 1, integer}, + {data_coding, 1, integer}, + {sm_default_msg_id, 1, integer}, + {sm_length, 1, integer}, + {short_message, {0, 255}, string}, + {message_submission_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := submit_sm_resp}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_submission_response_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := data_sm}, Bin) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring}, + {esm_class, 1, integer}, + {registered_delivery, 1, integer}, + {data_coding, 1, integer}, + {message_submission_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := data_sm_resp}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_submission_response_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := submit_multi}, Bin) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {number_of_dests, 1, integer}, + {dest_address, {0, 24}, composite}, + {esm_class, 1, integer}, + {protocol_id, 1, integer}, + {priority_flag, 1, integer}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {replace_if_present_flag, 1, integer}, + {data_coding, 1, integer}, + {sm_default_msg_id, 1, integer}, + {sm_length, 1, integer}, + {short_message, {0, 255}, string}, + {message_submission_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := submit_multi_resp}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {no_unsuccess, 1, integer}, + {unsuccess_sme, {0, 27}, composite}, + {message_submission_response_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := deliver_sm}, Bin) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring}, + {esm_class, 1, integer}, + {protocol_id, 1, integer}, + {priority_flag, 1, integer}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {replace_if_present_flag, 1, integer}, + {data_coding, 1, integer}, + {sm_default_msg_id, 1, integer}, + {sm_length, 1, integer}, + {short_message, {0, 255}, string}, + {message_delivery_request_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := deliver_sm_resp}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_delivery_response_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := broadcast_sm_resp}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {broadcast_response_optional_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := cancel_sm}, Bin) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := cancel_sm_resp}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := query_sm}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := query_sm_resp}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {final_date, {1,17}, cstring}, + {message_state, 1, integer}, + {error_code, 1, integer} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := replace_sm}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {sm_default_msg_id, 1, integer}, + {sm_length, 1, integer}, + {short_message, {0, 255}, string}, + {message_replacement_request_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := replace_sm_resp}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := query_broadcast_sm}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {query_broadcast_request_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := query_broadcast_sm_resp}, Bin) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_state, 1, integer}, + {broadcast_area_identifier, undefined, tlv}, + {broadcast_area_success, undefined, tlv}, + {query_broadcast_response_tlvs, undefined, tlv} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := cancel_broadcast_sm}, Bin) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring} + ], + decode_parameters(AllowedParameters, Bin); +decode_msg(#{command := cancel_broadcast_sm_resp}, Bin) -> + AllowedParameters = [], + decode_parameters(AllowedParameters, Bin). + +decode_parameters(Allowed, Bin) -> + decode_parameters(Allowed, Bin, #{}). + +decode_parameters([], <<>>, Acc) -> + Acc; +decode_parameters([], Bin, Acc) -> + ?LOG_INFO("More to decode, have: ~p, still need: ~p", [Acc, Bin]), + Acc; +decode_parameters(Allowed, <<>>, Acc) -> + ?LOG_INFO("Nothing more to decode but still want params, have: ~p, want: ~p", [Acc, Allowed]), + Acc; +decode_parameters([{Name, Length, integer}|Ts], Bin, Acc) -> + <> = Bin, + decode_parameters(Ts, Rest, Acc#{Name => Value}); +decode_parameters([{Name, {MinL, MaxL}, cstring}|Ts], Bin, Acc) -> + {Value, Rest} = extract_cstring(Bin, MinL, MaxL), + Length = byte_size(Value), + true = Length >= MinL andalso Length =< MaxL, + decode_parameters(Ts, Rest, Acc#{Name => Value}); +decode_parameters([{Name, {MinL, MaxL}, string}|Ts], Bin, Acc) -> + <> = Bin, + true = Length >= MinL andalso Length =< MaxL, + decode_parameters(Ts, Rest, Acc#{Name => Value}); +decode_parameters([{Name, Length, tlv}|Ts], Bin, Acc) -> + <> = Bin, + decode_parameters(Ts, Rest, Acc#{Name => {Tag, Value}}). + +extract_cstring(Bin, MinL, MaxL) -> + case string:split(Bin, <<0>>) of + [A] when byte_size(A) >= MinL, byte_size(A) =< MaxL -> + {A, <<>>}; + [A, Rest] when byte_size(A) >= MinL, byte_size(A) =< MaxL -> + {A, Rest} + end. diff --git a/test/otc_smpp_tests.erl b/test/otc_smpp_tests.erl new file mode 100644 index 0000000..3099990 --- /dev/null +++ b/test/otc_smpp_tests.erl @@ -0,0 +1,116 @@ +-module(otc_smpp_tests). + +-include_lib("eunit/include/eunit.hrl"). + +bind_transmitter_test() -> + Bin = <<"00000028" + "00000002" + "00000000" + "00000001" + "61626869" + "6b0070617373776f726400534d50500001000000">>, + Map = #{command => bind_transmitter, + command_status => esme_rok, + sequence_number => 1, + system_id => <<"abhik">>, + password => <<"password">>, + system_type => <<"SMPP">>, + interface_version => 1, + addr_ton => 0, + addr_npi => 0, + address_range => <<>> + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + +bind_transmitter_resp_test() -> + Bin = <<"00000015800000020000000000000001534d534300">>, + Map = #{command => bind_transmitter_resp, + command_status => esme_rok, + sequence_number => 1, + system_id => <<"SMSC">> + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + +enquire_link_test() -> + Bin = <<"00000010000000150000000000000002">>, + Map = #{command => enquire_link, + command_status => esme_rok, + sequence_number => 2 + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + +enquire_link_resp_test() -> + Bin = <<"00000010800000150000000000000002">>, + Map = #{command => enquire_link_resp, + command_status => esme_rok, + sequence_number => 2 + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + +submit_sm_test() -> + Bin = <<"0000003200000004000000000000000300000131303000010131323334353637383930000000000000000000000474657374">>, + Map = #{command => submit_sm, + command_status => esme_rok, + sequence_number => 3 + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + + +submit_sm_resp_test() -> + Bin = <<"00000019800000040000000000000003343763386337376100">>, + Map = #{command => submit_sm_resp, + command_status => esme_rok, + sequence_number => 3, + message_id => <<"47c8c77a">> + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + +unbind_test() -> + Bin = <<"00000010000000060000000000000004">>, + Map = #{command => unbind, + command_status => esme_rok, + sequence_number => 4 + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + +unbind_resp_test() -> + Bin = <<"00000010800000060000000000000004">>, + Map = #{command => unbind_resp, + command_status => esme_rok, + sequence_number => 4 + }, + Msg = otc_smpp:decode(binary:decode_hex(Bin)), + ?assertMatch(Map, Msg)%% , + %% NewBin = otc_smpp:encode(Map), + %% ?assertMatch(Bin, binary:encode_hex(NewBin)) + . + + From 6491549f84a6d819ee99ef6d1beb998b3277d72f Mon Sep 17 00:00:00 2001 From: Sebastian Weddmark Olsson Date: Mon, 7 Oct 2024 20:17:16 +0200 Subject: [PATCH 2/6] fixes --- scripts/smpp_decode_msgs.awk | 4 ++++ src/otc_smpp.erl | 39 ++++++++++++++++++++++-------------- test/otc_smpp_tests.erl | 19 +++++++++++++++++- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/scripts/smpp_decode_msgs.awk b/scripts/smpp_decode_msgs.awk index fbb0285..4326670 100644 --- a/scripts/smpp_decode_msgs.awk +++ b/scripts/smpp_decode_msgs.awk @@ -108,6 +108,10 @@ start { field=atom(trim($2)); size=trim($3); type=field_type(trim($4)); + if(field == "sm_length") { + # no need for the short_message field + next; + } if(type == "tlv" && size == "-") { size="undefined"; } else { diff --git a/src/otc_smpp.erl b/src/otc_smpp.erl index 9842ed3..6456132 100644 --- a/src/otc_smpp.erl +++ b/src/otc_smpp.erl @@ -298,7 +298,6 @@ decode_msg(#{command := submit_sm}, Bin) -> {replace_if_present_flag, 1, integer}, {data_coding, 1, integer}, {sm_default_msg_id, 1, integer}, - {sm_length, 1, integer}, {short_message, {0, 255}, string}, {message_submission_tlvs, undefined, tlv} ], @@ -343,7 +342,6 @@ decode_msg(#{command := submit_multi}, Bin) -> {replace_if_present_flag, 1, integer}, {data_coding, 1, integer}, {sm_default_msg_id, 1, integer}, - {sm_length, 1, integer}, {short_message, {0, 255}, string}, {message_submission_tlvs, undefined, tlv} ], @@ -372,7 +370,6 @@ decode_msg(#{command := deliver_sm}, Bin) -> {replace_if_present_flag, 1, integer}, {data_coding, 1, integer}, {sm_default_msg_id, 1, integer}, - {sm_length, 1, integer}, {short_message, {0, 255}, string}, {message_delivery_request_tlvs, undefined, tlv} ], @@ -424,7 +421,6 @@ decode_msg(#{command := replace_sm}, Bin) -> {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {sm_default_msg_id, 1, integer}, - {sm_length, 1, integer}, {short_message, {0, 255}, string}, {message_replacement_request_tlvs, undefined, tlv} ], @@ -472,25 +468,38 @@ decode_parameters(Allowed, <<>>, Acc) -> ?LOG_INFO("Nothing more to decode but still want params, have: ~p, want: ~p", [Acc, Allowed]), Acc; decode_parameters([{Name, Length, integer}|Ts], Bin, Acc) -> - <> = Bin, - decode_parameters(Ts, Rest, Acc#{Name => Value}); + <> = Bin, + NewAcc = decode_parameter(Name, Value, Acc), + decode_parameters(Ts, Rest, NewAcc); decode_parameters([{Name, {MinL, MaxL}, cstring}|Ts], Bin, Acc) -> - {Value, Rest} = extract_cstring(Bin, MinL, MaxL), - Length = byte_size(Value), + {Value, Rest} = extract_cstring(Bin), + Length = byte_size(Value)+1, true = Length >= MinL andalso Length =< MaxL, - decode_parameters(Ts, Rest, Acc#{Name => Value}); + NewAcc = decode_parameter(Name, Value, Acc), + decode_parameters(Ts, Rest, NewAcc); decode_parameters([{Name, {MinL, MaxL}, string}|Ts], Bin, Acc) -> + timer:sleep(100), <> = Bin, true = Length >= MinL andalso Length =< MaxL, - decode_parameters(Ts, Rest, Acc#{Name => Value}); + NewAcc = decode_parameter(Name, Value, Acc), + decode_parameters(Ts, Rest, NewAcc); decode_parameters([{Name, Length, tlv}|Ts], Bin, Acc) -> - <> = Bin, - decode_parameters(Ts, Rest, Acc#{Name => {Tag, Value}}). + <> = Bin, + NewAcc = decode_tlv(Name, Tag, Value, Acc), + decode_parameters(Ts, Rest, NewAcc). -extract_cstring(Bin, MinL, MaxL) -> +extract_cstring(Bin) -> case string:split(Bin, <<0>>) of - [A] when byte_size(A) >= MinL, byte_size(A) =< MaxL -> + [] -> + {<<>>, <<>>}; + [A] -> {A, <<>>}; - [A, Rest] when byte_size(A) >= MinL, byte_size(A) =< MaxL -> + [A, Rest] -> {A, Rest} end. + +decode_parameter(Name, Value, Acc) -> + Acc#{Name => Value}. + +decode_tlv(Name, Tag, Value, Acc) -> + Acc#{Name => {Tag, Value}}. diff --git a/test/otc_smpp_tests.erl b/test/otc_smpp_tests.erl index 3099990..f83db96 100644 --- a/test/otc_smpp_tests.erl +++ b/test/otc_smpp_tests.erl @@ -67,7 +67,24 @@ submit_sm_test() -> Bin = <<"0000003200000004000000000000000300000131303000010131323334353637383930000000000000000000000474657374">>, Map = #{command => submit_sm, command_status => esme_rok, - sequence_number => 3 + sequence_number => 3, + service_type => <<>>, + source_addr_ton => 0, + source_addr_npi => 1, + source_addr => <<"100">>, + dest_addr_ton => 1, + dest_addr_npi => 1, + destination_addr => <<"1234567890">>, + esm_class => 0, + protocol_id => 0, + priority_flag => 0, + schedule_delivery_time => <<>>, + validity_period => <<>>, + registered_delivery => 0, + replace_if_present_flag => 0, + data_coding => 0, + sm_default_msg_id => 0, + short_message => <<"test">> }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), ?assertMatch(Map, Msg)%% , From 4c7999b607bd6b1553ceac702f6ea4b6845be0a3 Mon Sep 17 00:00:00 2001 From: Sebastian Weddmark Olsson Date: Thu, 19 Dec 2024 10:25:38 +0100 Subject: [PATCH 3/6] add tests --- src/otc_smpp.erl | 573 +++++++++++++++++++++++++++++++++++++++- test/otc_smpp_tests.erl | 96 ++++--- 2 files changed, 626 insertions(+), 43 deletions(-) diff --git a/src/otc_smpp.erl b/src/otc_smpp.erl index 6456132..171bab9 100644 --- a/src/otc_smpp.erl +++ b/src/otc_smpp.erl @@ -30,8 +30,13 @@ decode(Bin) -> Msg = decode_msg(Header, Rest), maps:merge(Header, Msg). -encode({_, _}) -> - <<>>. +encode({Map, PDU}) -> + M = encode(Map), + <>; +encode(Map) -> + Msg = encode_msg(Map), + Header = compose_header(Map, Msg), + <
>. parse_header(Bin) -> <> = Bin, @@ -40,6 +45,15 @@ parse_header(Bin) -> sequence_number => SN}, {Header, Rest}. +compose_header(Map, Msg) -> + #{command := Command, + command_status := CommandStatus, + sequence_number := SN} = Map, + CL = byte_size(Msg) + 16, + CID = compose_command_id(Command), + CS = compose_command_status(CommandStatus), + <>. + parse_command_id(?SMPP_CMD_ID_BIND_RECEIVER) -> bind_receiver; parse_command_id(?SMPP_CMD_ID_BIND_TRANSMITTER) -> bind_transmitter; parse_command_id(?SMPP_CMD_ID_QUERY_SM) -> query_sm; @@ -74,6 +88,40 @@ parse_command_id(?SMPP_CMD_ID_BROADCAST_SM_RESP) -> broadcast_sm_resp; parse_command_id(?SMPP_CMD_ID_QUERY_BROADCAST_SM_RESP) -> query_broadcast_sm_resp; parse_command_id(?SMPP_CMD_ID_CANCEL_BROADCAST_SM_RESP) -> cancel_broadcast_sm_resp. +compose_command_id(bind_receiver) -> ?SMPP_CMD_ID_BIND_RECEIVER; +compose_command_id(bind_transmitter) -> ?SMPP_CMD_ID_BIND_TRANSMITTER; +compose_command_id(query_sm) -> ?SMPP_CMD_ID_QUERY_SM; +compose_command_id(submit_sm) -> ?SMPP_CMD_ID_SUBMIT_SM; +compose_command_id(deliver_sm) -> ?SMPP_CMD_ID_DELIVER_SM; +compose_command_id(unbind) -> ?SMPP_CMD_ID_UNBIND; +compose_command_id(replace_sm) -> ?SMPP_CMD_ID_REPLACE_SM; +compose_command_id(cancel_sm) -> ?SMPP_CMD_ID_CANCEL_SM; +compose_command_id(bind_transceiver) -> ?SMPP_CMD_ID_BIND_TRANSCEIVER; +compose_command_id(outbind) -> ?SMPP_CMD_ID_OUTBIND; +compose_command_id(enquire_link) -> ?SMPP_CMD_ID_ENQUIRE_LINK; +compose_command_id(submit_multi) -> ?SMPP_CMD_ID_SUBMIT_MULTI; +compose_command_id(alert_notification) -> ?SMPP_CMD_ID_ALERT_NOTIFICATION; +compose_command_id(data_sm) -> ?SMPP_CMD_ID_DATA_SM; +compose_command_id(broadcast_sm) -> ?SMPP_CMD_ID_BROADCAST_SM; +compose_command_id(query_broadcast_sm) -> ?SMPP_CMD_ID_QUERY_BROADCAST_SM; +compose_command_id(cancel_broadcast_sm) -> ?SMPP_CMD_ID_CANCEL_BROADCAST_SM; +compose_command_id(generic_nack) -> ?SMPP_CMD_ID_GENERIC_NACK; +compose_command_id(bind_receiver_resp) -> ?SMPP_CMD_ID_BIND_RECEIVER_RESP; +compose_command_id(bind_transmitter_resp) -> ?SMPP_CMD_ID_BIND_TRANSMITTER_RESP; +compose_command_id(query_sm_resp) -> ?SMPP_CMD_ID_QUERY_SM_RESP; +compose_command_id(submit_sm_resp) -> ?SMPP_CMD_ID_SUBMIT_SM_RESP; +compose_command_id(deliver_sm_resp) -> ?SMPP_CMD_ID_DELIVER_SM_RESP; +compose_command_id(unbind_resp) -> ?SMPP_CMD_ID_UNBIND_RESP; +compose_command_id(replace_sm_resp) -> ?SMPP_CMD_ID_REPLACE_SM_RESP; +compose_command_id(cancel_sm_resp) -> ?SMPP_CMD_ID_CANCEL_SM_RESP; +compose_command_id(bind_transceiver_resp) -> ?SMPP_CMD_ID_BIND_TRANSCEIVER_RESP; +compose_command_id(enquire_link_resp) -> ?SMPP_CMD_ID_ENQUIRE_LINK_RESP; +compose_command_id(submit_multi_resp) -> ?SMPP_CMD_ID_SUBMIT_MULTI_RESP; +compose_command_id(data_sm_resp) -> ?SMPP_CMD_ID_DATA_SM_RESP; +compose_command_id(broadcast_sm_resp) -> ?SMPP_CMD_ID_BROADCAST_SM_RESP; +compose_command_id(query_broadcast_sm_resp) -> ?SMPP_CMD_ID_QUERY_BROADCAST_SM_RESP; +compose_command_id(cancel_broadcast_sm_resp) -> ?SMPP_CMD_ID_CANCEL_BROADCAST_SM_RESP. + parse_command_status(?SMPP_CMD_STATUS_ESME_ROK) -> esme_rok; parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBNDSTS) -> @@ -205,6 +253,136 @@ parse_command_status(?SMPP_CMD_STATUS_ESME_RINVBCASTCHANIND) -> parse_command_status(V) when V >= 16#00000400; V =< 16#000004FF -> {vendor_specific, V}. +compose_command_status(esme_rok) -> + ?SMPP_CMD_STATUS_ESME_ROK; +compose_command_status(esme_rinvbndsts) -> + ?SMPP_CMD_STATUS_ESME_RINVBNDSTS; +compose_command_status(esme_ralybnd) -> + ?SMPP_CMD_STATUS_ESME_RALYBND; +compose_command_status(esme_rinvprtflg) -> + ?SMPP_CMD_STATUS_ESME_RINVPRTFLG; +compose_command_status(esme_rinvregdlvflg) -> + ?SMPP_CMD_STATUS_ESME_RINVREGDLVFLG; +compose_command_status(esme_rsyserr) -> + ?SMPP_CMD_STATUS_ESME_RSYSERR; +compose_command_status(esme_rinvsrcadr) -> + ?SMPP_CMD_STATUS_ESME_RINVSRCADR; +compose_command_status(esme_rinvdstadr) -> + ?SMPP_CMD_STATUS_ESME_RINVDSTADR; +compose_command_status(esme_rinvmsgid) -> + ?SMPP_CMD_STATUS_ESME_RINVMSGID; +compose_command_status(esme_rbindfail) -> + ?SMPP_CMD_STATUS_ESME_RBINDFAIL; +compose_command_status(esme_rinvpaswd) -> + ?SMPP_CMD_STATUS_ESME_RINVPASWD; +compose_command_status(esme_rinvsysid) -> + ?SMPP_CMD_STATUS_ESME_RINVSYSID; +compose_command_status(esme_rcancelfail) -> + ?SMPP_CMD_STATUS_ESME_RCANCELFAIL; +compose_command_status(esme_rreplacefail) -> + ?SMPP_CMD_STATUS_ESME_RREPLACEFAIL; +compose_command_status(esme_rmsgqful) -> + ?SMPP_CMD_STATUS_ESME_RMSGQFUL; +compose_command_status(esme_rinvsertyp) -> + ?SMPP_CMD_STATUS_ESME_RINVSERTYP; +compose_command_status(esme_rinvnumdests) -> + ?SMPP_CMD_STATUS_ESME_RINVNUMDESTS; +compose_command_status(esme_rinvdlname) -> + ?SMPP_CMD_STATUS_ESME_RINVDLNAME; +compose_command_status(esme_rinvdestflag) -> + ?SMPP_CMD_STATUS_ESME_RINVDESTFLAG; +compose_command_status(esme_rinvsubrep) -> + ?SMPP_CMD_STATUS_ESME_RINVSUBREP; +compose_command_status(esme_rinvesmclass) -> + ?SMPP_CMD_STATUS_ESME_RINVESMCLASS; +compose_command_status(esme_rcntsubdl) -> + ?SMPP_CMD_STATUS_ESME_RCNTSUBDL; +compose_command_status(esme_rsubmitfail) -> + ?SMPP_CMD_STATUS_ESME_RSUBMITFAIL; +compose_command_status(esme_rinvsrcton) -> + ?SMPP_CMD_STATUS_ESME_RINVSRCTON; +compose_command_status(esme_rinvsrcnpi) -> + ?SMPP_CMD_STATUS_ESME_RINVSRCNPI; +compose_command_status(esme_rinvdstton) -> + ?SMPP_CMD_STATUS_ESME_RINVDSTTON; +compose_command_status(esme_rinvdstnpi) -> + ?SMPP_CMD_STATUS_ESME_RINVDSTNPI; +compose_command_status(esme_rinvsystyp) -> + ?SMPP_CMD_STATUS_ESME_RINVSYSTYP; +compose_command_status(esme_rinvrepflag) -> + ?SMPP_CMD_STATUS_ESME_RINVREPFLAG; +compose_command_status(esme_rinvnummsgs) -> + ?SMPP_CMD_STATUS_ESME_RINVNUMMSGS; +compose_command_status(esme_rthrottled) -> + ?SMPP_CMD_STATUS_ESME_RTHROTTLED; +compose_command_status(esme_rinvsched) -> + ?SMPP_CMD_STATUS_ESME_RINVSCHED; +compose_command_status(esme_rinvexpiry) -> + ?SMPP_CMD_STATUS_ESME_RINVEXPIRY; +compose_command_status(esme_rinvdftmsgid) -> + ?SMPP_CMD_STATUS_ESME_RINVDFTMSGID; +compose_command_status(esme_rx_t_appn) -> + ?SMPP_CMD_STATUS_ESME_RX_T_APPN; +compose_command_status(esme_rx_p_appn) -> + ?SMPP_CMD_STATUS_ESME_RX_P_APPN; +compose_command_status(esme_rx_r_appn) -> + ?SMPP_CMD_STATUS_ESME_RX_R_APPN; +compose_command_status(esme_rqueryfail) -> + ?SMPP_CMD_STATUS_ESME_RQUERYFAIL; +compose_command_status(esme_rinvtlvstream) -> + ?SMPP_CMD_STATUS_ESME_RINVTLVSTREAM; +compose_command_status(esme_rtlvnotallwd) -> + ?SMPP_CMD_STATUS_ESME_RTLVNOTALLWD; +compose_command_status(esme_rinvtlvlen) -> + ?SMPP_CMD_STATUS_ESME_RINVTLVLEN; +compose_command_status(esme_rmissingtlv) -> + ?SMPP_CMD_STATUS_ESME_RMISSINGTLV; +compose_command_status(esme_rinvtlvval) -> + ?SMPP_CMD_STATUS_ESME_RINVTLVVAL; +compose_command_status(esme_rdeliveryfailure) -> + ?SMPP_CMD_STATUS_ESME_RDELIVERYFAILURE; +compose_command_status(esme_runknownerr) -> + ?SMPP_CMD_STATUS_ESME_RUNKNOWNERR; +compose_command_status(esme_rsertypunauth) -> + ?SMPP_CMD_STATUS_ESME_RSERTYPUNAUTH; +compose_command_status(esme_rprohibited) -> + ?SMPP_CMD_STATUS_ESME_RPROHIBITED; +compose_command_status(esme_rsertypunavail) -> + ?SMPP_CMD_STATUS_ESME_RSERTYPUNAVAIL; +compose_command_status(esme_rsertypdenied) -> + ?SMPP_CMD_STATUS_ESME_RSERTYPDENIED; +compose_command_status(esme_rinvdcs) -> + ?SMPP_CMD_STATUS_ESME_RINVDCS; +compose_command_status(esme_rinvsrcaddrsubunit) -> + ?SMPP_CMD_STATUS_ESME_RINVSRCADDRSUBUNIT; +compose_command_status(esme_rinvdstaddrsubunit) -> + ?SMPP_CMD_STATUS_ESME_RINVDSTADDRSUBUNIT; +compose_command_status(esme_rinvbcastfreqint) -> + ?SMPP_CMD_STATUS_ESME_RINVBCASTFREQINT; +compose_command_status(esme_rinvbcastalias_name) -> + ?SMPP_CMD_STATUS_ESME_RINVBCASTALIAS_NAME; +compose_command_status(esme_rinvbcastareafmt) -> + ?SMPP_CMD_STATUS_ESME_RINVBCASTAREAFMT; +compose_command_status(esme_rinvnumbcast_areas) -> + ?SMPP_CMD_STATUS_ESME_RINVNUMBCAST_AREAS; +compose_command_status(esme_rinvbcastcnttype) -> + ?SMPP_CMD_STATUS_ESME_RINVBCASTCNTTYPE; +compose_command_status(esme_rinvbcastmsgclass) -> + ?SMPP_CMD_STATUS_ESME_RINVBCASTMSGCLASS; +compose_command_status(esme_rbcastfail) -> + ?SMPP_CMD_STATUS_ESME_RBCASTFAIL; +compose_command_status(esme_rbcastqueryfail) -> + ?SMPP_CMD_STATUS_ESME_RBCASTQUERYFAIL; +compose_command_status(esme_rbcastcancelfail) -> + ?SMPP_CMD_STATUS_ESME_RBCASTCANCELFAIL; +compose_command_status(esme_rinvbcast_rep) -> + ?SMPP_CMD_STATUS_ESME_RINVBCAST_REP; +compose_command_status(esme_rinvbcastsrvgrp) -> + ?SMPP_CMD_STATUS_ESME_RINVBCASTSRVGRP; +compose_command_status(esme_rinvbcastchanind) -> + ?SMPP_CMD_STATUS_ESME_RINVBCASTCHANIND; +compose_command_status({vendor_specific, V}) -> + V. decode_msg(#{command := bind_transmitter}, Bin) -> AllowedParameters = [{system_id, {0, 16}, cstring}, @@ -456,9 +634,263 @@ decode_msg(#{command := cancel_broadcast_sm_resp}, Bin) -> AllowedParameters = [], decode_parameters(AllowedParameters, Bin). +encode_msg(#{command := bind_transmitter} = Map) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring}, + {system_type, {0, 13}, cstring}, + {interface_version, 1, integer}, + {addr_ton, 1, integer}, + {addr_npi, 1, integer}, + {address_range, {0, 41}, cstring} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := bind_transmitter_resp} = Map) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {sc_interface_version, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := bind_receiver} = Map) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring}, + {system_type, {0, 13}, cstring}, + {interface_version, 1, integer}, + {addr_ton, 1, integer}, + {addr_npi, 1, integer}, + {address_range, {0, 41}, cstring} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := bind_receiver_resp} = Map) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {sc_interface_version, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := bind_transceiver} = Map) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring}, + {system_type, {0, 13}, cstring}, + {interface_version, 1, integer}, + {addr_ton, 1, integer}, + {addr_npi, 1, integer}, + {address_range, {0, 41}, cstring} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := bind_transceiver_resp} = Map) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {sc_interface_version, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := outbind} = Map) -> + AllowedParameters = [{system_id, {0, 16}, cstring}, + {password, {0, 9}, cstring} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := unbind} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := unbind_resp} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := enquire_link} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := enquire_link_resp} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := alert_notification} = Map) -> + AllowedParameters = [{source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {esme_addr_ton, 1, integer}, + {esme_addr_npi, 1, integer}, + {esme_addr, {0, 65}, cstring}, + {ms_availability_status, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := generic_nack} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := submit_sm} = Map) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring}, + {esm_class, 1, integer}, + {protocol_id, 1, integer}, + {priority_flag, 1, integer}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {replace_if_present_flag, 1, integer}, + {data_coding, 1, integer}, + {sm_default_msg_id, 1, integer}, + {short_message, {0, 255}, string}, + {message_submission_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := submit_sm_resp} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_submission_response_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := data_sm} = Map) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring}, + {esm_class, 1, integer}, + {registered_delivery, 1, integer}, + {data_coding, 1, integer}, + {message_submission_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := data_sm_resp} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_submission_response_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := submit_multi} = Map) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {number_of_dests, 1, integer}, + {dest_address, {0, 24}, composite}, + {esm_class, 1, integer}, + {protocol_id, 1, integer}, + {priority_flag, 1, integer}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {replace_if_present_flag, 1, integer}, + {data_coding, 1, integer}, + {sm_default_msg_id, 1, integer}, + {short_message, {0, 255}, string}, + {message_submission_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := submit_multi_resp} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {no_unsuccess, 1, integer}, + {unsuccess_sme, {0, 27}, composite}, + {message_submission_response_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := deliver_sm} = Map) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring}, + {esm_class, 1, integer}, + {protocol_id, 1, integer}, + {priority_flag, 1, integer}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {replace_if_present_flag, 1, integer}, + {data_coding, 1, integer}, + {sm_default_msg_id, 1, integer}, + {short_message, {0, 255}, string}, + {message_delivery_request_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := deliver_sm_resp} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_delivery_response_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := broadcast_sm_resp} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {broadcast_response_optional_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := cancel_sm} = Map) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {dest_addr_ton, 1, integer}, + {dest_addr_npi, 1, integer}, + {destination_addr, {0, 21}, cstring} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := cancel_sm_resp} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := query_sm} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := query_sm_resp} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {final_date, {1,17}, cstring}, + {message_state, 1, integer}, + {error_code, 1, integer} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := replace_sm} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {schedule_delivery_time, {1,17}, cstring}, + {validity_period, {1,17}, cstring}, + {registered_delivery, 1, integer}, + {sm_default_msg_id, 1, integer}, + {short_message, {0, 255}, string}, + {message_replacement_request_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := replace_sm_resp} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := query_broadcast_sm} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring}, + {query_broadcast_request_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := query_broadcast_sm_resp} = Map) -> + AllowedParameters = [{message_id, {0, 65}, cstring}, + {message_state, 1, integer}, + {broadcast_area_identifier, undefined, tlv}, + {broadcast_area_success, undefined, tlv}, + {query_broadcast_response_tlvs, undefined, tlv} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := cancel_broadcast_sm} = Map) -> + AllowedParameters = [{service_type, {0, 6}, cstring}, + {message_id, {0, 65}, cstring}, + {source_addr_ton, 1, integer}, + {source_addr_npi, 1, integer}, + {source_addr, {0, 65}, cstring} + ], + encode_parameters(AllowedParameters, Map); +encode_msg(#{command := cancel_broadcast_sm_resp} = Map) -> + AllowedParameters = [], + encode_parameters(AllowedParameters, Map). + + decode_parameters(Allowed, Bin) -> decode_parameters(Allowed, Bin, #{}). +encode_parameters(Allowed, Map) -> + encode_parameters(lists:reverse(Allowed), Map, <<>>). + decode_parameters([], <<>>, Acc) -> Acc; decode_parameters([], Bin, Acc) -> @@ -478,7 +910,6 @@ decode_parameters([{Name, {MinL, MaxL}, cstring}|Ts], Bin, Acc) -> NewAcc = decode_parameter(Name, Value, Acc), decode_parameters(Ts, Rest, NewAcc); decode_parameters([{Name, {MinL, MaxL}, string}|Ts], Bin, Acc) -> - timer:sleep(100), <> = Bin, true = Length >= MinL andalso Length =< MaxL, NewAcc = decode_parameter(Name, Value, Acc), @@ -488,6 +919,49 @@ decode_parameters([{Name, Length, tlv}|Ts], Bin, Acc) -> NewAcc = decode_tlv(Name, Tag, Value, Acc), decode_parameters(Ts, Rest, NewAcc). +encode_parameters([], _, Acc) -> + Acc; +encode_parameters(_, Map, Acc) when map_size(Map) =:= 0 -> + Acc; +encode_parameters([{Name, Length, integer}|Ts], Map, Acc) -> + case maps:take(Name, Map) of + error -> + encode_parameters(Ts, Map, Acc); + {Value, NewMap} -> + NewVal = encode_parameter(Name, Value), + NewAcc = <>, + encode_parameters(Ts, NewMap, NewAcc) + end; +encode_parameters([{Name, {_MinL, _MaxL}, cstring}|Ts], Map, Acc) -> + case maps:take(Name, Map) of + error -> + encode_parameters(Ts, Map, Acc); + {Value, NewMap} -> + NewVal = encode_parameter(Name, Value), + NewAcc = <>, + encode_parameters(Ts, NewMap, NewAcc) + end; +encode_parameters([{Name, {_MinL, _MaxL}, string}|Ts], Map, Acc) -> + case maps:take(Name, Map) of + error -> + encode_parameters(Ts, Map, Acc); + {Value, NewMap} -> + NewVal = encode_parameter(Name, Value), + Length = byte_size(NewVal), + NewAcc = <>, + encode_parameters(Ts, NewMap, NewAcc) + end; +encode_parameters([{Name, Length, tlv}|Ts], Map, Acc) -> + case maps:take(Name, Map) of + error -> + encode_parameters(Ts, Map, Acc); + {Tlv, NewMap} -> + {Tag, Value} = encode_tlv(Tlv), + NewVal = encode_parameter(Name, Value), + NewAcc = <>, + encode_parameters(Ts, NewMap, NewAcc) + end. + extract_cstring(Bin) -> case string:split(Bin, <<0>>) of [] -> @@ -498,8 +972,101 @@ extract_cstring(Bin) -> {A, Rest} end. +decode_parameter(addr_ton, Value, Acc) -> + Acc#{addr_ton => parse_ton(Value)}; +decode_parameter(source_addr_ton, Value, Acc) -> + Acc#{source_addr_ton => parse_ton(Value)}; +decode_parameter(dest_addr_ton, Value, Acc) -> + Acc#{dest_addr_ton => parse_ton(Value)}; +decode_parameter(esme_addr_ton, Value, Acc) -> + Acc#{esme_addr_ton => parse_ton(Value)}; +decode_parameter(addr_npi, Value, Acc) -> + Acc#{addr_npi => parse_npi(Value)}; +decode_parameter(source_addr_npi, Value, Acc) -> + Acc#{source_addr_npi => parse_npi(Value)}; +decode_parameter(dest_addr_npi, Value, Acc) -> + Acc#{dest_addr_npi => parse_npi(Value)}; +decode_parameter(esme_addr_npi, Value, Acc) -> + Acc#{esme_addr_npi => parse_npi(Value)}; +decode_parameter(message_state, Value, Acc) -> + Acc#{message_state => parse_message_state(Value)}; +decode_parameter(replace_if_present_flag, Value, Acc) -> + Acc#{replace_if_present_flag => case Value of + 0 -> false; + 1 -> true + end + }; decode_parameter(Name, Value, Acc) -> Acc#{Name => Value}. +encode_parameter(_Name, Value) -> + Value. + decode_tlv(Name, Tag, Value, Acc) -> Acc#{Name => {Tag, Value}}. + +encode_tlv({Tag, Value}) -> + {Tag, Value}. + +parse_ton(2#00000000) -> + unknown; +parse_ton(2#00000001) -> + international; +parse_ton(2#00000010) -> + national; +parse_ton(2#00000011) -> + network_specific; +parse_ton(2#00000100) -> + subscriber_number; +parse_ton(2#00000101) -> + alphanumeric; +parse_ton(2#00000110) -> + abbreviated. + +parse_npi(2#00000000) -> + unknown; +parse_npi(2#00000001) -> + isdn; +parse_npi(2#00000011) -> + data; +parse_npi(2#00000100) -> + telex; +parse_npi(2#00000110) -> + land_mobile; +parse_npi(2#00001000) -> + national; +parse_npi(2#00001001) -> + private; +parse_npi(2#00001010) -> + ermes; +parse_npi(2#00001110) -> + internet; +parse_npi(2#00010010) -> + wap. + +parse_dest_flag(1) -> + sme_address; +parse_dest_flag(2) -> + distribution_list_name. + +parse_message_state(0) -> + scheduled; +parse_message_state(1) -> + enroute; +parse_message_state(2) -> + delivered; +parse_message_state(3) -> + expired; +parse_message_state(4) -> + deleted; +parse_message_state(5) -> + undeliverable; +parse_message_state(6) -> + accepted; +parse_message_state(7) -> + unknown; +parse_message_state(8) -> + rejected; +parse_message_state(9) -> + skipped. + diff --git a/test/otc_smpp_tests.erl b/test/otc_smpp_tests.erl index f83db96..729d315 100644 --- a/test/otc_smpp_tests.erl +++ b/test/otc_smpp_tests.erl @@ -8,7 +8,7 @@ bind_transmitter_test() -> "00000000" "00000001" "61626869" - "6b0070617373776f726400534d50500001000000">>, + "6B0070617373776F726400534D50500001000000">>, Map = #{command => bind_transmitter, command_status => esme_rok, sequence_number => 1, @@ -21,50 +21,60 @@ bind_transmitter_test() -> address_range => <<>> }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). bind_transmitter_resp_test() -> - Bin = <<"00000015800000020000000000000001534d534300">>, + Bin = <<"00000015" + "80000002" + "00000000" + "00000001" + "534D534300">>, Map = #{command => bind_transmitter_resp, command_status => esme_rok, sequence_number => 1, system_id => <<"SMSC">> }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). enquire_link_test() -> - Bin = <<"00000010000000150000000000000002">>, + Bin = <<"00000010" + "00000015" + "00000000" + "00000002">>, Map = #{command => enquire_link, command_status => esme_rok, sequence_number => 2 }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). enquire_link_resp_test() -> - Bin = <<"00000010800000150000000000000002">>, + Bin = <<"00000010" + "80000015" + "00000000" + "00000002">>, Map = #{command => enquire_link_resp, command_status => esme_rok, sequence_number => 2 }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). submit_sm_test() -> - Bin = <<"0000003200000004000000000000000300000131303000010131323334353637383930000000000000000000000474657374">>, + Bin = <<"00000032" + "00000004" + "00000000" + "00000003" + "00000131303000010131323334353637383930000000000000000000000474657374">>, Map = #{command => submit_sm, command_status => esme_rok, sequence_number => 3, @@ -87,47 +97,53 @@ submit_sm_test() -> short_message => <<"test">> }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). submit_sm_resp_test() -> - Bin = <<"00000019800000040000000000000003343763386337376100">>, + Bin = <<"00000019" + "80000004" + "00000000" + "00000003" + "343763386337376100">>, Map = #{command => submit_sm_resp, command_status => esme_rok, sequence_number => 3, message_id => <<"47c8c77a">> }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). unbind_test() -> - Bin = <<"00000010000000060000000000000004">>, + Bin = <<"00000010" + "00000006" + "00000000" + "00000004">>, Map = #{command => unbind, command_status => esme_rok, sequence_number => 4 }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). unbind_resp_test() -> - Bin = <<"00000010800000060000000000000004">>, + Bin = <<"00000010" + "80000006" + "00000000" + "00000004">>, Map = #{command => unbind_resp, command_status => esme_rok, sequence_number => 4 }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), - ?assertMatch(Map, Msg)%% , - %% NewBin = otc_smpp:encode(Map), - %% ?assertMatch(Bin, binary:encode_hex(NewBin)) - . + ?assertMatch(Map, Msg), + NewBin = otc_smpp:encode(Map), + ?assertMatch(Bin, binary:encode_hex(NewBin)). From 4499fa6e49b771cc43b4a90afca6cb0753950b5c Mon Sep 17 00:00:00 2001 From: Sebastian Weddmark Olsson Date: Sun, 22 Jun 2025 18:26:27 +0200 Subject: [PATCH 4/6] all parameters --- src/otc_smpp.erl | 387 +++++++++++++++++++++++++++++++++++++--- test/otc_smpp_tests.erl | 24 +-- 2 files changed, 378 insertions(+), 33 deletions(-) diff --git a/src/otc_smpp.erl b/src/otc_smpp.erl index 171bab9..85dac82 100644 --- a/src/otc_smpp.erl +++ b/src/otc_smpp.erl @@ -2,10 +2,12 @@ -behaviour(otc_codec). -export([spec/0, - codec/1, + codec/2, next/1, decode/1, - encode/1 + decode/2, + encode/1, + encode/2 ]). -include("include/smpp.hrl"). @@ -15,25 +17,31 @@ spec() -> "SMS Forum SMPP V5.0". -codec(Bin) when is_binary(Bin) -> - decode(Bin); -codec(Map) when is_map(Map) -> - encode({Map, <<>>}); -codec({Map, PDU}) -> - encode({Map, PDU}). +codec(Bin, Opts) when is_binary(Bin) -> + decode(Bin, Opts); +codec(Map, Opts) when is_map(Map) -> + encode({Map, <<>>}, Opts); +codec({Map, PDU}, Opts) -> + encode({Map, PDU}, Opts). next(_) -> '$stop'. decode(Bin) -> + decode(Bin, #{}). + +decode(Bin, _Opts) -> {Header, Rest} = parse_header(Bin), Msg = decode_msg(Header, Rest), maps:merge(Header, Msg). -encode({Map, PDU}) -> - M = encode(Map), +encode(Pdu) -> + encode(Pdu, #{}). + +encode({Map, PDU}, Opts) -> + M = encode(Map, Opts), <>; -encode(Map) -> +encode(Map, _Opts) -> Msg = encode_msg(Map), Header = compose_header(Map, Msg), <
>. @@ -470,7 +478,7 @@ decode_msg(#{command := submit_sm}, Bin) -> {esm_class, 1, integer}, {protocol_id, 1, integer}, {priority_flag, 1, integer}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {replace_if_present_flag, 1, integer}, @@ -514,7 +522,7 @@ decode_msg(#{command := submit_multi}, Bin) -> {esm_class, 1, integer}, {protocol_id, 1, integer}, {priority_flag, 1, integer}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {replace_if_present_flag, 1, integer}, @@ -542,7 +550,7 @@ decode_msg(#{command := deliver_sm}, Bin) -> {esm_class, 1, integer}, {protocol_id, 1, integer}, {priority_flag, 1, integer}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {replace_if_present_flag, 1, integer}, @@ -595,7 +603,7 @@ decode_msg(#{command := replace_sm}, Bin) -> {source_addr_ton, 1, integer}, {source_addr_npi, 1, integer}, {source_addr, {0, 65}, cstring}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {sm_default_msg_id, 1, integer}, @@ -720,7 +728,7 @@ encode_msg(#{command := submit_sm} = Map) -> {esm_class, 1, integer}, {protocol_id, 1, integer}, {priority_flag, 1, integer}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {replace_if_present_flag, 1, integer}, @@ -764,7 +772,7 @@ encode_msg(#{command := submit_multi} = Map) -> {esm_class, 1, integer}, {protocol_id, 1, integer}, {priority_flag, 1, integer}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {replace_if_present_flag, 1, integer}, @@ -792,7 +800,7 @@ encode_msg(#{command := deliver_sm} = Map) -> {esm_class, 1, integer}, {protocol_id, 1, integer}, {priority_flag, 1, integer}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {replace_if_present_flag, 1, integer}, @@ -845,7 +853,7 @@ encode_msg(#{command := replace_sm} = Map) -> {source_addr_ton, 1, integer}, {source_addr_npi, 1, integer}, {source_addr, {0, 65}, cstring}, - {schedule_delivery_time, {1,17}, cstring}, + {scheduled_delivery_time, {1,17}, cstring}, {validity_period, {1,17}, cstring}, {registered_delivery, 1, integer}, {sm_default_msg_id, 1, integer}, @@ -988,18 +996,158 @@ decode_parameter(dest_addr_npi, Value, Acc) -> Acc#{dest_addr_npi => parse_npi(Value)}; decode_parameter(esme_addr_npi, Value, Acc) -> Acc#{esme_addr_npi => parse_npi(Value)}; + +decode_parameter(address_range, Value, Acc) -> + Acc#{address_range => Value}; +decode_parameter(data_coding, Value, Acc) -> + Acc#{data_coding => parse_data_coding(Value)}; +decode_parameter(destination_addr, Value, Acc) -> + Acc#{destination_addr => Value}; +decode_parameter(dest_flag, Value, Acc) -> + Acc#{dest_flag => parse_dest_flag(Value)}; +decode_parameter(dl_name, Value, Acc) -> + Acc#{dl_name => Value}; +decode_parameter(esme_addr, Value, Acc) -> + Acc#{esme_addr => Value}; +decode_parameter(esm_class, Value, Acc) -> + <> = <>, + Acc#{esm_class => #{gsm_specific => parse_esm_class_gsm_specific(GS), + message_type => parse_esm_class_message_type(MT), + messaging_mode => parse_esm_class_messaging_mode(MM) + } + }; +decode_parameter(interface_version, Value, Acc) -> + <> = <>, + Acc#{interface_version => {Ma, Mi}}; +decode_parameter(message_id, Value, Acc) -> + Acc#{message_id => Value}; decode_parameter(message_state, Value, Acc) -> Acc#{message_state => parse_message_state(Value)}; +decode_parameter(no_unsuccess, Value, Acc) -> + Acc#{no_unsuccess => Value}; +decode_parameter(number_of_dests, Value, Acc) -> + Acc#{number_of_dests => Value}; +decode_parameter(password, Value, Acc) -> + Acc#{password => Value}; +decode_parameter(priority_flag, Value, Acc) -> + Acc#{priority_flag => Value}; +decode_parameter(protocol_id, Value, Acc) -> + Acc#{protocol_id => Value}; +decode_parameter(registered_delivery, Value, Acc) -> + Acc#{registered_delivery => Value}; decode_parameter(replace_if_present_flag, Value, Acc) -> Acc#{replace_if_present_flag => case Value of 0 -> false; 1 -> true end }; -decode_parameter(Name, Value, Acc) -> - Acc#{Name => Value}. +decode_parameter(scheduled_delivery_time, Value, Acc) -> + Acc#{scheduled_delivery_time => Value}; +decode_parameter(validity_period, Value, Acc) -> + Acc#{validity_period => Value}; +decode_parameter(final_date, Value, Acc) -> + Acc#{final_date => Value}; +decode_parameter(sequence_number, Value, Acc) -> + Acc#{sequence_number => Value}; +decode_parameter(service_type, Value, Acc) -> + Acc#{service_type => Value}; +decode_parameter(short_message, Value, Acc) -> + Acc#{short_message => Value}; +decode_parameter(sm_default_msg_id, Value, Acc) -> + Acc#{sm_default_msg_id => Value}; +decode_parameter(sm_length, Value, Acc) -> + Acc#{sm_length => Value}; +decode_parameter(source_addr, Value, Acc) -> + Acc#{source_addr => Value}; +decode_parameter(system_id, Value, Acc) -> + Acc#{system_id => Value}; +decode_parameter(system_type, Value, Acc) -> + Acc#{system_type => Value}. -encode_parameter(_Name, Value) -> +encode_parameter(addr_ton, Value) -> + compose_ton(Value); +encode_parameter(source_addr_ton, Value) -> + compose_ton(Value); +encode_parameter(dest_addr_ton, Value) -> + compose_ton(Value); +encode_parameter(esme_addr_ton, Value) -> + compose_ton(Value); +encode_parameter(addr_npi, Value) -> + compose_npi(Value); +encode_parameter(source_addr_npi, Value) -> + compose_npi(Value); +encode_parameter(dest_addr_npi, Value) -> + compose_npi(Value); +encode_parameter(esme_addr_npi, Value) -> + compose_npi(Value); +encode_parameter(address_range, Value) -> + Value; +encode_parameter(data_coding, Value) -> + compose_data_coding(Value); +encode_parameter(destination_addr, Value) -> + Value; +encode_parameter(dest_flag, Value) -> + compose_dest_flag(Value); +encode_parameter(dl_name, Value) -> + Value; +encode_parameter(esme_addr, Value) -> + Value; +encode_parameter(esm_class, Value) -> + #{gsm_specific := GsmSpec, + message_type := MessageType, + messaging_mode := MessagingMode + } = Value, + GS = compose_esm_class_gsm_specific(GsmSpec), + MT = compose_esm_class_message_type(MessageType), + MM = compose_esm_class_messaging_mode(MessagingMode), + <> = <>, + EsmValue; +encode_parameter(interface_version, Value) -> + {Ma, Mi} = Value, + <> = <>, + Version; +encode_parameter(message_id, Value) -> + Value; +encode_parameter(message_state, Value) -> + compose_message_state(Value); +encode_parameter(no_unsuccess, Value) -> + Value; +encode_parameter(number_of_dests, Value) -> + Value; +encode_parameter(password, Value) -> + Value; +encode_parameter(priority_flag, Value) -> + Value; +encode_parameter(protocol_id, Value) -> + Value; +encode_parameter(registered_delivery, Value) -> + Value; +encode_parameter(replace_if_present_flag, Value) -> + case Value of + false -> 0; + true -> 1 + end; +encode_parameter(scheduled_delivery_time, Value) -> + Value; +encode_parameter(validity_period, Value) -> + Value; +encode_parameter(final_date, Value) -> + Value; +encode_parameter(sequence_number, Value) -> + Value; +encode_parameter(service_type, Value) -> + Value; +encode_parameter(short_message, Value) -> + Value; +encode_parameter(sm_default_msg_id, Value) -> + Value; +encode_parameter(sm_length, Value) -> + Value; +encode_parameter(source_addr, Value) -> + Value; +encode_parameter(system_id, Value) -> + Value; +encode_parameter(system_type, Value) -> Value. decode_tlv(Name, Tag, Value, Acc) -> @@ -1023,6 +1171,21 @@ parse_ton(2#00000101) -> parse_ton(2#00000110) -> abbreviated. +compose_ton(unknown) -> + 2#00000000; +compose_ton(international) -> + 2#00000001; +compose_ton(national) -> + 2#00000010; +compose_ton(network_specific) -> + 2#00000011; +compose_ton(subscriber_number) -> + 2#00000100; +compose_ton(alphanumeric) -> + 2#00000101; +compose_ton(abbreviated) -> + 2#00000110. + parse_npi(2#00000000) -> unknown; parse_npi(2#00000001) -> @@ -1044,11 +1207,37 @@ parse_npi(2#00001110) -> parse_npi(2#00010010) -> wap. +compose_npi(unknown) -> + 2#00000000; +compose_npi(isdn) -> + 2#00000001; +compose_npi(data) -> + 2#00000011; +compose_npi(telex) -> + 2#00000100; +compose_npi(land_mobile) -> + 2#00000110; +compose_npi(national) -> + 2#00001000; +compose_npi(private) -> + 2#00001001; +compose_npi(ermes) -> + 2#00001010; +compose_npi(internet) -> + 2#00001110; +compose_npi(wap) -> + 2#00010010. + parse_dest_flag(1) -> sme_address; parse_dest_flag(2) -> distribution_list_name. +compose_dest_flag(sme_address) -> + 1; +compose_dest_flag(distribution_list_name) -> + 2. + parse_message_state(0) -> scheduled; parse_message_state(1) -> @@ -1070,3 +1259,157 @@ parse_message_state(8) -> parse_message_state(9) -> skipped. +compose_message_state(scheduled) -> + 0; +compose_message_state(enroute) -> + 1; +compose_message_state(delivered) -> + 2; +compose_message_state(expired) -> + 3; +compose_message_state(deleted) -> + 4; +compose_message_state(undeliverable) -> + 5; +compose_message_state(accepted) -> + 6; +compose_message_state(unknown) -> + 7; +compose_message_state(rejected) -> + 8; +compose_message_state(skipped) -> + 9. + +parse_data_coding(2#0000_0000) -> + mc_specific; +parse_data_coding(2#0000_0001) -> + ia5; +parse_data_coding(2#0000_0010 = C) -> + {unspecified, C}; +parse_data_coding(2#0000_0011) -> + latin1; +parse_data_coding(2#0000_0100 = C) -> + {unspecified, C}; +parse_data_coding(2#0000_0101) -> + jis; +parse_data_coding(2#0000_0110) -> + cyrillic; +parse_data_coding(2#0000_0111) -> + latin_hebrew; +parse_data_coding(2#0000_1000) -> + ucs2; +parse_data_coding(2#0000_1001) -> + pictogram; +parse_data_coding(2#0000_1010) -> + music; +parse_data_coding(2#0000_1011 = C) -> + {reserved, C}; +parse_data_coding(2#0000_1100 = C) -> + {reserved, C}; +parse_data_coding(2#0000_1101) -> + extended_kanji_jis; +parse_data_coding(2#0000_1110) -> + ks_c_5601; +parse_data_coding(C) when C >= 2#0000_1111; C =<2#1011_1111 -> + {reserved, C}; +parse_data_coding(C) when C >= 2#1100_0000; C =< 2#1100_1111 -> + {gsm_mwi_control, C}; +parse_data_coding(C) when C >= 2#1101_0000; C =< 2#1101_1111 -> + {gsm_mwi_control, C}; +parse_data_coding(C) when C >= 2#1110_0000; C =< 2#1110_1111 -> + {reserved, C}; +parse_data_coding(C) when C >= 2#1111_0000; C =< 2#1111_1111 -> + {gsm_message_class_control, C}. + +compose_data_coding(mc_specific) -> + 2#0000_0000; +compose_data_coding(ia5) -> + 2#0000_0001; +compose_data_coding({unspecified, C}) -> + C; +compose_data_coding(latin1) -> + 2#0000_0011; +compose_data_coding(jis) -> + 2#0000_0101; +compose_data_coding(cyrillic) -> + 2#0000_0110; +compose_data_coding(latin_hebrew) -> + 2#0000_0111; +compose_data_coding(ucs2) -> + 2#0000_1000; +compose_data_coding(pictogram) -> + 2#0000_1001; +compose_data_coding(music) -> + 2#0000_1010; +compose_data_coding({reserved, C}) -> + C; +compose_data_coding(extended_kanji_jis) -> + 2#0000_1101; +compose_data_coding(ks_c_5601) -> + 2#0000_1110; +compose_data_coding({gsm_mwi_control, C}) -> + C; +compose_data_coding({gsm_message_class_control, C}) -> + C. + +parse_esm_class_gsm_specific(2#00) -> + default; +parse_esm_class_gsm_specific(2#01) -> + datagram; +parse_esm_class_gsm_specific(2#10) -> + forward; +parse_esm_class_gsm_specific(2#11) -> + store_forward. + +compose_esm_class_gsm_specific(default) -> + 2#00; +compose_esm_class_gsm_specific(datagram) -> + 2#01; +compose_esm_class_gsm_specific(forward) -> + 2#10; +compose_esm_class_gsm_specific(store_forward) -> + 2#11. + +parse_esm_class_message_type(2#0000) -> + default; +parse_esm_class_message_type(2#0001) -> + mc_delivery_receipt; +parse_esm_class_message_type(2#1000) -> + intermediate_delivery_notification; +parse_esm_class_message_type(2#0010) -> + delivery_acknowledgement; +parse_esm_class_message_type(2#0100) -> + user_acknowledgement; +parse_esm_class_message_type(2#0110) -> + conversation_abort. + +compose_esm_class_message_type(default) -> + 2#0000; +compose_esm_class_message_type(mc_delivery_receipt) -> + 2#0001; +compose_esm_class_message_type(intermediate_delivery_notification) -> + 2#1000; +compose_esm_class_message_type(delivery_acknowledgement) -> + 2#0010; +compose_esm_class_message_type(user_acknowledgement) -> + 2#0100; +compose_esm_class_message_type(conversation_abort) -> + 2#0110. + +parse_esm_class_messaging_mode(2#00) -> + no_features; +parse_esm_class_messaging_mode(2#01) -> + udhi; +parse_esm_class_messaging_mode(2#10) -> + reply_path; +parse_esm_class_messaging_mode(2#11) -> + udhi_reply_path. + +compose_esm_class_messaging_mode(no_features) -> + 2#00; +compose_esm_class_messaging_mode(udhi) -> + 2#01; +compose_esm_class_messaging_mode(reply_path) -> + 2#10; +compose_esm_class_messaging_mode(udhi_reply_path) -> + 2#11. diff --git a/test/otc_smpp_tests.erl b/test/otc_smpp_tests.erl index 729d315..23e583b 100644 --- a/test/otc_smpp_tests.erl +++ b/test/otc_smpp_tests.erl @@ -15,9 +15,9 @@ bind_transmitter_test() -> system_id => <<"abhik">>, password => <<"password">>, system_type => <<"SMPP">>, - interface_version => 1, - addr_ton => 0, - addr_npi => 0, + interface_version => {0, 1}, + addr_ton => unknown, + addr_npi => unknown, address_range => <<>> }, Msg = otc_smpp:decode(binary:decode_hex(Bin)), @@ -79,20 +79,22 @@ submit_sm_test() -> command_status => esme_rok, sequence_number => 3, service_type => <<>>, - source_addr_ton => 0, - source_addr_npi => 1, + source_addr_ton => unknown, + source_addr_npi => isdn, source_addr => <<"100">>, - dest_addr_ton => 1, - dest_addr_npi => 1, + dest_addr_ton => international, + dest_addr_npi => isdn, destination_addr => <<"1234567890">>, - esm_class => 0, + esm_class => #{gsm_specific => default, + message_type => default, + messaging_mode => no_features}, protocol_id => 0, priority_flag => 0, - schedule_delivery_time => <<>>, + scheduled_delivery_time => <<>>, validity_period => <<>>, registered_delivery => 0, - replace_if_present_flag => 0, - data_coding => 0, + replace_if_present_flag => false, + data_coding => mc_specific, sm_default_msg_id => 0, short_message => <<"test">> }, From 123d3b40131b69db58c75203a42fda2f69bcb3b3 Mon Sep 17 00:00:00 2001 From: Sebastian Weddmark Olsson Date: Fri, 29 Aug 2025 10:33:39 +0200 Subject: [PATCH 5/6] smpp documentation --- doc/smpp.org | 1278 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1278 insertions(+) create mode 100644 doc/smpp.org diff --git a/doc/smpp.org b/doc/smpp.org new file mode 100644 index 0000000..512f196 --- /dev/null +++ b/doc/smpp.org @@ -0,0 +1,1278 @@ +* Overview + + SMS Forum SMPP V5.0 + Short Message Peer-to-Peer Protocol Specification + +* 3.2 General PDU Format + +** 3.2.1 PDU Format + +#+BEGIN_COMMENT +| SMPP PDU Field | Size (Octets) | Type | Description | +|---------------------+---------------+---------+-----------------------------------------------------------------------| +| command_length | 4 | Integer | Overall size of PDU including header and body | +| command_id | 4 | Integer | Identifies the PDU | +| command_status | 4 | Integer | Used to carry a SMPP error code | +| sequence_number | 4 | Integer | Used to uniquely identify a SMPP PDU in the context of a SMPP session | +| Standard Parameters | var. | mixed | The Body part of a PDU differs from PDU to | +| TLV Parameters | var. | mixed | PDU and in some cases, there is no body at all | +#+END_COMMENT + +* 4 SMPP PDU Definitions + +** 4.1 Session Management Operations + +*** 4.1.1.1 bind_transmitter Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| system_id | 0-16 | C-Octet String | M | 4.7.30 | +| password | 0-9 | C-Octet String | M | 4.7.18 | +| system_type | 0-13 | C-Octet String | M | 4.7.31 | +| interface_version | 1 | Integer | M | 4.7.13 | +| addr_ton | 1 | Integer | M | 4.7.1 | +| addr_npi | 1 | Integer | M | 4.7.2 | +| address_range | 0-41 | C-Octet String | M | 4.7.3 | + +*** 4.1.1.2 bind_transmitter_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| system_id | 0-16 | C-Octet String | M | 4.7.30 | +| sc_interface_version | - | TLV | O | 4.8.4.51 | + +*** 4.1.1.3 bind_receiver Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| system_id | 0-16 | C-Octet String | M | 4.7.30 | +| password | 0-9 | C-Octet String | M | 4.7.18 | +| system_type | 0-13 | C-Octet String | M | 4.7.31 | +| interface_version | 1 | Integer | M | 4.7.13 | +| addr_ton | 1 | Integer | M | 4.7.1 | +| addr_npi | 1 | Integer | M | 4.7.2 | +| address_range | 0-41 | C-Octet String | M | 4.7.3 | + +*** 4.1.1.4 bind_receiver_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| system_id | 0-16 | C-Octet String | M | 4.7.30 | +| sc_interface_version | - | TLV | O | 4.8.4.51 | + +*** 4.1.1.5 bind_transceiver Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| system_id | 0-16 | C-Octet String | M | 4.7.30 | +| password | 0-9 | C-Octet String | M | 4.7.18 | +| system_type | 0-13 | C-Octet String | M | 4.7.31 | +| interface_version | 1 | Integer | M | 4.7.13 | +| addr_ton | 1 | Integer | M | 4.7.1 | +| addr_npi | 1 | Integer | M | 4.7.2 | +| address_range | 0-41 | C-Octet String | M | 4.7.3 | + +*** 4.1.1.6 bind_transceiver_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| system_id | 0-16 | C-Octet String | M | 4.7.30 | +| sc_interface_version | - | TLV | O | 4.8.4.51 | + +*** 4.1.1.7 outbind Syntax. + +| Field Name | Size octets | Type | M/O | Ref. | +|-----------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| system_id | 0-16 | C-Octet String | M | 4.7.30 | +| password | 0-9 | C-Octet String | M | 4.7.18 | + +*** 4.1.1.8 unbind Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +*** 4.1.1.9 unbind_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +*** 4.1.2.1 enquire_link Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +*** 4.1.2.2 enquire_link_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +*** 4.1.3.1 alert_notification Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| esme_addr_ton | 1 | Integer | M | 4.7.1 | +| esme_addr_npi | 1 | Integer | M | 4.7.2 | +| esme_addr | 0-65 | C-Octet String | M | 4.7.11 | +| ms_availability_status | - | TLV | O | 4.8.4.39 | + +*** 4.1.4.1 generic_nack Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +** 4.2 Message Submission Operations + +*** 4.2.1.1 submit_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| service_type | 0-6 | C-Octet String | M | 4.7.25 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| dest_addr_ton | 1 | Integer | M | 4.7.1 | +| dest_addr_npi | 1 | Integer | M | 4.7.2 | +| destination_addr | 0-21 | C-Octet String | M | 4.7.8 | +| esm_class | 1 | Integer | M | 4.7.12 | +| protocol_id | 1 | Integer | M | 4.7.20 | +| priority_flag | 1 | Integer | M | 4.7.19 | +| schedule_delivery_time | 1,17 | C-Octet String | M | 4.7.23.1 | +| validity_period | 1,17 | C-Octet String | M | 4.7.23.2 | +| registered_delivery | 1 | Integer | M | 4.7.21 | +| replace_if_present_flag | 1 | Integer | M | 4.7.22 | +| data_coding | 1 | Integer | M | 4.7.7 | +| sm_default_msg_id | 1 | Integer | M | 4.7.27 | +| sm_length | 1 | Integer | M | 4.7.28 | +| short_message | 0-255 | Octet String | M | 4.7.27 | +| Message Submission TLVs | - | TLV | | 4.2.4 | + +*** 4.2.1.2 submit_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| Message Submission Response TLVs | - | TLV | | 4.2.5 | + +*** 4.2.2.1 data_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| service_type | 0-6 | C-Octet String | M | 4.7.25 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| dest_addr_ton | 1 | Integer | M | 4.7.1 | +| dest_addr_npi | 1 | Integer | M | 4.7.2 | +| destination_addr | 0-21 | C-Octet String | M | 4.7.8 | +| esm_class | 1 | Integer | M | 4.7.12 | +| registered_delivery | 1 | Integer | M | 4.7.21 | +| data_coding | 1 | Integer | M | 4.7.7 | +| Message Submission TLVs | - | TLV | | 4.2.4 | + +*** 4.2.2.2 data_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| Message Submission Response TLVs | - | TLV | | 4.2.5 | + +*** 4.2.3.1 submit_multi Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------------+-------------+-----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| service_type | 0-6 | C-Octet String | M | 4.7.25 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| number_of_dests | 1 | Integer | M | 4.7.17 | +| dest_address | 0-24 | Composite Field | | | +| esm_class | 1 | Integer | | 4.7.12 | +| protocol_id | 1 | Integer | | 4.7.20 | +| priority_flag | 1 | Integer | | 4.7.19 | +| schedule_delivery_time | 1,17 | C-Octet String | | 4.7.23.1 | +| validity_period | 1,17 | C-Octet String | M | 4.7.23.2 | +| registered_delivery | 1 | Integer | M | 4.7.21 | +| replace_if_present_flag | 1 | Integer | M | 4.7.22 | +| data_coding | 1 | Integer | M | 4.7.7 | +| sm_default_msg_id | 1 | Integer | M | 4.7.27 | +| sm_length | 1 | Integer | M | 4.7.28 | +| short_message | 0-255 | Octet String | M | 4.7.27 | +| Message Submission TLVs | - | TLV | | 4.2.4 | + +*** 4.2.3.2 submit_multi_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------------------+-------------+-----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| no_unsuccess | 1 | Integer | | 4.7.16 | +| unsuccess_sme | 0-27 | Composite Field | | | +| Message Submission Response TLVs | - | TLV | | 4.2.5 | + +** 4.3 Message Delivery Operations + +*** 4.3.1.1 deliver_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| service_type | 0-6 | C-Octet String | M | 4.7.25 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| dest_addr_ton | 1 | Integer | M | 4.7.1 | +| dest_addr_npi | 1 | Integer | M | 4.7.2 | +| destination_addr | 0-21 | C-Octet String | M | 4.7.8 | +| esm_class | 1 | Integer | M | 4.7.12 | +| protocol_id | 1 | Integer | | 4.7.20 | +| priority_flag | 1 | Integer | | 4.7.19 | +| schedule_delivery_time | 1,17 | C-Octet String | | 4.7.23.1 | +| validity_period | 1,17 | C-Octet String | M | 4.7.23.2 | +| registered_delivery | 1 | Integer | M | 4.7.21 | +| replace_if_present_flag | 1 | Integer | M | 4.7.22 | +| data_coding | 1 | Integer | M | 4.7.7 | +| sm_default_msg_id | 1 | Integer | M | 4.7.27 | +| sm_length | 1 | Integer | M | 4.7.28 | +| short_message | 0-255 | Octet String | M | 4.7.27 | +| Message Delivery Request TLVs | - | TLV | | 4.3.3 | + +*** 4.3.1.2 deliver_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|--------------------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| Message Delivery Response TLVs | - | TLV | | 4.3.4 | + +*** 4.3.2 data_sm Operation + +See 4.2.2 + +** 4.4 Message Broadcast Operations + +*** 4.4.1.1 broadcast_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|---------------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| service_type | 0-6 | C-Octet String | M | 4.7.25 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| priority_flag | 1 | Integer | | 4.7.19 | +| schedule_delivery_time | 1,17 | C-Octet String | | 4.7.23.1 | +| validity_period | 1,17 | C-Octet String | M | 4.7.23.2 | +| replace_if_present_flag | 1 | Integer | M | 4.7.22 | +| data_coding | 1 | Integer | M | 4.7.7 | +| sm_default_msg_id | 1 | Integer | M | 4.7.27 | +| broadcast_area_identifier | - | TLV | | 4.8.4.4 | +| broadcast_content_type | - | TLV | | 4.8.4.8 | +| broadcast_rep_num | - | TLV | | 4.8.4.13 | +| broadcast_frequency_interval | - | TLV | | 4.8.4.11 | +| Broadcast Request Optional TLVs | - | TLV | | 4.4.2 | + +*** 4.4.1.2 broadcast_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| Broadcast Response Optional TLVs | - | TLV | | 4.4.3 | + +** 4.5 Ancillary Submission Operations + +*** 4.5.1.1 cancel_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|------------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| service_type | 0-6 | C-Octet String | M | 4.7.25 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| dest_addr_ton | 1 | Integer | M | 4.7.1 | +| dest_addr_npi | 1 | Integer | M | 4.7.2 | +| destination_addr | 0-21 | C-Octet String | M | 4.7.8 | + +*** 4.5.1.2 cancel_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +*** 4.5.2.1 query_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-----------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | + +*** 4.5.2.2 query_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-----------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| final_date | 1,17 | C-Octet String | | 4.7.23.3 | +| message_state | 1 | Integer | | 4.7.15 | +| error_code | 1 | Integer | | | + +*** 4.5.3.1 replace_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|----------------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| schedule_delivery_time | 1,17 | C-Octet String | | 4.7.23.1 | +| validity_period | 1,17 | C-Octet String | M | 4.7.23.2 | +| registered_delivery | 1 | Integer | M | 4.7.21 | +| sm_default_msg_id | 1 | Integer | M | 4.7.27 | +| sm_length | 1 | Integer | M | 4.7.28 | +| short_message | 0-255 | Octet String | M | 4.7.27 | +| Message Replacement Request TLVs | - | TLV | | 4.5.3.3 | + +*** 4.5.3.2 replace_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +** 4.6 Ancillary Broadcast Operations + +*** 4.6.1.1 query_broadcast_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|------------------------------+-------------+----------------+-----+---------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | +| Query Broadcast Request TLVs | - | TLV | | 4.6.1.2 | + +*** 4.6.1.3 query_broadcast_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-------------------------------+-------------+----------------+-----+---------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| message_state | 1 | Integer | | 4.7.15 | +| broadcast_area_identifier | - | TLV | | 4.8.4.4 | +| broadcast_area_success | - | TLV | | 4.8.4.5 | +| Query Broadcast Response TLVs | - | TLV | | 4.6.1.4 | + +*** 4.6.2.1 cancel_broadcast_sm Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|-----------------+-------------+----------------+-----+--------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | +| service_type | 0-6 | C-Octet String | M | 4.7.25 | +| message_id | 0-65 | C-Octet String | M | 4.7.14 | +| source_addr_ton | 1 | Integer | M | 4.7.1 | +| source_addr_npi | 1 | Integer | M | 4.7.2 | +| source_addr | 0-65 | C-Octet String | M | 4.7.29 | + +*** 4.6.2.3 cancel_broadcast_sm_resp Syntax + +| Field Name | Size octets | Type | M/O | Ref. | +|------------------------+-------------+----------------+-----+----------| +| command_length | 4 | Integer | M | 4.7.4 | +| command_id | 4 | Integer | M | 4.7.5 | +| command_status | 4 | Integer | M | 4.7.6 | +| sequence_number | 4 | Integer | M | 4.7.24 | + +* 4.7 PDU Field Definitions + +** 4.7.1 addr_ton, source_addr_ton, dest_addr_ton, esme_addr_ton + +| TON | Value | +|-------------------+----------| +| Unknown | 00000000 | +| International | 00000001 | +| National | 00000010 | +| Network Specific | 00000011 | +| Subscriber Number | 00000100 | +| Alphanumeric | 00000101 | +| Abbreviated | 00000110 | + +** 4.7.2 addr_npi, source_addr_npi, dest_addr_npi, esme_addr_npi + +| NPI | Value | +|--------------------------------------------+----------| +| Unknown | 00000000 | +| ISDN (E163/E164) | 00000001 | +| Data (X.121) | 00000011 | +| Telex (F.69) | 00000100 | +| Land Mobile (E.212) | 00000110 | +| National | 00001000 | +| Private | 00001001 | +| ERMES | 00001010 | +| Internet (IP) | 00001110 | +| WAP Client Id (to be defined by WAP Forum) | 00010010 | + +** 4.7.3 address_range + +** 4.7.4 command_length + +** 4.7.5 command_id + +| Command ID | Value | +|--------------------------+-------------------------| +| bind_receiver | 0x00000001 | +| bind_transmitter | 0x00000002 | +| query_sm | 0x00000003 | +| submit_sm | 0x00000004 | +| deliver_sm | 0x00000005 | +| unbind | 0x00000006 | +| replace_sm | 0x00000007 | +| cancel_sm | 0x00000008 | +| bind_transceiver | 0x00000009 | +| outbind | 0x0000000B | +| enquire_link | 0x00000015 | +| submit_multi | 0x00000021 | +| alert_notification | 0x00000102 | +| data_sm | 0x00000103 | +| broadcast_sm | 0x00000111 | +| query_broadcast_sm | 0x00000112 | +| cancel_broadcast_sm | 0x00000113 | +| generic_nack | 0x80000000 | +| bind_receiver_resp | 0x80000001 | +| bind_transmitter_resp | 0x80000002 | +| query_sm_resp | 0x80000003 | +| submit_sm_resp | 0x80000004 | +| deliver_sm_resp | 0x80000005 | +| unbind_resp | 0x80000006 | +| replace_sm_resp | 0x80000007 | +| cancel_sm_resp | 0x80000008 | +| bind_transceiver_resp | 0x80000009 | +| enquire_link_resp | 0x80000015 | +| submit_multi_resp | 0x80000021 | +| data_sm_resp | 0x80000103 | +| broadcast_sm_resp | 0x80000111 | +| query_broadcast_sm_resp | 0x80000112 | +| cancel_broadcast_sm_resp | 0x80000113 | +| Reserved for MC Vendor | 0x00010200 - 0x000102FF | +| Reserved for MC Vendor | 0x80010200 - 0x800102FF | + +** 4.7.6 command_status, error_status_code + +| Command Status Name | Value | +|----------------------------------------+-----------------------| +| 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_RINVTLVSTREAM | 0x000000C0 | +| ESME_RTLVNOTALLWD | 0x000000C1 | +| ESME_RINVTLVLEN | 0x000000C2 | +| ESME_RMISSINGTLV | 0x000000C3 | +| ESME_RINVTLVVAL | 0x000000C4 | +| ESME_RDELIVERYFAILURE | 0x000000FE | +| ESME_RUNKNOWNERR | 0x000000FF | +| ESME_RSERTYPUNAUTH | 0x00000100 | +| ESME_RPROHIBITED | 0x00000101 | +| ESME_RSERTYPUNAVAIL | 0x00000102 | +| ESME_RSERTYPDENIED | 0x00000103 | +| ESME_RINVDCS | 0x00000104 | +| ESME_RINVSRCADDRSUBUNIT | 0x00000105 | +| ESME_RINVDSTADDRSUBUNIT | 0x00000106 | +| ESME_RINVBCASTFREQINT | 0x00000107 | +| ESME_RINVBCASTALIAS_NAME | 0x00000108 | +| ESME_RINVBCASTAREAFMT | 0x00000109 | +| ESME_RINVNUMBCAST_AREAS | 0x0000010A | +| ESME_RINVBCASTCNTTYPE | 0x0000010B | +| ESME_RINVBCASTMSGCLASS | 0x0000010C | +| ESME_RBCASTFAIL | 0x0000010D | +| ESME_RBCASTQUERYFAIL | 0x0000010E | +| ESME_RBCASTCANCELFAIL | 0x0000010F | +| ESME_RINVBCAST_REP | 0x00000110 | +| ESME_RINVBCASTSRVGRP | 0x00000111 | +| ESME_RINVBCASTCHANIND | 0x00000112 | +| Reserved for MC vendor specific errors | 0x00000400-0x000004FF | + +** 4.7.7 data_coding + +** 4.7.8 destination_addr + +** 4.7.9 dest_flag + +| dest_flag Value | Meaning | +|-----------------+------------------------| +| 0x01 | SME Address | +| 0x02 | Distribution List Name | + +** 4.7.10 dl_name + +** 4.7.11 esme_addr + +** 4.7.12 esm_class + +** 4.7.13 interface_version + +** 4.7.14 message_id + +** 4.7.15 message_state + +| Message State | Value | Type | +|---------------+-------+--------------| +| SCHEDULED | 0 | Intermediate | +| ENROUTE | 1 | Intermediate | +| DELIVERED | 2 | Final | +| EXPIRED | 3 | Final | +| DELETED | 4 | Final | +| UNDELIVERABLE | 5 | Final | +| ACCEPTED | 6 | Final | +| UNKNOWN | 7 | N/A | +| REJECTED | 8 | Final | +| SKIPPED | 9 | Final | + +** 4.7.16 no_unsuccess + +** 4.7.17 number_of_dests + +** 4.7.18 password + +** 4.7.19 priority_flag + +** 4.7.20 protocol_id + +** 4.7.21 registered_delivery + +** 4.7.22 replace_if_present_flag + +| Value | Meaning | +|---------+-------------------------| +| 0 | Don’t replace (default) | +| 1 | Replace | +| 2 - 255 | Reserved | + +** 4.7.23 scheduled_delivery_time, validity_period, final_date + +** 4.7.24 sequence_number + +** 4.7.25 service_type + +** 4.7.26 short_message + +** 4.7.27 sm_default_msg_id + +** 4.7.28 sm_length + +** 4.7.29 source_addr + +** 4.7.30 system_id + +** 4.7.31 system_type + +* 4.8 PDU TLV Definitions + +| Tag | Value | Wireless Network Technology | +|------------------------------+--------+-----------------------------| +| dest_addr_subunit | 0x0005 | GSM | +| dest_network_type | 0x0006 | Generic | +| dest_bearer_type | 0x0007 | Generic | +| dest_telematics_id | 0x0008 | GSM | +| source_addr_subunit | 0x000D | GSM | +| source_network_type | 0x000E | Generic | +| source_bearer_type | 0x000F | Generic | +| source_telematics_id | 0x0010 | GSM | +| qos_time_to_live | 0x0017 | Generic | +| payload_type | 0x0019 | Generic | +| additional_status_info_text | 0x001D | Generic | +| receipted_message_id | 0x001E | Generic | +| ms_msg_wait_facilities | 0x0030 | GSM | +| privacy_indicator | 0x0201 | CDMA, TDMA | +| source_subaddress | 0x0202 | CDMA, TDMA | +| dest_subaddress | 0x0203 | CDMA, TDMA | +| user_message_reference | 0x0204 | Generic | +| user_response_code | 0x0205 | CDMA, TDMA | +| source_port | 0x020A | Generic | +| dest_port | 0x020B | Generic | +| sar_msg_ref_num | 0x020C | Generic | +| language_indicator | 0x020D | CDMA, TDMA | +| sar_total_segments | 0x020E | Generic | +| sar_segment_seqnum | 0x020F | Generic | +| sc_interface_version | 0x0210 | Generic | +| callback_num_pres_ind | 0x0302 | TDMA | +| callback_num_atag | 0x0303 | TDMA | +| number_of_messages | 0x0304 | CDMA | +| callback_num | 0x0381 | CDMA, TDMA, GSM, iDEN | +| dpf_result | 0x0420 | Generic | +| set_dpf | 0x0421 | Generic | +| ms_availability_status | 0x0422 | Generic | +| network_error_code | 0x0423 | Generic | +| message_payload | 0x0424 | Generic | +| delivery_failure_reason | 0x0425 | Generic | +| more_messages_to_send | 0x0426 | GSM | +| message_state | 0x0427 | Generic | +| congestion_state | 0x0428 | Generic | +| ussd_service_op | 0x0501 | GSM (USSD) | +| broadcast_channel_indicator | 0x0600 | GSM | +| broadcast_content_type | 0x0601 | CDMA, TDMA, GSM | +| broadcast_content_type_info | 0x0602 | CDMA, TDMA | +| broadcast_message_class | 0x0603 | GSM | +| broadcast_rep_num | 0x0604 | GSM | +| broadcast_frequency_interval | 0x0605 | CDMA, TDMA, GSM | +| broadcast_area_identifier | 0x0606 | CDMA, TDMA, GSM | +| broadcast_error_status | 0x0607 | CDMA, TDMA, GSM | +| broadcast_area_success | 0x0608 | GSM | +| broadcast_end_time | 0x0609 | CDMA, TDMA, GSM | +| broadcast_service_group | 0x060A | CDMA, TDMA | +| billing_identification | 0x060B | Generic | +| source_network_id | 0x060D | Generic | +| dest_network_id | 0x060E | Generic | +| source_node_id | 0x060F | Generic | +| dest_node_id | 0x0610 | Generic | +| dest_addr_np_resolution | 0x0611 | CDMA, TDMA (US Only) | +| dest_addr_np_information | 0x0612 | CDMA, TDMA (US Only) | +| dest_addr_np_country | 0x0613 | CDMA, TDMA (US Only) | +| display_time | 0x1201 | CDMA, TDMA | +| sms_signal | 0x1203 | TDMA | +| ms_validity | 0x1204 | CDMA, TDMA | +| alert_on_message_delivery | 0x130C | CDMA | +| its_reply_type | 0x1380 | CDMA | +| its_session_info | 0x1383 | CDMA | + +** 4.8.4.1 additional_status_info_text + +** 4.8.4.2 alert_on_message_delivery + +0 = Use mobile default alert (Default) +1 = Use low-priority alert +2 = Use medium-priority alert +3 = Use high-priority alert + +** 4.8.4.3 billing_identification + +** 4.8.4.4 broadcast_area_identifier, failed_broadcast_area_identifier + +Octet 1 is used to specify the area format. Ref. 4.8.4.4.1 +The remaining data is used to specify the broadcast area details. + +*** 4.8.4.4.1 Broadcast Area Format types + +| Format | Format Value | Size Octets | Value Type | Description | Technology | +|---------------+--------------+--------------+--------------+------------------------------------------------------------------------------------------------------------+------------| +| alias/name | 0x00 | Var. Max.100 | Octet String | This field allows specification of an area by name. | Generic | +| ellipsoid_arc | 0x01 | Var. Max.100 | Octet String | This field allows specification of an area as an ellipsoid arc. Ref. [3GPP TS 23.032] Sections: 5.7, 7.3.7 | GSM | +| polygon | 0x02 | Var. Max.100 | Octet String | This field allows specification of an area as a polygon. Ref. [3GPP TS 23.032] Sections: 5.4, 7.3.4 | GSM | + +** 4.8.4.5 broadcast_area_success + +0-100 = allowed range +255 = Information not available + +** 4.8.4.6 broadcast_content_type_info + +** 4.8.4.7 broadcast_channel_indicator + +0 = Basic Broadcast Channel (Default) +1 = Extended Broadcast Channel + +** 4.8.4.8 broadcast_content_type + +The first octet is a Type of Network tag indicating the network type. +Valid Tag values are: +0 = Generic +1 = GSM [23041] +2 = TDMA [IS824][ANSI-41] +3 = CDMA [IS824][IS637] + +Octets 2-3 contain the broadcast content type. The +following values apply when this is set to 0 (Generic): + +Encoding Content Type + +| | System Services | +| 0x0000 | Index | +| 0x0001 | Emergency Broadcasts | +| 0x0002 | IRDB Download | +| | News Services | +| 0x0010 | News Flashes | +| 0x0011 | General News (Local) | +| 0x0012 | General News (Regional) | +| 0x0013 | General News (National) | +| 0x0014 | General News (International) | +| 0x0015 | Business/Financial News (Local) | +| 0x0016 | Business/Financial News (Regional) | +| 0x0017 | Business/Financial News (National) | +| 0x0018 | Business/Financial News (International) | +| 0x0019 | Sports News (Local) | +| 0x001A | Sports News (Regional) | +| 0x001B | Sports News (National) | +| 0x001C | Sports News (International) | +| 0x001D | Entertainment News (Local) | +| 0x001E | Entertainment News (Regional) | +| 0x001F | Entertainment News (National) | +| 0x0020 | Entertainment News (International) | +| | Subscriber Information Services | +| 0x0021 | Medical/Health/Hospitals | +| 0x0022 | Doctors | +| 0x0023 | Pharmacy | +| 0x0030 | Local Traffic/Road Reports | +| 0x0031 | Long Distance Traffic/Road Reports | +| 0x0032 | Taxis | +| 0x0033 | Weather | +| 0x0034 | Local Airport Flight Schedules | +| 0x0035 | Restaurants | +| 0x0036 | Lodgings | +| 0x0037 | Retail Directory | +| 0x0038 | Advertisements | +| 0x0038 | Advertisements | +| 0x0039 | Stock Quotes | +| 0x0040 | Employment Opportunities | +| 0x0041 | Technology News | +| | Carrier Information Services | +| 0x0070 | District (Base Station Info) | +| 0x0071 | Network Information | +| | Subscriber Care Services | +| 0x0080 | Operator Services | +| 0x0081 | Directory Enquiries (National) | +| 0x0082 | Directory Enquiries (International) | +| 0x0083 | Customer Care (National) | +| 0x0084 | Customer Care (International) | +| 0x0085 | Local Date/Time/Time Zone | +| | Multi Category Services | +| 0x0100 | Multi Category Services | + +** 4.8.4.9 broadcast_end_time + +This field is encoded in absolute UTC format as specified in Section 4.7.23.4 + +** 4.8.4.10 broadcast_error_status + +The value is one of the SMPP Error Code values as defined in Section 4.7.6 + +** 4.8.4.11 broadcast _frequency _interval + +Octet 1: specifies the Units of Time specified as follows: +| Encoding | Time Unit | +|----------+---------------------------| +| 0x00 | As frequently as possible | +| 0x08 | seconds | +| 0x09 | minutes | +| 0x0A | hours | +| 0x0B | days | +| 0x0C | weeks | +| 0x0D | months | +| 0x0E | years | + +Octet 2 + Octet 3: specifies the number of the specified time units in +an unsigned integral format. + +** 4.8.4.12 broadcast_message_class + +0x00 = No Class Specified (default) +0x02 = Class 1 (User Defined) +0x02 = Class 2 (User Defined) +0x03 = Class 3 (Terminal Equipment) + +** 4.8.4.13 broadcast_rep_num + +** 4.8.4.14 broadcast_service_group + +** 4.8.4.15 callback_num + +** 4.8.4.16 callback_num_atag + +** 4.8.4.17 callback_num_pres_ind + +| Bits | 7......0 | +| | 0000ppss | + +The Presentation Indicator is encoded in bits 2 and 3 as follows: +00 = Presentation Allowed +01 = Presentation Restricted +10 = Number Not Available +11 = Reserved + +The Screening Indicator is encoded in bits 0 and 1 as follows: +00 = User provided, not screened +01 = User provided, verified and passed +10 = User provided, verified and failed +11 = Network Provided. + +** 4.8.4.18 congestion_state + +0 = Idle +1-29 = Low Load +30-49 = Medium Load +50-79 = High Load +80-90 = Optimum Load +90-99 = Nearing Congestion +100 = Congested / Maximum Load + +** 4.8.4.19 delivery_failure_reason + +0 = Destination unavailable +1 = Destination Address Invalid (e.g. suspended, no SMS capability, etc.) +2 = Permanent network error +3 = Temporary network error + +** 4.8.4.20 dest_addr_np_country + +** 4.8.4.21 dest_addr_np_information + +** 4.8.4.22 dest_addr_np_resolution + +0 = query has not been performed (default) +1 = query has been performed, number not ported +2 = query has been performed, number ported + +** 4.8.4.23 dest_addr_subunit + +0x00 = Unknown (default) +0x01 = MS Display +0x02 = Mobile Equipment +0x03 = Smart Card 1 (expected to be SIM if a SIM exists in the MS) +0x04 = External Unit 1 + +** 4.8.4.24 dest_bearer_type + +0x00 = Unknown +0x01 = SMS +0x02 = Circuit Switched Data (CSD) +0x03 = Packet Data +0x04 = USSD +0x05 = CDPD +0x06 = DataTAC +0x07 = FLEX/ReFLEX +0x08 = Cell Broadcast (cell cast) + +** 4.8.4.25 dest_network_id + +See 4.8.4.56 + +** 4.8.4.26 dest_network_type + +0x00 = Unknown +0x01 = GSM +0x02 = ANSI-136/TDMA +0x03 = IS-95/CDMA +0x04 = PDC +0x05 = PHS +0x06 = iDEN +0x07 = AMPS +0x08 = Paging Network + +** 4.8.4.27 dest_node_id + +** 4.8.4.28 dest_subaddress + +See 4.8.4.60 for parameter encoding. + +** 4.8.4.29 dest_telematics_id + +Octet1 is used to represent the protocol_id field as used by GSM. + +See 4.7.20 + +** 4.8.4.30 dest_port + +** 4.8.4.31 display_time + +0 = Temporary +1 = Default (default) +2 = Invoke + +** 4.8.4.32 dpf_result + +0 = DPF not set +1 = DPF set + +** 4.8.4.33 its_reply_type + +0 = Digit +1 = Number +2 = Telephone No. +3 = Password +4 = Character Line +5 = Menu +6 = Date +7 = Time +8 = Continue + +** 4.8.4.34 its_session_info + +| Bits | 7......0 | +|-----------+----------| +| (octet 1) | SSSSSSSS | +| (octet 2) | NNNNNNNE | + +Octet 1 contains the session number (0 - 255) encoded in binary. + +The sequence number of the dialogue unit (as +assigned by the ESME) within the session is +encoded in bits 7..1 of octet 2. + +The End of Session Indicator indicates the +message is the end of the conversation session +and is encoded in bit 0 of octet 2 as follows: +0 = End of Session Indicator inactive. +1 = End of Session Indicator active. + +** 4.8.4.35 language_indicator + +0 = unspecified (default) +1 = English +2 = French +3 = Spanish +4 = German +5 = Portuguese +Refer to [CMT-136] for other values + +** 4.8.4.36 message_payload + +** 4.8.4.37 message_state + +Values as per section 4.7.15 + +** 4.8.4.38 more_messages_to_send + +0 = No more messages to follow +1 = More messages to follow (default) + +** 4.8.4.39 ms_availability_status + +0 = Available (Default) +1 = Denied (e.g. suspended, no SMS capability, etc.) +2 = Unavailable + +** 4.8.4.40 ms_msg_wait_facilities + +| Bits | 7......0 | +|------+----------| +| | I00000TT | + +The Indicator is encoded in bit 7 as follows: +0 = Set Indication Inactive +1 = Set Indication Active + +The Type of Message associated with the MWI is encoded in bits 0 and 1 as follows: +00 = Voicemail Message Waiting +01 = Fax Message Waiting +10 = Electronic Mail Message Waiting +11 = Other Message Waiting + +** 4.8.4.41 ms_validity + +Octet 1: specifies validity behaviour +0 = Store Indefinitely (default) +1 = Power Down +2 = Valid until Registration Area Changes +3 = Display Only +4 = Relative time period (which is specified in the following 3 octets.) +values 5 to 255 are reserved + +Octet 2-4 are optional and when specified, provide +extended validity information + +Octet 2: specifies the Units of Time +| Encoding | Time Unit | +|-------------+-----------| +| 00 00 00 00 | seconds | +| 00 00 00 01 | minutes | +| 00 00 00 10 | hours | +| 00 00 00 11 | days | +| 00 00 01 00 | weeks | +| 00 00 01 01 | months | +| 00 00 01 10 | years | + +Octet3 + Octet 4: specifies the number of the specified time units in +an unsigned integer format. + +** 4.8.4.42 network_error_code + +| Sub-field | Size | Type | +|--------------+------+---------| +| Network Type | 1 | Integer | +| Error Code | 2 | Integer | + +The first octet indicates the network type. +The following values are defined: +1 = ANSI 136 Access Denied Reason +2 = IS 95 Access Denied Reason +3 = GSM +4 = ANSI 136 Cause Code +5 = IS 95 Cause Code +6 = ANSI-41 Error +7 = SMPP Error +8 = Message Center Specific + +The remaining two octets specify the actual network error code +appropriate to the network type. + +** 4.8.4.43 number_of_messages + +0 to 99 = allowed values. + +** 4.8.4.44 payload_type + +0 = Default. +In the case of a WAP application, the default higher layer message type is a WDP message. See [15] + +1= WCMP message. +Wireless Control Message Protocol formatted data. See [14] for details. + +** 4.8.4.45 privacy_indicator + +0 = Privacy Level 0 (Not Restricted) (default) +1 = Privacy Level 1 (Restricted) +2 = Privacy Level 2 (Confidential) +3 = Privacy Level 3 (Secret) + +** 4.8.4.46 qos_time_to_live + +** 4.8.4.47 receipted_message_id + +** 4.8.4.48 sar_msg_ref_num + +** 4.8.4.49 sar_segment_seqnum + +** 4.8.4.50 sar_total_segments + +** 4.8.4.51 sc_interface_version + +values as per 4.7.13 (interface_version) + +** 4.8.4.52 set_dpf + +0 = Setting of DPF for delivery failure to MS not requested +1 = Setting of DPF for delivery failure requested (default) + +** 4.8.4.53 sms_signal + +Encoded as per [CMT-136] + +** 4.8.4.54 source_addr_subunit + +See 4.8.4.23 + +** 4.8.4.55 source_bearer_type + +See 4.8.4.24 + +** 4.8.4.56 source_network_id + +For GSM Networks: +1 + MCC+ MNC; (1cccnn) Ref. [1] + +For TDMA or CDMA Networks: +2 + MCC+ SID; (2cccsssss) Ref. [12] + +For ESME Operators: +3 + MCC + address type ind. + unique address + +The following address type indicators are defined: +| Type | Value | +|--------------+-------| +| IP Address | 1 | +| Alphanumeric | 2 | +| E.164 | 3 | +| X.212 | 4 | + +** 4.8.4.57 source_network_type + +See 4.8.4.26 + +** 4.8.4.58 source_node_id + +** 4.8.4.59 source_port + +** 4.8.4.60 source_subaddress + +Valid Tag values are: +00000001 - Reserved +00000010 - Reserved +10000000 - NSAP (Even) [ITUT X.213] +10001000 - NSAP (Odd) [ITUT X.213] +10100000 - User Specified + +The remaining octets contain the subaddress. + +** 4.8.4.61 source_telematics_id + +See 4.8.4.29 + +** 4.8.4.62 user_message_reference + +** 4.8.4.63 user_response_code + +0 to 255 (IS-95 CDMA) +0 to 15 (CMT-136 TDMA) + +** 4.8.4.64 ussd_service_op + +0 = PSSD indication +1 = PSSR indication +2 = USSR request +3 = USSN request +4 to 15 = reserved +16 = PSSD response +17 = PSSR response +18 = USSR confirm +19 = USSN confirm +20 to 31 = reserved +32 to 255 = reserved for vendor specific USSD operations From 2a19be8fd61aaebec5de017912a97a1fa616bcd3 Mon Sep 17 00:00:00 2001 From: Sebastian Weddmark Olsson Date: Sat, 6 Sep 2025 01:05:43 +0200 Subject: [PATCH 6/6] add readme --- README.org | 1 + 1 file changed, 1 insertion(+) diff --git a/README.org b/README.org index 027e1e1..1b64548 100644 --- a/README.org +++ b/README.org @@ -144,6 +144,7 @@ | SGSAP | 3GPP TS 29.118 v17.0.0 | X | X | | Parameters might be binary decoded (i.e. passthrough) | | GTPv1-C | 3GPP TS 29.060 v17.3.0 | X | X | | Parameters might be binary decoded (i.e. passthrough) | | GTPv2-C | 3GPP TS 29.274 v17.7.0 | X | X | | Parameters might be binary decoded (i.e. passthrough) | +| SMPP | SMS Forum SMPP V5.0 | x | x | | Parameters might be binary decoded (i.e. passthrough) | * Diameter interfaces