GithubHelp home page GithubHelp logo

gosmpp's People

Contributors

bachhh avatar dasmfm avatar dependabot[bot] avatar goten4 avatar hurtsich avatar krstdmr avatar laduchesneau avatar linhduonggnu avatar linhtran1989 avatar linxgnu avatar septs avatar tahseenjamal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gosmpp's Issues

SMSC simulator is having some issue

I was trying to run SMSC by ./run.sh inside example folder but I'm getting following error

➜  smsc git:(master) ✗ ./run.sh
smsc.cpp: In destructor ‘virtual SMPPSession::~SMPPSession()’:
smsc.cpp:646:41: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
       logCommand("session aborted", "--");
                                         ^
smsc.cpp: In member function ‘void SMPPSession::logCommand(uint64_t, const char*)’:
smsc.cpp:1186:58: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 3 has type ‘uint64_t {aka long unsigned int}’ [-Wformat=]
     sprintf(buf, "0x%08llx %s", cmdID, b.cmdString(cmdID));
                                                          ^
smsc.cpp: In member function ‘void SMPPSession::logCommand(char*, const char*)’:
smsc.cpp:1201:33: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘uint64_t {aka long unsigned int}’ [-Wformat=]
     printf("%s %s [s%06llx:%-15s] %s\n", buffer, direction, session_id,
                                                             ~~~~~~~~~~
            conn.getIP(), logline);

Deadlock on closing

There is sometimes a deadlock when the connection is closed by server :

  • The close function is call in transmittable.go:45 and wait for the Lock:
func (t *transmittable) close(state State) (err error) {
	t.lock.Lock()

This function will call t.Cancel() (transmittable.go:50)

  • In the same time, the Submit func in transmittable.go:90 has the Lock and is waiting for t.ctx.Done() to release the Lock.

And so, the session is never released.

Example File not Working [Critical Error]

go run main.go

Getting Error.
2022/10/27 02:47:41 EOF
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x78 pc=0x3b5460]

goroutine 1 [running]:
github.com/linxGnu/gosmpp.(*Session).Close(...)
C:/Users/agan/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:92
main.sendingAndReceiveSMS.func5()
C:/Users/agan/Desktop/ezsmpp/main.go:63 +0x20
panic({0x3c9aa0, 0x4bf9b0})
C:/Program Files/Go/src/runtime/panic.go:1038 +0x215
github.com/linxGnu/gosmpp.(*Session).bound(...)
C:/Users/agan/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:71
github.com/linxGnu/gosmpp.(*Session).Transceiver(...)
C:/Users/agan/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:87
main.sendingAndReceiveSMS(0x2f8005)
C:/Users/agan/Desktop/ezsmpp/main.go:68 +0x372
main.main()
C:/Users/agan/Desktop/ezsmpp/main.go:19 +0x3e
exit status 2

go.mod

module ezsmpp

go 1.17

require github.com/linxGnu/gosmpp v0.1.4-rc24

require golang.org/x/text v0.4.0 // indirect

go.sum

github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/linxGnu/gosmpp v0.1.4-rc24 h1:cAZXUHNGphurSPWRl1RwKDJbaTwxGEEw2jU/QveAfAM=
github.com/linxGnu/gosmpp v0.1.4-rc24/go.mod h1:GDPzhhhZOM/hDQWIEp11VnK1KFcgDxaJ/eRen/YF+9Q=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

package main

import (
"fmt"
"log"
"strings"
"sync"
"time"

"github.com/linxGnu/gosmpp"
"github.com/linxGnu/gosmpp/data"
"github.com/linxGnu/gosmpp/pdu"

)

func main() {
var wg sync.WaitGroup

wg.Add(6)
sendingAndReceiveSMS(&wg)

wg.Wait()

}

