smpp

package module
v0.0.0-...-96fae21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2023 License: MIT Imports: 12 Imported by: 0

README

SMPP 3.4 Library

Goals of this fork

github.com/Derek-meng/smpp is a fork of github.com/ajankovic/smpp with the following changes:

  • Fix Can't receive deliver_sm when Store and Forward Message Mode

smpp is library contains implementation of SMPP 3.4 protocol.

It allows easier creation of SMPP clients and servers by providing utilities for PDU and session handling.

Project Status

Although usable, project is still to be considered as WORK IN PROGRESS until it reaches it's first official version. Util then breaking API to fix possible design flaws is possible.

Project goals

  • Provide API for manipulating and observing PDU data.
  • Provide API for SMPP session handling.
  • Provide reference SMPP SMSC Server implementation.
  • Enforce SMPP specification integrity (to the most useful degree).

Feature description

  • Smpp protocol integrity is enforced with the use of session states.

  • Allow communication over tcp protocol.

  • Pdu data structure should have access to all of it's elements.

  • When client and server are connected session is created.

  • When connection is terminated or unbinding is finished session is closed.

  • Closing can be completed only after graceful handling of all remaining operations.

  • Request window is the number of sent requests during a session that are waiting for the matching response from the other side.

  • Size of the window is configurable if window is closed send should fail.

  • Sending should timeout if response is not received in configured amount of time.

  • Sender should wait for responses only for requests that have matching responses defined by the spec.

  • Client should have an option to listen for outbind requests from server.

  • Server should be able to rate limit client's requests.

  • Session should handle sequence numbers.

  • Provide logging for critical paths.

  • Sessions should be uniquely identifiable.

  • Helpers for sending enquire_link in regular intervals.

  • If an SMPP entity receives an unrecognized PDU/command, it must return a generic_nack PDU indicating an invalid command_id in the command_status field of the header.

  • Provide stats about running session(s):

    • Open sessions
    • Type of sessions
    • Session send window size
    • Session receive window size
    • Session running time
    • Average send/response times
    • Rate of failures
  • Support all PDU commands defined by the specification.

  • Helper functions for common tasks.

Installation

You can use go get:

go get -u github.com/Derek-meng/smpp

Usage

In order to do any kind of interaction you first need to create an SMPP Session. Session is the main carrier of the protocol and enforcer of the specification rules.

Naked session can be created with:

// You must provide already established connection and configuration struct.
sess := smpp.NewSession(conn, conf)

But it's much more convenient to use helpers that would do the binding with the remote SMSC and return you session prepared for sending:

// Bind with remote server by providing config structs.
sess, err := smpp.BindTRx(sessConf, bindConf)

And once you have the session it can be used for sending PDUs to the binded peer.

sm := smpp.SubmitSm{
    SourceAddr:      "11111111",
    DestinationAddr: "22222222",
    ShortMessage:    "Hello from SMPP!",
}
// Session can then be used for sending PDUs.
resp, err := sess.Send(p)

Session that is no longer used must be closed:

sess.Close()

If you want to handle incoming requests to the session specify SMPPHandler in session configuration when creating new session similarly to HTTPHandler from net/http package:

conf := smpp.SessionConf{
    Handler: smpp.HandlerFunc(func(ctx *smpp.Context) {
        switch ctx.CommandID() {
        case pdu.UnbindID:
            ubd, err := ctx.Unbind()
            if err != nil {
                t.Errorf(err.Error())
            }
            resp := ubd.Response()
            if err := ctx.Respond(resp, pdu.StatusOK); err != nil {
                t.Errorf(err.Error())
            }
        }
    }),
}

Documentation

Overview

Package smpp implements SMPP protocol v3.4.

