node-smpp icon indicating copy to clipboard operation
node-smpp copied to clipboard

crashes when trying to forward UDH

Open 0LEG0 opened this issue 5 years ago • 7 comments

For testing purposes, I created a transparent smpp proxy. socket A <-> (PDU) <-> socket B. Here is part of simplest code:

a.on("pdu", pdu => {
        console.log("->", pdu);
		
        // Inverse message_id to prevent message_id dublicates
        if (pdu.message_id) pdu.message_id = inverse(pdu.message_id);
        
        // Forward PDU
        console.log("<-", pdu);
        b.send(pdu);
});

Everything works fine, but sometimes these death messages happen: console.log output:

-> PDU {
  command_length: 83,
  command_id: 4,
  command_status: 0,
  sequence_number: 16704908,
  command: 'submit_sm',
  service_type: '',
  source_addr_ton: 5,
  source_addr_npi: 0,
  source_addr: 'aaaa',
  dest_addr_ton: 1,
  dest_addr_npi: 1,
  destination_addr: '0000',
  esm_class: 64,
  protocol_id: 0,
  priority_flag: 0,
  schedule_delivery_time: '',
  validity_period: 2021-01-16T15:32:33.000Z,
  registered_delivery: 1,
  replace_if_present_flag: 0,
  data_coding: 8,
  sm_default_msg_id: 0,
  short_message: { udh: [ <Buffer 00 03 26 02 02> ], message: 'nutes.' }
}
<- PDU {
  command_length: 83,
  command_id: 4,
  command_status: 0,
  sequence_number: 16704908,
  command: 'submit_sm',
  service_type: '',
  source_addr_ton: 5,
  source_addr_npi: 0,
  source_addr: 'aaaa',
  dest_addr_ton: 1,
  dest_addr_npi: 1,
  destination_addr: '0000',
  esm_class: 64,
  protocol_id: 0,
  priority_flag: 0,
  schedule_delivery_time: '',
  validity_period: 2021-01-16T15:32:33.000Z,
  registered_delivery: 1,
  replace_if_present_flag: 0,
  data_coding: 8,
  sm_default_msg_id: 0,
  short_message: { udh: [ <Buffer 00 03 26 02 02> ], message: 'nutes.' }
}
TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received an instance of Array
    at Object.concat (buffer.js:574:13)
    at PDU.encode (/devel/smpp-tools/node_modules/smpp/lib/defs.js:545:17)
    at PDU._filter (/devel/smpp-tools/node_modules/smpp/lib/pdu.js:144:41)
    at PDU.toBuffer (/devel/smpp-tools/node_modules/smpp/lib/pdu.js:177:7)
    at Session.send (/devel/smpp-tools/node_modules/smpp/lib/smpp.js:119:24)
    at Session.<anonymous> (/devel/smpp-tools/proxy.js:90:11)
    at Session.emit (events.js:315:20)
    at Session._extractPDUs (/devel/smpp-tools/node_modules/smpp/lib/smpp.js:88:8)
    at Socket.emit (events.js:315:20)
    at emitReadable_ (_stream_readable.js:569:12) {
  code: 'ERR_INVALID_ARG_TYPE'
}

It looks like not correctly decoded-encoded message body. Here is my ugly workaround preventing proxy from crash but we lost a part of message:

a.on("pdu", pdu => {
        console.log("->", pdu);
		
        // Inverse message_id to prevent message_id dublicates
        if (pdu.message_id) pdu.message_id = inverse(pdu.message_id);

        // Ugly workaround
        for (let key in pdu.short_message) {
            if (key !== "message") delete pdu.short_message[key];
        }

        // Forward PDU
        console.log("<-", pdu);
        b.send(pdu);
});

I would like to understand the problem, but manipulations with message encoding-decoding are confusing.

0LEG0 avatar Jan 15 '21 08:01 0LEG0

I'm not sure about what kind of configuration are you testing. Are you trying to implement some type of relay server?

client > relay (server & client) > server

Also, could you enable the new debug mode for all clients / servers and post the results? They will be very helpful to try to understand the issue. Debug messages are emitted on the socket level.

rmruano avatar Sep 30 '21 19:09 rmruano

Thanks for your comment, rmruano. I've made some kind of transparent proxy for SMPP PDUs. And I found the problem with incorrect encoding/decoding of the UDHs. If one socket sends us a SUBMIT_SM with a UDH, we will get a failure trying to resend this PDU as is on another socket.

0LEG0 avatar Oct 08 '21 18:10 0LEG0

@0LEG0 that is because the original full UDH header is lost and extracted as an array to be used by the developer, you should compose the UDH header again

juliangut avatar Oct 14 '21 09:10 juliangut

Need help to resolve the issue. Here is the PDU I received:

PDU {
  command_length: 83,
  command_id: 4,
  command_status: 0,
  sequence_number: 16704908,
  command: 'submit_sm',
  service_type: '',
  source_addr_ton: 5,
  source_addr_npi: 0,
  source_addr: 'aaaa',
  dest_addr_ton: 1,
  dest_addr_npi: 1,
  destination_addr: '0000',
  esm_class: 64,
  protocol_id: 0,
  priority_flag: 0,
  schedule_delivery_time: '',
  validity_period: 2021-01-16T15:32:33.000Z,
  registered_delivery: 1,
  replace_if_present_flag: 0,
  data_coding: 8,
  sm_default_msg_id: 0,
  short_message: { udh: [ <Buffer 00 03 26 02 02> ], message: 'nutes.' }
}