func sendingAndReceiveSMS(wg *sync.WaitGroup) {
defer wg.Done()

auth := gosmpp.Auth{
	SMSC:       "smscsim.melroselabs.com:2775",
	SystemID:   "169994",
	Password:   "EDXPJU",
	SystemType: "",
}

trans, err := gosmpp.NewSession(
	gosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),
	gosmpp.Settings{
		EnquireLink: 5 * time.Second,

		ReadTimeout: 10 * time.Second,

		OnSubmitError: func(_ pdu.PDU, err error) {
			log.Fatal("SubmitPDU error:", err)
		},

		OnReceivingError: func(err error) {
			fmt.Println("Receiving PDU/Network error:", err)
		},

		OnRebindingError: func(err error) {
			fmt.Println("Rebinding but error:", err)
		},

		OnPDU: handlePDU(),

		OnClosed: func(state gosmpp.State) {
			fmt.Println(state)
		},
	}, 5*time.Second)
if err != nil {
	log.Println(err)
}
defer func() {
	_ = trans.Close()
}()

// sending SMS(s)
for i := 0; i < 1800; i++ {
	if err = trans.Transceiver().Submit(newSubmitSM()); err != nil {
		fmt.Println(err)
	}
	time.Sleep(time.Second)
}

}

func handlePDU() func(pdu.PDU, bool) {
concatenated := map[uint8][]string{}
return func(p pdu.PDU, _ bool) {
switch pd := p.(type) {
case *pdu.SubmitSMResp:
fmt.Printf("SubmitSMResp:%+v\n", pd)

	case *pdu.GenericNack:
		fmt.Println("GenericNack Received")

	case *pdu.EnquireLinkResp:
		fmt.Println("EnquireLinkResp Received")

	case *pdu.DataSM:
		fmt.Printf("DataSM:%+v\n", pd)

	case *pdu.DeliverSM:
		fmt.Printf("DeliverSM:%+v\n", pd)
		log.Println(pd.Message.GetMessage())
		// region concatenated sms (sample code)
		message, err := pd.Message.GetMessage()
		if err != nil {
			log.Fatal(err)
		}
		totalParts, sequence, reference, found := pd.Message.UDH().GetConcatInfo()
		if found {
			if _, ok := concatenated[reference]; !ok {
				concatenated[reference] = make([]string, totalParts)
			}
			concatenated[reference][sequence-1] = message
		}
		if !found {
			log.Println(message)
		} else if parts, ok := concatenated[reference]; ok && isConcatenatedDone(parts, totalParts) {
			log.Println(strings.Join(parts, ""))
			delete(concatenated, reference)
		}
		// endregion
	}
}

}

func newSubmitSM() *pdu.SubmitSM {
// build up submitSM
srcAddr := pdu.NewAddress()
srcAddr.SetTon(5)
srcAddr.SetNpi(0)
_ = srcAddr.SetAddress("00" + "522241")

destAddr := pdu.NewAddress()
destAddr.SetTon(1)
destAddr.SetNpi(1)
_ = destAddr.SetAddress("99" + "522241")

submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
submitSM.SourceAddr = srcAddr
submitSM.DestAddr = destAddr
_ = submitSM.Message.SetMessageWithEncoding("Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ", data.UCS2)
submitSM.ProtocolID = 0
submitSM.RegisteredDelivery = 1
submitSM.ReplaceIfPresentFlag = 0
submitSM.EsmClass = 0

return submitSM

}

func isConcatenatedDone(parts []string, total byte) bool {
for _, part := range parts {
if part != "" {
total--
}
}
return total == 0
}

Implementing TLS / Supporting SMPPS

I see by default we use a non-TLS dialer, which in turn relies on net.Conn for instantiating and subsequently defining a significant portion of the downstream buffer reads/etc.

Is there a working example or any plans to support a crypto/tls Dialer?

I've attempted to do this myself, but I'm getting some odd PDU related errors which I'm in the process of trying to debug.

It seems a significant portion of the libraries depend on the root connection working from the net.Conn return type. Has anyone had successful forks of implementing the above?

Example

