/****************************************************************************** * Copyright (c) 2000-2019 Ericsson Telecom AB * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html * * Contributors: * Gabor Szalai - initial implementation and initial documentation * Harald Welte - Alert Notification support ******************************************************************************/ // // File: SMPP_EncDec.cc // Rev: R2A // Prodnr: CNL 113 772 // Reference: [1] Short Message Peer to Peer Protocol Specification v3.4 // Reference: [2] Short Message Peer to Peer Protocol Specification v5.0 #include "SMPP_Types.hh" namespace SMPP__Types { void decode_SMPP_Bind(TTCN_Buffer& input, SMPP__Bind& output); void decode_SMPP_Bind_resp(TTCN_Buffer& input, const unsigned int body_length, SMPP__Bind__resp& output); void decode_SMPP_SM(TTCN_Buffer& input, const unsigned int body_length, SMPP__SM& output); void decode_SMPP_Cancel(TTCN_Buffer& input, const unsigned int body_length, SMPP__Cancel& output); void decode_SMPP_Replace(TTCN_Buffer& input, const unsigned int body_length, SMPP__Replace& output); void decode_SMPP_SM_resp(TTCN_Buffer& input, const unsigned int body_length, SMPP__SM__resp& output); void decode_SMPP_Outbind(TTCN_Buffer& input, SMPP__Outbind& output); void decode_SMPP_MULTI(TTCN_Buffer& input, const unsigned int body_length, SMPP__MULTI& output); void decode_SMPP_MULTI_resp(TTCN_Buffer& input, const unsigned int body_length, SMPP__MULTI__resp& output); void decode_SMPP_AlertNotif(TTCN_Buffer& input, const unsigned int body_length, SMPP__AlertNotif& output); // puts '\0' octets at the end of C-Octetstrings void pad_C_Octetstrings(SMPP__PDU& data); void pad_C_Octetstrings(SMPP__optional__parameters& data); void crop_C_Octetstrings(SMPP__optional__parameters& data); void pad_C_Octetstrings(SMPP__Bind& data); void pad_C_Octetstrings(SMPP__Bind__resp& data); void pad_C_Octetstrings(SMPP__Outbind& data); void pad_C_Octetstrings(SMPP__Cancel& data); void pad_C_Octetstrings(SMPP__Replace& data); void pad_C_Octetstrings(SMPP__SM& data); void pad_C_Octetstrings(SMPP__SM__resp& data); void pad_C_Octetstrings(SMPP__MULTI& data); void pad_C_Octetstrings(SMPP__MULTI__resp& data); void pad_C_Octetstrings(SMPP__AlertNotif& data); void pad_C_Octetstring(CHARSTRING& data); unsigned int pick_os(TTCN_Buffer& buf, const unsigned int length, OCTETSTRING& output); void pick_bit1(TTCN_Buffer& buf, BITSTRING& output); // picks a C-Octetstring from the buffer // returns number of bytes extracted (including trailing null char) unsigned int pick_cos(TTCN_Buffer& buf, CHARSTRING& output); int pick_int(TTCN_Buffer& buf); INTEGER f__msg__length(const OCTETSTRING& data) { size_t msg_len = data.lengthof(); if (msg_len < 4) return -1; const unsigned char *data_ptr = (const unsigned char *)data; size_t expected_len = (data_ptr[0] << 24) | (data_ptr[1] << 16) | (data_ptr[2] << 8) | data_ptr[3]; return expected_len; } INTEGER f__decode__SMPP(const OCTETSTRING& data, SMPP__PDU& pdu){ TTCN_Buffer input=data; pdu.header().decode(SMPP__header_descr_, input, TTCN_EncDec::CT_RAW); // decode body if (pdu.header().command__id() == c__SMPP__command__id__bind__receiver) { decode_SMPP_Bind(input, pdu.body().bind__receiver()); } else if (pdu.header().command__id() == c__SMPP__command__id__bind__receiver__resp) { decode_SMPP_Bind_resp(input, pdu.header().command__len() - 16, pdu.body().bind__receiver__resp()); } else if (pdu.header().command__id() == c__SMPP__command__id__bind__transmitter) { decode_SMPP_Bind(input, pdu.body().bind__transmitter()); } else if (pdu.header().command__id() == c__SMPP__command__id__bind__transmitter__resp) { decode_SMPP_Bind_resp(input, pdu.header().command__len() - 16, pdu.body().bind__transmitter__resp()); } else if (pdu.header().command__id() == c__SMPP__command__id__submit__sm) { decode_SMPP_SM(input, pdu.header().command__len() - 16, pdu.body().submit__sm()); } else if (pdu.header().command__id() == c__SMPP__command__id__submit__sm__resp) { decode_SMPP_SM_resp(input, pdu.header().command__len() - 16, pdu.body().submit__sm__resp()); } else if (pdu.header().command__id() == c__SMPP__command__id__submit__multi) { decode_SMPP_MULTI(input, pdu.header().command__len() - 16, pdu.body().submit__multi()); } else if (pdu.header().command__id() == c__SMPP__command__id__submit__multi__resp) { decode_SMPP_MULTI_resp(input, pdu.header().command__len() - 16, pdu.body().submit__multi__resp()); } else if (pdu.header().command__id() == c__SMPP__command__id__bind__transceiver) { decode_SMPP_Bind(input, pdu.body().bind__transceiver()); } else if (pdu.header().command__id() == c__SMPP__command__id__bind__transceiver__resp) { decode_SMPP_Bind_resp(input, pdu.header().command__len() - 16, pdu.body().bind__transceiver__resp()); } else if (pdu.header().command__id() == c__SMPP__command__id__outbind) { decode_SMPP_Outbind(input, pdu.body().outbind()); } else if (pdu.header().command__id() == c__SMPP__command__id__deliver__sm) { decode_SMPP_SM(input, pdu.header().command__len() - 16, pdu.body().deliver__sm()); } else if (pdu.header().command__id() == c__SMPP__command__id__deliver__sm__resp) { decode_SMPP_SM_resp(input, pdu.header().command__len() - 16, pdu.body().deliver__sm__resp()); } else if (pdu.header().command__id() == c__SMPP__command__id__cancel__sm) { decode_SMPP_Cancel(input, pdu.header().command__len() - 16, pdu.body().cancel__sm()); } else if (pdu.header().command__id() == c__SMPP__command__id__cancel__sm__resp) { pdu.body().cancel__sm__resp() = NULL_VALUE; } else if (pdu.header().command__id() == c__SMPP__command__id__replace__sm) { decode_SMPP_Replace(input, pdu.header().command__len() - 16, pdu.body().replace__sm()); } else if (pdu.header().command__id() == c__SMPP__command__id__replace__sm__resp) { pdu.body().replace__sm__resp() = NULL_VALUE; } else if (pdu.header().command__id() == c__SMPP__command__id__unbind) { pdu.body().unbind() = NULL_VALUE; } else if (pdu.header().command__id() == c__SMPP__command__id__unbind__resp) { pdu.body().unbind__resp() = NULL_VALUE; } else if (pdu.header().command__id() == c__SMPP__command__id__enquire__link) { pdu.body().enquire__link() = NULL_VALUE; } else if (pdu.header().command__id() == c__SMPP__command__id__enquire__link__resp) { pdu.body().enquire__link__resp() = NULL_VALUE; } else if (pdu.header().command__id() == c__SMPP__command__id__generic__nack){ pdu.body().generic__nack() = NULL_VALUE; } else if (pdu.header().command__id() == c__SMPP__command__id__alert__notification) { decode_SMPP_AlertNotif(input, pdu.header().command__len() - 16, pdu.body().alert__notif()); } else { TTCN_warning("Decoding of incoming message not implemented."); int body_length = pdu.header().command__len() - 16; if (body_length < 0) TTCN_warning("Unable to decode body of SMPP message (invalid command_len)"); pdu.body().raw() = OCTETSTRING(body_length, input.get_read_data()); input.set_pos(input.get_pos() + body_length); } return 0; } void f__encode__SMPP(const SMPP__PDU& pdu, OCTETSTRING& data){ SMPP__PDU to_padding = pdu; pad_C_Octetstrings(to_padding); TTCN_Buffer out_buf; to_padding.encode(SMPP__PDU_descr_, out_buf, TTCN_EncDec::CT_RAW); out_buf.get_string(data); return; } void pad_C_Octetstrings(SMPP__PDU& data) { SMPP__operation__body& body = data.body(); switch(body.get_selection()){ case SMPP__operation__body::ALT_bind__transmitter: { pad_C_Octetstrings(body.bind__transmitter()); } break; case SMPP__operation__body::ALT_bind__transmitter__resp: { pad_C_Octetstrings(body.bind__transmitter__resp()); } break; case SMPP__operation__body::ALT_bind__receiver: { pad_C_Octetstrings(body.bind__receiver()); } break; case SMPP__operation__body::ALT_bind__receiver__resp: { pad_C_Octetstrings(body.bind__receiver__resp()); } break; case SMPP__operation__body::ALT_bind__transceiver: { pad_C_Octetstrings(body.bind__transceiver()); } break; case SMPP__operation__body::ALT_bind__transceiver__resp: { pad_C_Octetstrings(body.bind__transceiver__resp()); } break; case SMPP__operation__body::ALT_outbind: { pad_C_Octetstrings(body.outbind()); } break; case SMPP__operation__body::ALT_unbind: { // void } break; case SMPP__operation__body::ALT_unbind__resp: { // void } break; case SMPP__operation__body::ALT_submit__sm: { pad_C_Octetstrings(body.submit__sm()); } break; case SMPP__operation__body::ALT_submit__sm__resp: { pad_C_Octetstrings(body.submit__sm__resp()); } break; case SMPP__operation__body::ALT_submit__multi: { pad_C_Octetstrings(body.submit__multi()); } break; case SMPP__operation__body::ALT_submit__multi__resp: { pad_C_Octetstrings(body.submit__multi__resp()); } break; case SMPP__operation__body::ALT_deliver__sm: { pad_C_Octetstrings(body.deliver__sm()); } break; case SMPP__operation__body::ALT_deliver__sm__resp: { pad_C_Octetstrings(body.deliver__sm__resp()); } break; case SMPP__operation__body::ALT_cancel__sm: { pad_C_Octetstrings(body.cancel__sm()); } break; case SMPP__operation__body::ALT_cancel__sm__resp: { // void } break; case SMPP__operation__body::ALT_replace__sm: { pad_C_Octetstrings(body.replace__sm()); } break; case SMPP__operation__body::ALT_replace__sm__resp: { // void } break; case SMPP__operation__body::ALT_enquire__link: { // void } break; case SMPP__operation__body::ALT_enquire__link__resp: { // void } break; case SMPP__operation__body::ALT_generic__nack: { // void } break; case SMPP__operation__body::ALT_alert__notif: { pad_C_Octetstrings(body.alert__notif()); } break; default: TTCN_error("Unhandled union field in SMPP_operation_body."); } } void pad_C_Octetstrings(SMPP__SM& data) { pad_C_Octetstring(data.service__type()); pad_C_Octetstring(data.source__addr()); pad_C_Octetstring(data.destination__addr()); pad_C_Octetstring(data.schedule__delivery__time()); pad_C_Octetstring(data.validity__period()); pad_C_Octetstrings(data.opt__pars()); } void pad_C_Octetstrings(SMPP__SM__resp& data) { pad_C_Octetstring(data.message__id()); if (data.opt__pars().ispresent()) pad_C_Octetstrings(data.opt__pars()); } void pad_C_Octetstrings(SMPP__MULTI& data) { pad_C_Octetstring(data.service__type()); pad_C_Octetstring(data.source__addr()); for(int a=0;a 0) { // should be >=4 in this case anyway if (remaining_bytes < 4) { TTCN_warning("Invalid length of optional parameters."); output.opt__pars() = NULL_VALUE; } else { output.opt__pars()().decode(SMPP__optional__parameters_descr_, input, TTCN_EncDec::CT_RAW); } } else { output.opt__pars() = OMIT_VALUE; } if (output.opt__pars() != OMIT_VALUE) crop_C_Octetstrings(output.opt__pars()); } void decode_SMPP_SM(TTCN_Buffer& input, const unsigned int body_length, SMPP__SM& output) { unsigned int remaining_bytes = body_length; remaining_bytes -= pick_cos(input, output.service__type()); output.source__addr__ton() = pick_int(input); output.source__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.source__addr()); output.dest__addr__ton() = pick_int(input); output.dest__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.destination__addr()); pick_bit1(input, output.esm__class()); output.protocol__id() = pick_int(input); output.priority__flag() = pick_int(input); remaining_bytes -= 3; remaining_bytes -= pick_cos(input, output.schedule__delivery__time()); remaining_bytes -= pick_cos(input, output.validity__period()); pick_bit1(input, output.registered__delivery()); output.replace__if__present() = pick_int(input); pick_bit1(input, output.data__coding()); output.sm__default__msg__id() = pick_int(input); output.sm__length() = pick_int(input); remaining_bytes -= 5; remaining_bytes -= pick_os(input, output.sm__length(), output.short__message()); // optional parameters if (remaining_bytes > 0) { // should be >=4 in this case anyway if (remaining_bytes < 4) { TTCN_warning("Invalid length of optional parameters."); output.opt__pars() = NULL_VALUE; } else { output.opt__pars().decode(SMPP__optional__parameters_descr_, input, TTCN_EncDec::CT_RAW); } } else { output.opt__pars() = NULL_VALUE; } crop_C_Octetstrings(output.opt__pars()); } void decode_SMPP_MULTI_resp(TTCN_Buffer& input, const unsigned int body_length, SMPP__MULTI__resp& output) { unsigned int remaining_bytes = body_length; remaining_bytes -= pick_cos(input, output.message__id()); output.no__unsuccess() = pick_int(input); remaining_bytes--; output.unsucecss__sme() = NULL_VALUE; for(int a=0;a 0) { // should be >=4 in this case anyway if (remaining_bytes < 4) { TTCN_warning("Invalid length of optional parameters."); output.opt__pars() = NULL_VALUE; } else { output.opt__pars()().decode(SMPP__optional__parameters_descr_, input, TTCN_EncDec::CT_RAW); } } else { output.opt__pars() = OMIT_VALUE; } if (output.opt__pars() != OMIT_VALUE) crop_C_Octetstrings(output.opt__pars()); } void decode_SMPP_MULTI(TTCN_Buffer& input, const unsigned int body_length, SMPP__MULTI& output) { unsigned int remaining_bytes = body_length; remaining_bytes -= pick_cos(input, output.service__type()); output.source__addr__ton() = pick_int(input); output.source__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.source__addr()); output.number__of__dests() = pick_int(input); remaining_bytes--; output.dest__address() = NULL_VALUE; for(int a=0;a 0) { // should be >=4 in this case anyway if (remaining_bytes < 4) { TTCN_warning("Invalid length of optional parameters."); output.opt__pars() = NULL_VALUE; } else { output.opt__pars().decode(SMPP__optional__parameters_descr_, input, TTCN_EncDec::CT_RAW); } } else { output.opt__pars() = NULL_VALUE; } crop_C_Octetstrings(output.opt__pars()); } void decode_SMPP_Cancel(TTCN_Buffer& input, const unsigned int body_length, SMPP__Cancel& output) { unsigned int remaining_bytes = body_length; remaining_bytes -= pick_cos(input, output.service__type()); remaining_bytes -= pick_cos(input, output.message__id()); output.source__addr__ton() = pick_int(input); output.source__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.source__addr()); output.dest__addr__ton() = pick_int(input); output.dest__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.destination__addr()); } void decode_SMPP_Replace(TTCN_Buffer& input, const unsigned int body_length, SMPP__Replace& output) { unsigned int remaining_bytes = body_length; remaining_bytes -= pick_cos(input, output.message__id()); output.source__addr__ton() = pick_int(input); output.source__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.source__addr()); remaining_bytes -= pick_cos(input, output.schedule__delivery__time()); remaining_bytes -= pick_cos(input, output.validity__period()); output.registered__delivery() = pick_int(input); output.sm__default__msg__id() = pick_int(input); output.sm__length() = pick_int(input); remaining_bytes -= 3; remaining_bytes -= pick_os(input, output.sm__length(), output.short__message()); // optional parameters if (remaining_bytes > 0) { // should be >=4 in this case anyway if (remaining_bytes < 4) { TTCN_warning("Invalid length of optional parameters."); output.opt__pars() = NULL_VALUE; } else { output.opt__pars().decode(SMPP__optional__parameters_descr_, input, TTCN_EncDec::CT_RAW); } } else { output.opt__pars() = NULL_VALUE; } crop_C_Octetstrings(output.opt__pars()); } void decode_SMPP_AlertNotif(TTCN_Buffer& input, const unsigned int body_length, SMPP__AlertNotif& output) { unsigned int remaining_bytes = body_length; output.source__addr__ton() = pick_int(input); output.source__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.source__addr()); output.esme__addr__ton() = pick_int(input); output.esme__addr__npi() = pick_int(input); remaining_bytes -= 2; remaining_bytes -= pick_cos(input, output.esme__addr()); if (remaining_bytes > 0) { // should be >=4 in this case anyway if (remaining_bytes < 4) { TTCN_warning("Invalid length of optional parameters."); output.opt__pars() = NULL_VALUE; } else { output.opt__pars().decode(SMPP__optional__parameters_descr_, input, TTCN_EncDec::CT_RAW); } } else { output.opt__pars() = NULL_VALUE; } crop_C_Octetstrings(output.opt__pars()); } void decode_SMPP_Bind_resp(TTCN_Buffer& input, const unsigned int body_length, SMPP__Bind__resp& output) { unsigned int remaining_bytes = body_length; remaining_bytes -= pick_cos(input, output.system__id()); if (remaining_bytes > 0) { // should be >=4 in this case anyway if (remaining_bytes < 4) { TTCN_warning("Invalid length of optional parameters."); output.opt__pars() = NULL_VALUE; } else { output.opt__pars().decode(SMPP__optional__parameters_descr_, input, TTCN_EncDec::CT_RAW); } } else { output.opt__pars() = NULL_VALUE; } crop_C_Octetstrings(output.opt__pars()); } void decode_SMPP_Outbind(TTCN_Buffer& input, SMPP__Outbind& output) { pick_cos(input, output.system__id()); pick_cos(input, output.password()); } // picks an octetstring from the buffer unsigned int pick_os(TTCN_Buffer& buf, const unsigned int length, OCTETSTRING& output) { const unsigned int read_len = buf.get_read_len(); unsigned int extracted_bytes; if (read_len < length) { TTCN_warning("Can not extract octetstring of length %d from buffer", length); output = OCTETSTRING(read_len, buf.get_read_data()); buf.set_pos(buf.get_pos() + read_len); extracted_bytes = read_len; } else { output = OCTETSTRING(length, buf.get_read_data()); buf.set_pos(buf.get_pos() + length); extracted_bytes = length; } return extracted_bytes; } // picks a C-Octetstring from the buffer // returns number of bytes extracted (including trailing null char) unsigned int pick_cos(TTCN_Buffer& buf, CHARSTRING& output) { const unsigned int read_len = buf.get_read_len(); unsigned int cur_pos = 0; const unsigned char* data = buf.get_read_data(); while (data[cur_pos] != '\0') { cur_pos++; if (cur_pos >= read_len) { TTCN_warning("Failed to extract C-Octetstring from the buffer."); cur_pos--; break; } } // now cur_pos points to the trailing 0 byte cur_pos++; output = CHARSTRING(cur_pos - 1, (const char*)data); buf.set_pos(buf.get_pos() + cur_pos); return cur_pos; } // picks bitstring of length 8 (msb first) void pick_bit1(TTCN_Buffer& buf, BITSTRING& output) { if (buf.get_read_len() < 1) { TTCN_warning("Failed to extract bitstring (8 bits) from buffer."); output = BITSTRING(8, (const unsigned char*)"\0"); } else { output.decode(SMPP__BIT1_descr_, buf, TTCN_EncDec::CT_RAW); } } int pick_int(TTCN_Buffer& buf) { if (buf.get_read_len() < 1) { TTCN_warning("Failed to extract integer from buffer."); return -1; } int ret_val = *(buf.get_read_data()); buf.set_pos(buf.get_pos() + 1); return ret_val; } }