Then I get following error when I try to resend this PDU (as is) to another smpp session:

TypeError [ERR_INVALID_ARG_TYPE]: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received an instance of Array
    at Object.concat (buffer.js:574:13)
    at PDU.encode (/devel/smpp-tools/node_modules/smpp/lib/defs.js:545:17)
    at PDU._filter (/devel/smpp-tools/node_modules/smpp/lib/pdu.js:144:41)
    at PDU.toBuffer (/devel/smpp-tools/node_modules/smpp/lib/pdu.js:177:7)
    at Session.send (/devel/smpp-tools/node_modules/smpp/lib/smpp.js:119:24)
    at Session.<anonymous> (/devel/smpp-tools/proxy.js:90:11)
    at Session.emit (events.js:315:20)
    at Session._extractPDUs (/devel/smpp-tools/node_modules/smpp/lib/smpp.js:88:8)
    at Socket.emit (events.js:315:20)
    at emitReadable_ (_stream_readable.js:569:12) {
  code: 'ERR_INVALID_ARG_TYPE'
}

Here's a possible problem spot in lib/defs.js:

filters.message = {
	encode: function(value) {
		if (Buffer.isBuffer(value)) {
			return value;
		}
		var message = typeof value === 'string' ? value : value.message;
		if (typeof message === 'string' && message) {
			var encoded = false;
			if (value.udh) {
				var udhList = udhCoder.getUdh(value.udh);
				for (var i = 0; i < udhList.length; i++) {
					var udh = udhList[i];
					if (udh[0] === 0x24 || udh[0] === 0x25) {
						this.data_coding = consts.ENCODING.ASCII;
						message = gsmCoder.encode(message, udh[2]);
						encoded = true;
						break;
					}
				}
			}
			if (!encoded) {
				var encoding = encodings.default;
				if (this.data_coding === null) {
					encoding = encodings.detect(message);
					this.data_coding = consts.ENCODING[encoding];
				} else if (this.data_coding !== consts.ENCODING.SMSC_DEFAULT) {
					for (var key in consts.ENCODING) {
						if (consts.ENCODING[key] === this.data_coding) {
							encoding = key;
							break;
						}
					}
				}
				message = encodings[encoding].encode(message);
			}
		}
		if (!value.udh || !value.udh.length) {
			return message;
		}
		if ('esm_class' in this) {
			this.esm_class = this.esm_class | consts.ESM_CLASS.UDH_INDICATOR;
		}
		return Buffer.concat([value.udh, message]);
		//                   ^
		//                   |
		// Error: The "list[0]" argument must be an instance of Buffer or Uint8Array. Received an instance of Array
	}

Unfortunately I can't reproduce this issue in my sandbox because I don't have enough experience with UDH. Any help is appreciated.

https://github.com/farhadi/node-smpp/issues/107 https://github.com/farhadi/node-smpp/issues/69

0LEG0 avatar Oct 14 '21 09:10 0LEG0

Greetings @juliangut! What do you mean when said "the original full UDH header is lost" ? I looked old issues about UDH and found https://github.com/farhadi/node-smpp/issues/139 somebody got the same message:

short_message: {
    udh: [ <Buffer 00 03 01 02 02> ],
    message: 'y  complaint or enquiry, please call 8980'
  }

Considering this https://en.wikipedia.org/wiki/User_Data_Header#UDH_Information_Elements and this https://en.m.wikipedia.org/wiki/Concatenated_SMS I assume this is a multiparted message: 00 - Concatenated short message 03 - Length of the header, excluding the first two fields; equal to 03 01 - CSMS reference number, must be same for all the SMS parts in the CSMS 02 - total number of parts 02 - part's number in the sequence Did I understand everything correctly? Thanks

0LEG0 avatar Oct 14 '21 10:10 0LEG0

For forwarding UDH, working well for me.


this.reCalculateUDH = (short_message)=>{
    if(short_message.udh){        
        let udhString = JSON.stringify(short_message.udh[0])
        let udhJSON = JSON.parse(udhString)
        let udhData = udhJSON.data
        let sms_count=udhData[3]
        let part_id=udhData[4]
        //This is a unique id present in each message part
        let concat_ref = this.concat_ref++; 
        let udh = new Buffer.alloc(6);

        udh.write(String.fromCharCode(0x5), 0); //Length of UDF
        udh.write(String.fromCharCode(0x0), 1); //Indicator for concatenated message
        udh.write(String.fromCharCode(0x3), 2); //  Subheader Length ( 3 bytes)
        udh.write(String.fromCharCode(concat_ref), 3); //Same reference for all concatenated messages
        udh.write(String.fromCharCode(sms_count), 4); //Number of total messages in the concatenation
        udh.write(String.fromCharCode(part_id), 5); //Sequence number ( used by the mobile to concatenate the split messages)
        return udh
    }else{
        return null
    }    
}

asepfauji95 avatar Jul 20 '23 09:07 asepfauji95