Can u please provide example for smpp client connection handling all pdu...Earlier it was there in test folder now it has been removed..
Thanks

UTF-16 encoded length issue

gosmpp/data/codings.go

Lines 150 to 152 in cc603b5

func (c ucs2) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
return uint(len(text)*2) > octetLimit
}

This a simple algorithm not correct.

e.q: Wrong for Emoji

>>> '😈'.encode('utf-16-be')
b'\xd8=\xde\x08'
>>> len(_)
4

v0.1.4 isn't working with example - Results in Segmentation Fault

I suspect this is because the example was built for 0.1.3 in mind.

0.1.4-rc29 gives an immediate segfault on session closure (possibly an uncaptured error):

2022/06/23 10:44:16 EOF
panic: runtime error: invalid memory address or nil pointer dereference
        panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x78 pc=0x4df8a0]

goroutine 19 [running]:
github.com/linxGnu/gosmpp.(*Session).Close(...)
        /home/aaron/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:93
main.sendingAndReceiveSMS.func5()
        tools/smpp-client/main.go:65 +0x20
panic({0x4f4460, 0x5e5e40})
        /usr/local/go/src/runtime/panic.go:838 +0x207
github.com/linxGnu/gosmpp.(*Session).bound(...)
        /home/aaron/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:72
github.com/linxGnu/gosmpp.(*Session).Transceiver(...)
        /home/aaron/go/pkg/mod/github.com/linx!gnu/[email protected]/session.go:88
main.sendingAndReceiveSMS(0x0?)
        tools/smpp-client/main.go:71 +0x3da
created by main.main
        tools/smpp-client/main.go:20 +0x76

Looks as though the session state isn't instantiated as the code is breaking here: https://github.com/linxGnu/gosmpp/blob/master/session.go#L90-L96

How should I handle binary messages ?

I need to handle binary messages.

It appears that the data coding of DeliverSM PDU is not exposed, and in codingMap (codings.go:209), data coding 0x02 and 0x04 are not supported (datacoding codes for 8-bit binary).
It's the same problem for SubmitSM.

Have you ever met this issue ?
Do you intend to support binary messages in the future ?
I can make a pull request if you wish, any suggestions of implementation are welcome ;).

How to implement SMPP Session Load Balancing?

I tried to create a session manager.

package smpp

import (
	"errors"
	"github.com/sujit-baniya/smpp/pdu"
	"github.com/sujit-baniya/xid"
	"time"
)

type ManagerConfig struct {
	Name           string
	Auth           Auth
	Settings       Settings
	RebindDuration time.Duration
	Mode           string
	TPS            int
	MaxSession     int
}

type Manager struct {
	Name       string
	ID         string
	Session    map[string]*Session
	Config     ManagerConfig
	MaxSession int
}

func NewManager(cfg ManagerConfig) *Manager {
	if cfg.TPS == 0 {
		cfg.TPS = 10
	}
	if cfg.MaxSession == 0 {
		cfg.MaxSession = 4
	}
	return &Manager{
		Name:   cfg.Name,
		ID:     xid.New().String(),
		Config: cfg,
	}
}

func (m *Manager) Start() {
	if m.Session == nil {
		m.AddSession()
	}
	// m.balancer.balance(m.pdu)
}

func (m *Manager) AddSession(noOfSession ...int) string {
	if len(noOfSession) == 0 {
		return m.addSession()
	}
	if noOfSession[0] > m.MaxSession {
		panic(errors.New("Can't create more than allowed no of sessions."))
	}
	if (len(m.Session) + noOfSession[0]) > m.MaxSession {
		panic(errors.New("There are active sessions. Can't create more than allowed no of sessions."))
	}
	for i := 0; i < noOfSession[0]; i++ {
		m.addSession()
	}
	return ""
}

func (m *Manager) addSession() string {
	if m.Session == nil {
		m.Session = make(map[string]*Session)
	}
	session, err := NewSession(
		TRXConnector(NonTLSDialer, m.Config.Auth),
		m.Config.Settings,
		m.Config.RebindDuration,
	)
	if err != nil {
		panic(err)
	}
	m.Session[session.ID] = session
	return session.ID
}