It allows easier creation of SMPP clients and servers by providing utilities for PDU and session handling. In order to do any kind of interaction you first need to create an SMPP Session(https://godoc.org/github.com/Derek-meng/smpp#Session). Session is the main carrier of the protocol and enforcer of the specification rules.

Naked session can be created with:

// You must provide already established connection and configuration struct.
sess := smpp.NewSession(conn, conf)

But it's much more convenient to use helpers that would do the binding with the remote SMSC and return you session prepared for sending:

// Bind with remote server by providing config structs.
sess, err := smpp.BindTRx(sessConf, bindConf)

And once you have the session it can be used for sending PDUs to the binded peer.

sm := smpp.SubmitSm{
    SourceAddr:      "11111111",
    DestinationAddr: "22222222",
    ShortMessage:    "Hello from SMPP!",
}
// Session can then be used for sending PDUs.
resp, err := sess.Send(p)

Session that is no longer used must be closed:

sess.Close()

If you want to handle incoming requests to the session specify SMPPHandler in session configuration when creating new session similarly to HTTPHandler from _net/http_ package:

conf := smpp.SessionConf{
    Handler: smpp.HandlerFunc(func(ctx *smpp.Context) {
        switch ctx.CommandID() {
        case pdu.UnbindID:
            ubd, err := ctx.Unbind()
            if err != nil {
                t.Errorf(err.Error())
            }
            resp := ubd.Response()
            if err := ctx.Respond(resp, pdu.StatusOK); err != nil {
                t.Errorf(err.Error())
            }
        }
    }),
}

Detailed examples for SMPP client and server can be found in the examples dir.

Index

Constants

View Source
const (
	// Version of the supported SMPP Protocol. Only supporting 3.4 for now.
	Version = 0x34
	// SequenceStart is the starting reference for sequence number.
	SequenceStart = 0x00000001
	// SequenceEnd s sequence number upper boundary.
	SequenceEnd = 0x7FFFFFFF
)

Variables

View Source
var SessionClosedBeforeReceiving error = sessionClosedBeforeReceiving{}

Functions

func SendAlertNotification

func SendAlertNotification(ctx context.Context, sess *Session, p *pdu.AlertNotification) error

SendAlertNotification is a helper function for sending AlertNotification PDU.

func SendBindRx

func SendBindRx(ctx context.Context, sess *Session, p *pdu.BindRx) (*pdu.BindRxResp, error)

SendBindRx is a helper function for sending BindRx PDU.

func SendBindRxResp

func SendBindRxResp(ctx context.Context, sess *Session, p *pdu.BindRxResp) error

SendBindRxResp is a helper function for sending BindRxResp PDU.

func SendBindTRx

func SendBindTRx(ctx context.Context, sess *Session, p *pdu.BindTRx) (*pdu.BindTRxResp, error)

SendBindTRx is a helper function for sending BindTRx PDU.

func SendBindTRxResp

func SendBindTRxResp(ctx context.Context, sess *Session, p *pdu.BindTRxResp) error

SendBindTRxResp is a helper function for sending BindTRxResp PDU.

func SendBindTx

func SendBindTx(ctx context.Context, sess *Session, p *pdu.BindTx) (*pdu.BindTxResp, error)

SendBindTx is a helper function for sending BindTx PDU.

func SendBindTxResp

func SendBindTxResp(ctx context.Context, sess *Session, p *pdu.BindTxResp) error

SendBindTxResp is a helper function for sending BindTxResp PDU.

func SendCancelSm

func SendCancelSm(ctx context.Context, sess *Session, p *pdu.CancelSm) (*pdu.CancelSmResp, error)

SendCancelSm is a helper function for sending CancelSm PDU.

func SendCancelSmResp

func SendCancelSmResp(ctx context.Context, sess *Session, p *pdu.CancelSmResp) error

SendCancelSmResp is a helper function for sending CancelSmResp PDU.

func SendDataSm

func SendDataSm(ctx context.Context, sess *Session, p *pdu.DataSm) (*pdu.DataSmResp, error)

SendDataSm is a helper function for sending DataSm PDU.

func SendDataSmResp

func SendDataSmResp(ctx context.Context, sess *Session, p *pdu.DataSmResp) error

SendDataSmResp is a helper function for sending DataSmResp PDU.

func SendDeliverSm

func SendDeliverSm(ctx context.Context, sess *Session, p *pdu.DeliverSm) (*pdu.DeliverSmResp, error)

SendDeliverSm is a helper function for sending DeliverSm PDU.

func SendDeliverSmResp

func SendDeliverSmResp(ctx context.Context, sess *Session, p *pdu.DeliverSmResp) error

SendDeliverSmResp is a helper function for sending DeliverSmResp PDU.

func SendEnquireLink(ctx context.Context, sess *Session, p *pdu.EnquireLink) (*pdu.EnquireLinkResp, error)

SendEnquireLink is a helper function for sending EnquireLink PDU.

func SendEnquireLinkResp

func SendEnquireLinkResp(ctx context.Context, sess *Session, p *pdu.EnquireLinkResp) error

SendEnquireLinkResp is a helper function for sending EnquireLinkResp PDU.

func SendGenericNack

func SendGenericNack(ctx context.Context, sess *Session, p *pdu.GenericNack) error

SendGenericNack is a helper function for sending GenericNack PDU.

func SendOutbind

func SendOutbind(ctx context.Context, sess *Session, p *pdu.Outbind) error

SendOutbind is a helper function for sending Outbind PDU.

func SendQuerySm

func SendQuerySm(ctx context.Context, sess *Session, p *pdu.QuerySm) (*pdu.QuerySmResp, error)

SendQuerySm is a helper function for sending QuerySm PDU.

func SendQuerySmResp

func SendQuerySmResp(ctx context.Context, sess *Session, p *pdu.QuerySmResp) error

SendQuerySmResp is a helper function for sending QuerySmResp PDU.

func SendReplaceSm

func SendReplaceSm(ctx context.Context, sess *Session, p *pdu.ReplaceSm) (*pdu.ReplaceSmResp, error)

SendReplaceSm is a helper function for sending ReplaceSm PDU.

func SendReplaceSmResp

func SendReplaceSmResp(ctx context.Context, sess *Session, p *pdu.ReplaceSmResp) error

SendReplaceSmResp is a helper function for sending ReplaceSmResp PDU.

func SendSubmitMulti

func SendSubmitMulti(ctx context.Context, sess *Session, p *pdu.SubmitMulti) (*pdu.SubmitMultiResp, error)

SendSubmitMulti is a helper function for sending SubmitMulti PDU.

func SendSubmitMultiResp

func SendSubmitMultiResp(ctx context.Context, sess *Session, p *pdu.SubmitMultiResp) error

SendSubmitMultiResp is a helper function for sending SubmitMultiResp PDU.

func SendSubmitSm

func SendSubmitSm(ctx context.Context, sess *Session, p *pdu.SubmitSm) (*pdu.SubmitSmResp, error)

SendSubmitSm is a helper function for sending SubmitSm PDU.

func SendSubmitSmResp

func SendSubmitSmResp(ctx context.Context, sess *Session, p *pdu.SubmitSmResp) error

SendSubmitSmResp is a helper function for sending SubmitSmResp PDU.

func SendUnbind

func SendUnbind(ctx context.Context, sess *Session, p *pdu.Unbind) (*pdu.UnbindResp, error)

SendUnbind is a helper function for sending Unbind PDU.

func SendUnbindResp

func SendUnbindResp(ctx context.Context, sess *Session, p *pdu.UnbindResp) error

SendUnbindResp is a helper function for sending UnbindResp PDU.

func Unbind

func Unbind(ctx context.Context, sess *Session) error

Unbind session will initiate session unbinding and close the session. First it will try to notify peer with unbind request. If there was any error during unbinding an error will be returned. Session will be closed even if there was an error during unbind.

Types

type BindConf

type BindConf struct {
	// Bind will be attempted to this addr.
	Addr string
	// Mandatory fields for binding PDU.
	SystemID   string
	Password   string
	SystemType string
	AddrTon    int
	AddrNpi    int
	AddrRange  string
}

BindConf is the configuration for binding to smpp servers.

type Context

type Context struct {
	Sess *Session
	// contains filtered or unexported fields
}

Context represents container for SMPP request related information.

func (*Context) AlertNotification

func (ctx *Context) AlertNotification() (*pdu.AlertNotification, error)

AlertNotification returns generic request PDU as pdu.AlertNotification.

func (*Context) BindRx

func (ctx *Context) BindRx() (*pdu.BindRx, error)

BindRx returns generic request PDU as pdu.BindRx.

func (*Context) BindRxResp

func (ctx *Context) BindRxResp() (*pdu.BindRxResp, error)

BindRxResp returns generic request PDU as pdu.BindRxResp.

func (*Context) BindTRx

func (ctx *Context) BindTRx() (*pdu.BindTRx, error)

BindTRx returns generic request PDU as pdu.BindTRx.

func (*Context) BindTRxResp

func (ctx *Context) BindTRxResp() (*pdu.BindTRxResp, error)

BindTRxResp returns generic request PDU as pdu.BindTRxResp.

func (*Context) BindTx

func (ctx *Context) BindTx() (*pdu.BindTx, error)

BindTx returns generic request PDU as pdu.BindTx.

func (*Context) BindTxResp

func (ctx *Context) BindTxResp() (*pdu.BindTxResp, error)

BindTxResp returns generic request PDU as pdu.BindTxResp.

func (*Context) CancelSm

func (ctx *Context) CancelSm() (*pdu.CancelSm, error)

CancelSm returns generic request PDU as pdu.CancelSm.

func (*Context) CancelSmResp

func (ctx *Context) CancelSmResp() (*pdu.CancelSmResp, error)

CancelSmResp returns generic request PDU as pdu.CancelSmResp.

func (*Context) CloseSession

func (ctx *Context) CloseSession()

CloseSession will initiate session shutdown after handler returns.

func (*Context) CommandID

func (ctx *Context) CommandID() pdu.CommandID

CommandID returns ID of the PDU request.

func (*Context) Context

func (ctx *Context) Context() context.Context

Context returns Go standard library context.

func (*Context) DataSm

func (ctx *Context) DataSm() (*pdu.DataSm, error)

DataSm returns generic request PDU as pdu.DataSm.

func (*Context) DataSmResp

func (ctx *Context) DataSmResp() (*pdu.DataSmResp, error)

DataSmResp returns generic request PDU as pdu.DataSmResp.

func (*Context) DeliverSm

func (ctx *Context) DeliverSm() (*pdu.DeliverSm, error)

DeliverSm returns generic request PDU as pdu.DeliverSm.

func (*Context) DeliverSmResp

func (ctx *Context) DeliverSmResp() (*pdu.DeliverSmResp, error)

DeliverSmResp returns generic request PDU as pdu.DeliverSmResp.

func (ctx *Context) EnquireLink() (*pdu.EnquireLink, error)

EnquireLink returns generic request PDU as pdu.EnquireLink.

func (*Context) EnquireLinkResp

func (ctx *Context) EnquireLinkResp() (*pdu.EnquireLinkResp, error)

EnquireLinkResp returns generic request PDU as pdu.EnquireLinkResp.

func (*Context) GenericNack

func (ctx *Context) GenericNack() (*pdu.GenericNack, error)

GenericNack returns generic request PDU as pdu.GenericNack.

func (*Context) Outbind

func (ctx *Context) Outbind() (*pdu.Outbind, error)

Outbind returns generic request PDU as pdu.Outbind.

func (*Context) QuerySm

func (ctx *Context) QuerySm() (*pdu.QuerySm, error)

QuerySm returns generic request PDU as pdu.QuerySm.

func (*Context) QuerySmResp

func (ctx *Context) QuerySmResp() (*pdu.QuerySmResp, error)

QuerySmResp returns generic request PDU as pdu.QuerySmResp.

func (*Context) RemoteAddr

func (ctx *Context) RemoteAddr() string

RemoteAddr returns IP address of the bounded peer.

func (*Context) ReplaceSm

func (ctx *Context) ReplaceSm() (*pdu.ReplaceSm, error)

ReplaceSm returns generic request PDU as pdu.ReplaceSm.

func (*Context) ReplaceSmResp

func (ctx *Context) ReplaceSmResp() (*pdu.ReplaceSmResp, error)

ReplaceSmResp returns generic request PDU as pdu.ReplaceSmResp.

func (*Context) Respond

func (ctx *Context) Respond(resp pdu.PDU, status pdu.Status) error

Respond sends pdu to the bounded peer.

func (*Context) SessionID

func (ctx *Context) SessionID() string

SessionID returns ID of the session that this context is responsible for handling this request.

func (*Context) Status

func (ctx *Context) Status() pdu.Status

Status returns status of the current request.

func (*Context) SubmitMulti

func (ctx *Context) SubmitMulti() (*pdu.SubmitMulti, error)

SubmitMulti returns generic request PDU as pdu.SubmitMulti.

func (*Context) SubmitMultiResp

func (ctx *Context) SubmitMultiResp() (*pdu.SubmitMultiResp, error)

SubmitMultiResp returns generic request PDU as pdu.SubmitMultiResp.

func (*Context) SubmitSm

func (ctx *Context) SubmitSm() (*pdu.SubmitSm, error)

SubmitSm returns generic request PDU as pdu.SubmitSm.

func (*Context) SubmitSmResp

func (ctx *Context) SubmitSmResp() (*pdu.SubmitSmResp, error)

SubmitSmResp returns generic request PDU as pdu.SubmitSmResp.

func (*Context) SystemID

func (ctx *Context) SystemID() string

SystemID returns SystemID of the bounded peer that request came from.

func (*Context) Unbind

func (ctx *Context) Unbind() (*pdu.Unbind, error)

Unbind returns generic request PDU as pdu.Unbind.

func (*Context) UnbindResp

func (ctx *Context) UnbindResp() (*pdu.UnbindResp, error)

UnbindResp returns generic request PDU as pdu.UnbindResp.

type DefaultLogger

type DefaultLogger struct{}

DefaultLogger prints logs if smpp.logs flag is set.

func (DefaultLogger) ErrorF

func (dl DefaultLogger) ErrorF(msg string, params ...interface{})

ErrorF implements Logger interface.

func (DefaultLogger) InfoF

func (dl DefaultLogger) InfoF(msg string, params ...interface{})

InfoF implements Logger interface.

type Error

type Error struct {
	Msg  string
	Temp bool
}

Error implements Error and Temporary interfaces.

func (Error) Error

func (e Error) Error() string

func (Error) Temporary

func (e Error) Temporary() bool

Temporary implements Temporary interface.

type Handler

type Handler interface {
	ServeSMPP(ctx *Context)
}

Handler handles smpp requests.

type HandlerFunc

type HandlerFunc func(ctx *Context)

HandlerFunc wraps func into Handler.

func (HandlerFunc) ServeSMPP

func (hc HandlerFunc) ServeSMPP(ctx *Context)

ServeSMPP implements Handler interface.

type Logger

type Logger interface {
	InfoF(msg string, params ...interface{})
	ErrorF(msg string, params ...interface{})
}

Logger provides logging interface for getting info about internals of smpp package.

type RemoteAddresser

type RemoteAddresser interface {
	RemoteAddr() net.Addr
}

RemoteAddresser is an abstraction to keep Session from depending on network connection.

type Server

type Server struct {
	Addr        string
	SessionConf *SessionConf
	// contains filtered or unexported fields
}

Server implements SMPP SMSC server.

func NewServer

func NewServer(addr string, conf SessionConf) *Server

NewServer creates new SMPP server for managing SMSC sessions. Sessions will use provided SessionConf as template configuration.

func (*Server) Close

func (srv *Server) Close() error

Close implements closer interface.

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe() error

ListenAndServe starts server listening. Blocking function.

func (*Server) Serve

func (srv *Server) Serve(ln net.Listener) error

Serve accepts incoming connections and starts SMPP sessions.

func (*Server) Unbind

func (srv *Server) Unbind(ctx context.Context) error

Unbind gracefully closes server by sending Unbind requests to all connected peers.

type Session

type Session struct {
	RWC io.ReadWriteCloser
	// contains filtered or unexported fields
}

Session is the engine that coordinates SMPP protocol for bounded peers.

func BindRx

func BindRx(sc SessionConf, bc BindConf) (*Session, error)

BindRx binds receiver session.

func BindTRx

func BindTRx(sc SessionConf, bc BindConf) (*Session, error)

BindTRx binds transreceiver session.

func BindTx

func BindTx(sc SessionConf, bc BindConf) (*Session, error)

BindTx binds transmitter session.

func NewSession

func NewSession(rwc io.ReadWriteCloser, conf SessionConf) *Session

NewSession creates new SMPP session and starts goroutine for listening incoming requests so make sure to call Session.Close() after you are done using it to avoid goroutine leak. Session will take ownership of the ReadWriteCloser and call Close on it during shutdown.

func (*Session) Close

func (sess *Session) Close() error

Close implements Closer interface. It MUST be called to dispose session cleanly. It gracefully waits for all handlers to finish execution before returning.

func (*Session) ID

func (sess *Session) ID() string

ID uniquely identifies the session.

func (*Session) NotifyClosed

func (sess *Session) NotifyClosed() <-chan struct{}

NotifyClosed provides channel that will be closed once session enters closed state.

func (*Session) Send

func (sess *Session) Send(ctx context.Context, req pdu.PDU) (pdu.PDU, error)

Send writes PDU to the bounded connection effectively sending it to the peer. Use context deadline to specify how much you would like to wait for the response.

func (*Session) String

func (sess *Session) String() string

func (*Session) SystemID

func (sess *Session) SystemID() string

SystemID identifies connected peer.

type SessionConf

type SessionConf struct {
	Type          SessionType
	SendWinSize   int
	ReqWinSize    int
	WindowTimeout time.Duration
	SessionState  func(sessionID, systemID string, state SessionState)
	SystemID      string
	ID            string
	Logger        Logger
	Handler       Handler
	Sequencer     pdu.Sequencer
	// MapResetInterval specifies the duration after which the session's map will be recreated
	// to mitigate potential memory growth. Setting this to a positive duration can help
	// manage memory usage, especially when large amounts of data are added and removed from the map.
	MapResetInterval time.Duration
}

SessionConf structured session configuration.

type SessionState

type SessionState int

SessionState describes session state.

const (
	// StateOpen is the initial session state.
	StateOpen SessionState = iota
	// StateBinding session has started binding process.
	// All communication will be blocked until session is bound.
	StateBinding
	// StateBoundTx session is bound as transmitter.
	StateBoundTx
	// StateBoundRx session is bound as receiver.
	StateBoundRx
	// StateBoundTRx session is bound as transceiver.
	StateBoundTRx
	// StateUnbinding session has started unbinding process.
	// Prevents any communication until unbinding is finished.
	StateUnbinding
	// StateClosing session is gracefully closing.
	StateClosing
	// StateClosed session is closed.
	StateClosed
)

func (SessionState) String

func (i SessionState) String() string

type SessionType

type SessionType int

SessionType defines if session is ESME or SMSC. In other words it defines if the session will behave like a client or like a server.

const (
	// ESME type of the session.
	ESME SessionType = iota
	// SMSC type of the session.
	SMSC
)

func (SessionType) String

func (i SessionType) String() string

type StatusError

type StatusError struct {
	// contains filtered or unexported fields
}

StatusError implements error interface for SMPP status errors.

func (StatusError) Error

func (se StatusError) Error() string

Error implements error interface.

func (StatusError) Status

func (se StatusError) Status() pdu.Status

Status returns PDU status code of the error.

Directories

Path Synopsis
examples
internal
mock
Package mock implements necessary mocking structures to allow easier testing for the smpp package.
Package mock implements necessary mocking structures to allow easier testing for the smpp package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL