fiorix / go-smpp

SMPP 3.4 Protocol for the Go programming language
MIT License
220 stars 134 forks source link

Decoded DeliverSM PDU differs from serialised one #69

Open Alexey1100 opened 6 years ago

Alexey1100 commented 6 years ago

Looks like decoding process adds one null byte at the start of ShortMessage field.

Example code:

package main

import (
    "bytes"
    "fmt"

    "github.com/fiorix/go-smpp/smpp"
    "github.com/fiorix/go-smpp/smpp/pdu"
    "github.com/fiorix/go-smpp/smpp/pdu/pdufield"
    "github.com/fiorix/go-smpp/smpp/pdu/pdutext"
)

func main(){
    sm := &smpp.ShortMessage{
            Src:      "TEST",
            Dst:      "+79000005555",
            Text:     pdutext.UCS2("Hello"),
            Register: pdufield.NoDeliveryReceipt,
    }
    p := pdu.NewDeliverSM()
    f := p.Fields()
    f.Set(pdufield.SourceAddr, sm.Src)
    f.Set(pdufield.DestinationAddr, sm.Dst)
    f.Set(pdufield.ShortMessage, sm.Text)
    f.Set(pdufield.RegisteredDelivery, uint8(sm.Register))
    f.Set(pdufield.ServiceType, sm.ServiceType)
    f.Set(pdufield.SourceAddrTON, sm.SourceAddrTON)
    f.Set(pdufield.SourceAddrNPI, sm.SourceAddrNPI)
    f.Set(pdufield.DestAddrTON, sm.DestAddrTON)
    f.Set(pdufield.DestAddrNPI, sm.DestAddrNPI)
    f.Set(pdufield.ESMClass, sm.ESMClass)
    f.Set(pdufield.ProtocolID, sm.ProtocolID)
    f.Set(pdufield.PriorityFlag, sm.PriorityFlag)
    f.Set(pdufield.ScheduleDeliveryTime, sm.ScheduleDeliveryTime)
    f.Set(pdufield.ReplaceIfPresentFlag, sm.ReplaceIfPresentFlag)
    f.Set(pdufield.SMDefaultMsgID, sm.SMDefaultMsgID)
    f.Set(pdufield.DataCoding, uint8(sm.Text.Type()))

    var b bytes.Buffer
    p.SerializeTo(&b)
    fmt.Printf("INITIAL PDU: %x\n", b.Bytes())

    np, _ := pdu.Decode(bytes.NewBuffer(b.Bytes()))
    var nb bytes.Buffer
    np.SerializeTo(&nb)
    fmt.Printf("DECODED PDU: %x\n", nb.Bytes())

    fmt.Printf("INITIAL SM: %x\n", p.Fields()[pdufield.ShortMessage])
    fmt.Printf("DECODED SM: %x\n", np.Fields()[pdufield.ShortMessage])
}

Output:

$ go run smpp_sample.go
INITIAL PDU: 0000003c000000050000000000000001000000544553540000002b3739303030303035353535000000000000000008000a0000480065006c006c006f
DECODED PDU: 0000003c000000050000000000000001000000544553540000002b3739303030303035353535000000000000000008000a000000480065006c006c00
INITIAL SM: 00480065006c006c006f
DECODED SM: 0000480065006c006c00
Alexey1100 commented 6 years ago

Though, it works fine when dealing with SubmitSM PDU:

p := pdu.NewSubmitSM()
$ go run smpp_sample.go
INITIAL PDU: 0000003b000000040000000000000001000000544553540000002b3739303030303035353535000000000000000008000a00480065006c006c006f
DECODED PDU: 0000003b000000040000000000000001000000544553540000002b3739303030303035353535000000000000000008000a00480065006c006c006f
INITIAL SM: 00480065006c006c006f
DECODED SM: 00480065006c006c006f
fiorix commented 6 years ago

Interesting. I haven't looked into this in a while, and haven't actually used this code for about a year now. If you can find where in the decoder we're adding that extra byte, please send a PR and I'll be happy to review.

Alexey1100 commented 6 years ago

What I found at the moment, that it somehow refers to UDH parsing. Since when I'm removing UDHLength from pdufield.List in newDeliverSM, it decodes the example PDU just fine.

https://github.com/fiorix/go-smpp/blob/8eb557099ddf995af5b778652b1812f1ff86889c/smpp/pdu/types.go#L311

newSubmitSM doesn't have UDH fields defined for some reason.

Alexey1100 commented 6 years ago

This dirty hack seems to fix the problem, but I'm really not sure if it breaks something with an actual UDH.

// smpp/pdu/codec.go

// SerializeTo implements the PDU interface.
func (pdu *codec) SerializeTo(w io.Writer) error {
    var b bytes.Buffer
    for _, k := range pdu.FieldList() {
        f, ok := pdu.f[k]
        if !ok {
            // HACK: Skipping serialisation of UDH if it's not found in PDU
            if k == "gsm_sms_ud.udh.len" || k == "gsm_sms_ud.udh" {
              continue
            }

            pdu.f.Set(k, nil)
            f = pdu.f[k]
        }
        if err := f.SerializeTo(&b); err != nil {
            return err
        }
    }
    for _, f := range pdu.TLVFields() {
        if err := f.SerializeTo(&b); err != nil {
            return err
        }
    }
    pdu.h.Len = uint32(pdu.Len())
    err := pdu.h.SerializeTo(w)
    if err != nil {
        return err
    }
    _, err = io.Copy(w, &b)
    return err
}
mdouchement commented 2 years ago

May be fixed by #94