func (m *Manager) RemoveSession(sessionId ...string) error {
	if len(sessionId) > 0 {
		return m.close(sessionId[0])
	} else {
		return m.Close()
	}
}

func (m *Manager) close(sessionId string) error {
	if !m.Session[sessionId].IsClosed() {
		err := m.Session[sessionId].close()
		if err != nil {
			return err
		}
	}
	// delete(m.Session, sessionId)
	return nil
}

func (m *Manager) Close(noOfSession ...int) error {
	if len(noOfSession) == 0 {
		for id := range m.Session {
			err := m.close(id)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (m *Manager) Rebind(sessionId ...string) {
	if len(sessionId) == 0 {
		if session, ok := m.Session[sessionId[0]]; ok {
			session.rebind()
		}
	}
}

func (m *Manager) Submit(pd pdu.PDU, sessionId ...string) error {
	session := m.getSession(sessionId...)
	if session == nil {
		return errors.New("Session not found")
	}
	return m.getSession(sessionId...).Transceiver().Submit(pd)
}

func (m *Manager) getSession(sessionId ...string) *Session {
	if len(sessionId) > 0 {
		if session, ok := m.Session[sessionId[0]]; ok {
			return session
		}
	}

	for _, session := range m.Session {
		return session
	}
	return nil
}

Can anyone suggest me how can I implement load balancing between the session?

Feature request: outgoing (send) window size

All real SMSC that I've worked with implement a mechanism for receiving a certain number of pdus without blocking the ESME until the replies are sent. The outgoing send window is a parameter when defining SMSC large account and by default is usually 1. Setting send window to a number higher than 1 help ESMEs to improve the throughput by allowing them to send a specific number of submit_sm pdus without blocking waiting for reply. If the send window gets full (meaning that ESME sent X number of submit_sms for which SMSC has not sent yet a reply), ESME will need to wait for submit_sm_resps to arrive before sending another submit_sm. In the ESME exceeds the limit set on SMSC settings, the SMSC will start replying with ESME_RSUBMITFAILED or ESME_RMSGQFUL. This mechanism acts like a sliding send window.

It would be great if the library would implement this feature to limit (by block when reached) the number of submit_sms sent without receiving a reply.

Deliver_SM UDH not always detected

I notice that UDH was not always detected correctly when deliver_sm is used. I believe the issue is when the ESM Class byte doesn't equal 0x40.

DeliverSM.go line 87
err = c.Message.Unmarshal(b, c.EsmClass == data.SM_UDH_GSM)

Can you modify DeliverSM.go to use bitwise to detect if the UDH Indicator bit is set ? Like it does for submit_sm.

SubmitSM.go line 125
err = c.Message.Unmarshal(b, (c.EsmClass&data.SM_UDH_GSM) > 0)

A quick search show that SubmitMulti.go also uses the same comparison as DeliverSM

SetLongMessageWith Encoding works not correct

Hi All.
In version 1.4.0+ cant send a long messages.

func newSubmitSM() *pdu.SubmitSM {
// build up submitSM
srcAddr := pdu.NewAddress()
srcAddr.SetTon(5)
srcAddr.SetNpi(0)
_ = srcAddr.SetAddress("xxx-xxx-xx")
destAddr := pdu.NewAddress()
destAddr.SetTon(1)
destAddr.SetNpi(1)
_ = destAddr.SetAddress("xxx-xxx-xx")
//submitSM := pdu.NewLongMessage().(*pdu.SubmitSM)
submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
submitSM.SourceAddr = srcAddr
submitSM.DestAddr = destAddr
_ = submitSM.Message.SetLongMessageWithEnc("Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text eve the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not on e centuries, but also the leap into electronic typesetting, remaining essentially unchanged.", data.UCS2)
submitSM.ProtocolID = 0
submitSM.RegisteredDelivery = 1
submitSM.ReplaceIfPresentFlag = 0
submitSM.EsmClass = 0
return submitSM
}

sends an empty message. Cant Send a sms messages with more than 160 symbols.
0-02-03-a1699370658df752053de266495cd31e2f22ca4906e459625e1b28a02d136e44_57d8b3537005ab6a

Is gosmpp thread safe?

I'm wondering when I call GetSequenceNumber, and Transceiver().Submit, are they thread safe or I should use Mutext to prevent multi access to these functions?

data.FindEncoding is not working as intended

I used following piece of code:

enc := data.FindEncoding("Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ")
fmt.Println(enc)

Above code results: &{} instead of actual encoding interface among:

var (
	// GSM7BIT is gsm-7bit encoding.
	GSM7BIT Encoding = &gsm7bit{}

	// ASCII is ascii encoding.
	ASCII Encoding = &ascii{}

	// LATIN1 encoding.
	LATIN1 Encoding = &iso88591{}

	// CYRILLIC encoding.
	CYRILLIC Encoding = &iso88595{}

	// HEBREW encoding.
	HEBREW Encoding = &iso88598{}

	// UCS2 encoding.
	UCS2 Encoding = &ucs2{}
)

Am I missing something?

The problem of getting MessageID after Submit

transmitter, err := gosmpp.NewTransmitterSession()
transmitter.Transmitter().Submit()

type TransmitSettings struct {
// WriteTimeout is timeout/deadline for submitting PDU.
WriteTimeout time.Duration

// EnquireLink periodically sends EnquireLink to SMSC.
// The duration must not be smaller than 1 minute.
//
// Zero duration disables auto enquire link.
EnquireLink time.Duration

// OnSubmitError notifies fail-to-submit PDU with along error.
OnSubmitError func(pdu.PDU, error)

// OnRebindingError notifies error while rebinding.
OnRebindingError func(error)

// OnClosed notifies `closed` event due to State.
OnClosed func(State)

}

I did n’t find any way to get Message. I need to get MessageID after submission and use it as a tag. I look forward to your answer. Thanks!

Setting DCS explicitly

I am trying to set the data coding scheme to 0xf6(246) to test SAT push messaging in a production environment. I couldn't find a way to provision the DCS explicitly for this. Could anyone please help here.. Thank you very much.

Managing messages with UDH in MessagePayload

There's an issue in ShortMessage.Unmarshal().

Sometimes SMS-C sends the message in TLV field MessagePayload. In these cases the ShortMessage length is 0.
So in the function, if the udhi is true, udh.UnmarshalBinary() returns an error.

A PR is coming (like winter ;)).

example folder is gone

hi @linxGnu,

can you provide an example for a long-lasting connection SMPP Receiver client?
I tried to check the Telco example folder, but is not in the repository anymore.

Thank you

UCS-2 using UTF-16-BE decoder issue.

in python3

>>> bytes.fromhex('60a876848d2662375f53524d53ef75284f59989d003100330039002e0032003051433002007f007f002067e58be28d2662374f59989d8be660c58bf756de590d00590045004d00583002007f007f00203010629751fb75ab60c5ff0c670d52a14e0d505cff014f7f7528624b673a84254e1a5385ff0c8db34e0d51fa62374ea48bdd8d39300167e54f59989d3001529e4e1a52a1ff0c514d6d4191cf770b75355f71300173a96e38620fff0c70b951fb00200068007400740070003a002f002f0075002e00310030003000310030002e0063006e002f006b00680064006400780020ff0c9a6c4e0a62e567093011').decode('utf-16-be')
'您的账户当前可用余额139.20元。\x7f\x7f 查询账户余额详情请回复YEMX。\x7f\x7f 【抗击疫情,服务不停!使用手机营业厅,足不出户交话费、查余额、办业务,免流量看电影、玩游戏,点击 http://u.10010.cn/khddx ,马上拥有】'

new line is an unexpected result

packages are accumulating

If problem connection then packages are accumulating
the screenshot shows that a lot of EnquireLink was sent after tcp retransmission
image

Example not working

I installed go package go get -u github.com/linxGnu/gosmpp

And used file https://github.com/linxGnu/gosmpp/blob/master/example/main.go

But running go build I'm getting following error:

main.go:10:2: case-insensitive import collision: "github.com/linxGnu/gosmpp/data" and "github.com/linxGnu/gosmpp/Data"
main.go:11:2: case-insensitive import collision: "github.com/linxGnu/gosmpp/pdu" and "github.com/linxGnu/gosmpp/PDU"

How would I fix it?

Small fix g++ and serving address

Some updates to run project

1. In the run.sh file

g++ smsc.cpp -o smsc && ./smsc
to
g++ -std=c++11 smsc.cpp -o smsc && ./smsc

in my case smsc.cpp does not compile, have 2 errors and around 20 warnings. (macos, default installed g++)

2. In file main.go change

auth := gosmpp.Auth{	
	SMSC:       "smscsim.melroselabs.com:2775", -> "localhost:2775"
        ...     

3. Is it possible to write whole server in go for testing?

Expose gosmpp.data.codings

Could you expose publicly gosmpp.data.codings? Data coding/decoding functions are needed for handling message_payload TLV. Some SMSCs accept or deliver payloads in this field instead of short_message. The coding/decoding of the payloads needs to be performed similar to short_message pdu field. It would be far more flexible to also have access to gosmpp.data.Codings.

Please check also SMPP spec: https://smpp.org/SMPP_v3_4_Issue1_2.pdf, section 5.3.2.32 for info about message_payload parameter.

Getting Error when trying to send one message

I'm trying to modify example/main.go to send one message instead of loop.

package main

import (
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/linxGnu/gosmpp"
	"github.com/linxGnu/gosmpp/data"
	"github.com/linxGnu/gosmpp/pdu"
)


func main() {
	var wg sync.WaitGroup

	wg.Add(1)
	go sendingAndReceiveSMS(&wg)

	wg.Wait()
}

func sendingAndReceiveSMS(wg *sync.WaitGroup) {
	defer wg.Done()

	auth := gosmpp.Auth{
		SMSC:       "192.168.88.250:2775",
		SystemID:   "522241",
		Password:   "UUDHWB",
		SystemType: "",
	}

	trans, err := gosmpp.NewTransceiverSession(gosmpp.NonTLSDialer, auth, gosmpp.TransceiveSettings{
		EnquireLink: 5 * time.Second,

		OnSubmitError: func(p pdu.PDU, err error) {
			log.Fatal(err)
		},

		OnReceivingError: func(err error) {
			fmt.Println(err)
		},

		OnRebindingError: func(err error) {
			fmt.Println(err)
		},

		OnPDU: handlePDU(),

		OnClosed: func(state gosmpp.State) {
			fmt.Println(state)
		},
	}, 5*time.Second)
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		_ = trans.Close()
	}()

	// sending SMS(s)
	if err = trans.Transceiver().Submit(newSubmitSM()); err != nil {
		log.Fatal(err)
	}
}

func handlePDU() func(pdu.PDU, bool) {
	return func(p pdu.PDU, responded bool) {
		switch pd := p.(type) {
		case *pdu.SubmitSMResp:
			fmt.Printf("SubmitSMResp:%+v\n", pd)

		case *pdu.GenerickNack:
			fmt.Println("GenericNack Received")

		case *pdu.EnquireLinkResp:
			fmt.Println("EnquireLinkResp Received")

		case *pdu.DataSM:
			fmt.Printf("DataSM:%+v\n", pd)

		case *pdu.DeliverSM:
			fmt.Printf("DeliverSM:%+v\n", pd)
			fmt.Println(pd.Message.GetMessage())
		}
	}
}

func newSubmitSM() *pdu.SubmitSM {
	// build up submitSM
	srcAddr := pdu.NewAddress()
	srcAddr.SetTon(5)
	srcAddr.SetNpi(0)
	_ = srcAddr.SetAddress("00" + "522241")

	destAddr := pdu.NewAddress()
	destAddr.SetTon(1)
	destAddr.SetNpi(1)
	_ = destAddr.SetAddress("99" + "522241")

	submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
	submitSM.SourceAddr = srcAddr
	submitSM.DestAddr = destAddr
	_ = submitSM.Message.SetMessageWithEncoding("Đừng buồn thế dù ngoài kia vẫn mưa nghiễng rợi tý tỵ", data.UCS2)
	submitSM.ProtocolID = 0
	submitSM.RegisteredDelivery = 1
	submitSM.ReplaceIfPresentFlag = 0
	submitSM.EsmClass = 0

	return submitSM
}

But I'm getting error
read tcp 192.168.88.248:52461->192.168.88.250:2775: use of closed network connection

How would I fix it?
Also is there any example where I can connect to SMSC once and send as many messages to different numbers?
Actually I'm new to go and little knowledge of smpp.

Submit SM with message_payload

i cant use message_payload on gosmpp. when I check on tcpdump, I found different from openSMPP and goSMPP. in openSMPP have Optional parameter payload but in goSMPP is nothing

Is it possible to detect sms encoding?

Thank you for this great library.
I am trying to implement this library along with REST.

User might send any text message with different message encoding.

Is it possible to detect the encoding?

By detecting the encoding, I wanted to clean the message content.

Some time the message content contains invisible empty characters which will change message encoding and thus message parts (length) might increase.

gosmpp.NewSession returns EOF

  • I'm testing gosmpp sample code against production SMSC and it returns EOF when calling gosmpp.NewSession using gosmpp v0.1.4-rc24
    https://github.com/linxGnu/gosmpp/edit/master/example/main.go

  • Testing the same code with local simulator (Written in Java) works fine

  • When changing to TXConnector instead of TRXConnector in production returns below error:

Binding error. Command status: [13]. Please refer to: https://github.com/linxGnu/gosmpp/blob/master/data/pkg.go for more detail about this status code

  • Previous version (0.1.13) was working fine with same production SMSC
    I have modified sample code to display error and prevent nil pointer error in case of failed NewSession:
package main

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/linxGnu/gosmpp"
	"github.com/linxGnu/gosmpp/data"
	"github.com/linxGnu/gosmpp/pdu"
)

func sendingAndReceiveSMS(wg *sync.WaitGroup, auth gosmpp.Auth, source, dest, message string) {
	defer wg.Done()

	trans, err := gosmpp.NewSession(
		gosmpp.TRXConnector(gosmpp.NonTLSDialer, auth),
		gosmpp.Settings{
			EnquireLink: 5 * time.Second,

			ReadTimeout: 10 * time.Second,

			OnSubmitError: func(_ pdu.PDU, err error) {
				fmt.Println("SubmitPDU error:", err.Error())
			},

			OnReceivingError: func(err error) {
				fmt.Println("Receiving PDU/Network error:", err.Error())
			},

			OnRebindingError: func(err error) {
				fmt.Println("Rebinding but error:", err.Error())
			},

			OnPDU: handlePDU(),

			OnClosed: func(state gosmpp.State) {
				fmt.Println(state)
			},
		}, 5*time.Second)
	if err != nil {
		fmt.Println("Error in sendingAndReceiveSMS: ", err.Error())
	} else {
		defer func() {
			_ = trans.Close()
		}()
	}

	// sending SMS(s)
	if err == nil {
		submitSMS := newSubmitSM(source, dest, message)

		if err = trans.Transceiver().Submit(submitSMS); err != nil {
			fmt.Println("Error in trans.Tranceiver(): ", err.Error())
		}
		time.Sleep(time.Second)
	}

}

func handlePDU() func(pdu.PDU, bool) {
	concatenated := map[uint8][]string{}
	return func(p pdu.PDU, _ bool) {
		switch pd := p.(type) {
		case *pdu.SubmitSMResp:
			fmt.Printf("SubmitSMResp:%+v\n", pd)

		case *pdu.GenericNack:
			fmt.Println("GenericNack Received")

		case *pdu.EnquireLinkResp:
			fmt.Println("EnquireLinkResp Received")

		case *pdu.DataSM:
			fmt.Printf("DataSM:%+v\n", pd)

		case *pdu.DeliverSM:
			fmt.Printf("DeliverSM:%+v\n", pd)
			log.Println(pd.Message.GetMessage())
			// region concatenated sms (sample code)
			message, err := pd.Message.GetMessage()
			if err != nil {
				fmt.Println("Error in pd.Message.GetMessage(): ", err.Error())
			}
			totalParts, sequence, reference, found := pd.Message.UDH().GetConcatInfo()
			if found {
				if _, ok := concatenated[reference]; !ok {
					concatenated[reference] = make([]string, totalParts)
				}
				concatenated[reference][sequence-1] = message
			}
			if !found {
				log.Println(message)
			} else if parts, ok := concatenated[reference]; ok && isConcatenatedDone(parts, totalParts) {
				log.Println(strings.Join(parts, ""))
				delete(concatenated, reference)
			}
			// endregion
		}
	}
}

func newSubmitSM(source, dest, message string) *pdu.SubmitSM {
	// build up submitSM
	srcAddr := pdu.NewAddress()
	srcAddr.SetTon(5)
	srcAddr.SetNpi(0)
	_ = srcAddr.SetAddress(source)

	destAddr := pdu.NewAddress()
	destAddr.SetTon(1)
	destAddr.SetNpi(1)
	_ = destAddr.SetAddress(dest)

	submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM)
	submitSM.SourceAddr = srcAddr
	submitSM.DestAddr = destAddr
	_ = submitSM.Message.SetMessageWithEncoding(message, data.UCS2)
	submitSM.ProtocolID = 0
	submitSM.RegisteredDelivery = 1
	submitSM.ReplaceIfPresentFlag = 0
	submitSM.EsmClass = 0

	return submitSM
}

