better-smpp
Server

Overview

SmppServer — accept ESME connections, authenticate binds, deliver messages.

SmppServer from @better-smpp/server is the SMSC side of SMPP: it accepts client connections over TCP or TLS, authenticates binds through a hook, and hands each connection to you as a typed SmppServerSession.

import { SmppServer } from "@better-smpp/server";
import { randomUUID } from "node:crypto";

const server = new SmppServer({
  systemId: "MYSMSC",
  onBind: (session, pdu) => authenticate(pdu.systemId, pdu.password),
});

server.on("session", (session) => {
  session.on("submit_sm", async (pdu) => {
    await session.respond(pdu, { messageId: randomUUID() });
  });
});

const port = await server.listen({ port: 2775 });

Options

OptionDefaultDescription
onBindaccept allAsync bind authentication hook — see Authentication
systemId""system_id reported in bind responses
interfaceVersionattach scInterfaceVersion TLV to bind responses (e.g. 0x50)
tlsoffnode:tls server options (key, cert, …)
proxyProtocoloffaccept PROXY protocol v1/v2 headers — see TLS & PROXY
enquireLinkoffkeepalive toward clients, with dead-link detection
windowSize / windowQueueLimitunlimitedcap outstanding server-initiated requests per session
requestTimeoutMs30000default timeout for server-initiated requests
registrystandardSmppRegistry with custom commands/TLVs
defaultEncoding / maxPduLength"ASCII" / 16384codec settings
logger / metricsno-opobservability hooks

Lifecycle

const port = await server.listen({ port: 2775, host: "0.0.0.0" });
server.address();  // bound AddressInfo
server.sessions;   // Set<SmppServerSession> of live sessions
await server.close(); // stop listening, destroy all sessions

Server events: session (a client connected, after TLS handshake and PROXY header), listening, error, close.

Automatic behaviors — you never handle these yourself:

  • inbound enquire_link is answered,
  • unknown command ids are answered with generic_nack (ESME_RINVCMDID),
  • malformed PDU bodies are nacked with the appropriate status without killing the connection,
  • unbind is acknowledged and the session state moves to UNBOUND,
  • a second bind on a bound session is rejected with ESME_RALYBND.

On this page