func isConcatenatedDone(parts []string, total byte) bool {
	for _, part := range parts {
		if part != "" {
			total--
		}
	}
	return total == 0
}

Feature request: Rate limiter for ESME submit_sms

All real SMSC that I've worked with have a rate limiting policy to prevent ESME to send submit_sm too fast. If the SMSC is detecting that ESME overpasses the allowed limiting (e.g. submits/sec), it will respond with ESME_RTHROTTLED and message will not be delivered. It would be useful if the library would support out-of-the box rate limiting the number of submits sent to SMSC.

use example/main.go

go rum ./main.go
main.go:11:2: no required module provides package github.com/linxGnu/gosmpp/data;

Error returned on bind failure is unclear

In connect.go line 89, the error returned on bind failure provides only int value of the command status, and the URL provided is not correct.

I suggest to provide a better error message, and ideally a specialized error that can provide the command status received.
It can be useful for an ESME to have a different behaviour on invalid password and on system error (for example).

Handling custom runes mapping

In GSM7 official table, the character ç does not exist.
It would be great if it was possible to customize charset tables.

In my case, I could map ç to 0x09, to obtain something like this : hameÇon, instead of getting an error.

What do you think ?
Of course I can make a PR if you like !

Closing TRX or TX session doesn't send UNBIND PDU

When closing a transmitter or transceiver session, if a PDU has already been sent (EnquireLink for ex.), the session is not properly closed. In this case a i/o timeout error is returned when t.conn.Write(marshal(pdu.NewUnbind())) is called in transmitter.go.

The reason is that the previous PDU write has set the WriteDeadline through the call of t.conn.SetWriteTimeout() in write() function. So when the session is closed, the deadline is in the past when we try to write the UNBIND PDU.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.