Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

smpp

Package Overview
Dependencies
Maintainers
2
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

smpp - npm Package Compare versions

Comparing version 0.6.0-rc.0 to 0.6.0-rc.1

14

lib/defs.js

@@ -50,10 +50,10 @@ var iconv = require('iconv-lite'),

write: function(value, buffer, offset) {
if (!Buffer.isBuffer(value)) {
value = Buffer.from(String(value), 'ascii');
}
buffer.writeUInt8(value.length, offset++);
if (typeof value == 'string') {
value = Buffer.from(value, 'ascii');
}
value.copy(buffer, offset);
},
size: function(value) {
return value.length + 1;
return (value.length || String(value).length) + 1;
},

@@ -71,4 +71,4 @@ default: ''

write: function(value, buffer, offset) {
if (typeof value == 'string') {
value = Buffer.from(value, 'ascii');
if (!Buffer.isBuffer(value)) {
value = Buffer.from(String(value), 'ascii');
}

@@ -79,3 +79,3 @@ value.copy(buffer, offset);

size: function(value) {
return value.length + 1;
return (value.length || String(value).length) + 1;
},

@@ -82,0 +82,0 @@ default: ''

@@ -7,4 +7,14 @@ var net = require('net'),

PDU = require('./pdu').PDU,
EventEmitter = require('events').EventEmitter;
EventEmitter = require('events').EventEmitter,
proxy = require("findhit-proxywrap").proxy;
var proxyTransport = proxy(net, {
strict: false,
ignoreStrictExceptions: true
});
var proxyTlsTransport = proxy(tls, {
strict: false,
ignoreStrictExceptions: true
});
function Session(options) {

@@ -14,3 +24,3 @@ EventEmitter.call(this);

var self = this;
var transport = net;
var clientTransport = net;
var connectTimeout;

@@ -22,3 +32,4 @@ this._extractPDUs = this._extractPDUs.bind(self);

this.remoteAddress = null;
this._proxyProtocolChecked = false;
this.remotePort = null;
this.proxyProtocolProxy = null;
this._busy = false;

@@ -39,17 +50,20 @@ this._callbacks = {};

if (options.tls) {
transport = tls;
clientTransport = tls;
}
connectTimeout = setTimeout(function() {
if (self.socket) {
var e = new Error("Timeout of " + options.connectTimeout + "ms while connecting to " +
self.options.host + ":" + self.options.port);
e.code = "ETIMEOUT";
e.timeout = options.connectTimeout;
self.socket.destroy(e);
}
}, options.connectTimeout);
this.socket = transport.connect(this.options);
if (options.hasOwnProperty("connectTimeout") && options.connectTimeout>0) {
connectTimeout = setTimeout(function () {
if (self.socket) {
var e = new Error("Timeout of " + options.connectTimeout + "ms while connecting to " +
self.options.host + ":" + self.options.port);
e.code = "ETIMEOUT";
e.timeout = options.connectTimeout;
self.socket.destroy(e);
}
}, options.connectTimeout);
}
this.socket = clientTransport.connect(this.options);
this.socket.on('connect', (function() {
clearTimeout(connectTimeout);
self.remoteAddress = self.socket.remoteAddress;
self.remoteAddress = self.rootSocket().remoteAddress;
self.remotePort = self.rootSocket().remotePort;
self.debug("server.connected", "connected to server", {secure: options.tls});

@@ -67,6 +81,2 @@ self.emit('connect'); // @todo should emmit the session, but it would break BC

}
this.remoteAddress = this.socket.remoteAddress;
this.socket.on('open', function() {
self.debug("socket.open");
});
this.socket.on('readable', function() {

@@ -103,2 +113,9 @@ if ( (self.socket.bytesRead - self._prevBytesRead) > 0 ) {

});
this.rootSocket = (function() {
if (self.socket._parent) return self.socket._parent;
return self.socket;
});
this.remoteAddress = this.rootSocket().remoteAddress;
this.remotePort = this.rootSocket().remotePort;
this.proxyProtocolProxy = this.rootSocket().proxyAddress ? { address: this.rootSocket().proxyAddress, port: this.rootSocket().proxyPort } : false;
}

@@ -133,2 +150,5 @@

}
if (this.options.debugListener instanceof Function) {
this.options.debugListener(type, msg, payload);
}
this.emit('debug', type, msg, payload);

@@ -153,6 +173,2 @@ }

try {
if(this._mode === "server" && this.options.enable_proxy_protocol_detection && !this._proxyProtocolChecked) {
this._handleProxyProtocolV1();
this._proxyProtocolChecked = true;
}
if(!this._command_length) {

@@ -184,49 +200,3 @@ this._command_length = PDU.commandLength(this.socket);

Session.prototype._handleProxyProtocolV1 = function() {
var proxyBuffer = this.socket.read(6);
if (!proxyBuffer) {
return;
}
// Header specs: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
if (proxyBuffer.toString() === 'PROXY ') {
var proxyProtocol = proxyBuffer.toString();
var char = null;
var b = null;
var prevChar = null;
while (b = this.socket.read(1)) {
char = b.toString('ascii');
proxyProtocol += char;
if (char === "\n" && prevChar === "\r") break;
if (proxyProtocol.length > 108) {
this.debug("proxy_protocol.error", "Proxy protocol header cannot exceed 108 bytes", proxyProtocol);
var e = new Error('Proxy protocol header cannot exceed 108 bytes');
e.code = "PROXY_PROTOCOL_HEADER_TOO_LARGE";
throw e;
}
prevChar = char;
}
this.debug("proxy_protocol.header.decoded", "Decoded", proxyProtocol.split("\r").join("").split("\n").join(""));
var proxyProtocolAddressFound = null;
if (proxyProtocol.indexOf('PROXY TCP') === 0) { // 'PROXY TCP(4|6) .+\r\n'
var proxyProtocolParts = proxyProtocol.substring(10).split(' ');
if (proxyProtocolParts.length>1) proxyProtocolAddressFound = proxyProtocolParts[1];
}
if (proxyProtocolAddressFound) {
this.remoteAddress = proxyProtocolAddressFound.toLowerCase();
this.debug("proxy_protocol.address.ok", "Found "+this.remoteAddress, this.remoteAddress);
} else {
this.debug("proxy_protocol.address.ko", "Not found");
}
} else {
this.debug("proxy_protocol.header.error","Header mismatch");
this.socket.unshift(proxyBuffer);
}
}
Session.prototype.send = function(pdu, responseCallback, sendCallback) {

@@ -313,4 +283,6 @@ if (!this.socket.writable) {

function Server(options, listener) {
var self = this;
var self = this,
transport;
this.sessions = [];
this.isProxiedServer = options.isProxiedServer == true;

@@ -329,9 +301,37 @@ if (typeof options == 'function') {

this.tls = options.key && options.cert;
var transport = this.tls ? tls : net;
options.tls = this.tls != null; // standarized option for the session on both client & server
this.options = options;
self.on("proxiedConnection", function(socket) {
// The connection has successfully passed through the proxied server (event emitted by proxywrap)
socket.proxiedConnection = true;
});
// Fetch the right transport based on the current options
if (this.isProxiedServer) {
transport = this.tls ? proxyTlsTransport : proxyTransport;
} else {
transport = this.tls ? tls : net;
}
transport.Server.call(this, options, function(socket) {
var session = new Session({socket: socket, enable_proxy_protocol_detection: self.options.enable_proxy_protocol_detection, debug: self.options.debug});
session.debug("client.connected", "client has connected");
session.server = self;
var session = new Session({
socket: socket,
tls: self.options.tls,
debug: self.options.debug,
debugListener: self.options.debugListener || null
});
if (socket.savedEmit) {
// Restore the saved emit to fix the proxywrap bug (on nodejs <=8)
socket.emit = socket.savedEmit;
socket.savedEmit = null;
}
session.debug("client.connected", "client has connected", {
secure: self.options.tls,
// Useful information for Proxy protocol debugging & testing
proxiedServer: self.isProxiedServer,
proxiedConnection: socket.proxiedConnection || (socket._parent ? socket._parent.proxiedConnection : false) || false,
remoteAddress: session.remoteAddress,
remotePort: session.remotePort,
proxyProtocolProxy: session.proxyProtocolProxy,
});
self.sessions.push(session);

@@ -343,6 +343,22 @@ socket.on('close', function() {

});
if (this.isProxiedServer) {
// The proxied wrapper clears all connection listeners and adds their own.
// A new listener is added in order to catch socket error on the wrapper.
self.on("connection", function (socket) {
socket.on("error", function (e) {
self.emit("error", e);
});
if (self.options.autoPrependBuffer) {
// Allows to automatically prepend a buffer on the client socket. This feature is intended only for
// testing purposes and it's used to inject client simulated headers (Proxy protocol)
socket.unshift(self.options.autoPrependBuffer);
}
// There's a bug in the proxywrap server which tampers the emit method in nodejs <= 8 and makes the
// socket unable to emit the events. As a simple fix, save the emit method so it can be restored later.
socket.savedEmit = socket.emit;
});
}
}
util.inherits(Server, net.Server);
function SecureServer(options, listener) {

@@ -352,16 +368,20 @@ Server.call(this, options, listener);

function ProxyServer(options, listener) {
options.isProxiedServer = true;
Server.call(this, options, listener);
}
function ProxySecureServer(options, listener) {
options.isProxiedServer = true;
Server.call(this, options, listener);
}
// Standard servers without proxy protocol support
util.inherits(Server, net.Server);
util.inherits(SecureServer, tls.Server);
SecureServer.prototype.listen = Server.prototype.listen = function() {
var args = [this.tls ? 3550 : 2775];
if (typeof arguments[0] == 'function') {
args[1] = arguments[0];
} else if (arguments.length > 0) {
args = arguments;
}
// Servers with proxy protocol support
util.inherits(ProxyServer, proxyTransport.Server);
util.inherits(ProxySecureServer, proxyTlsTransport.Server);
var transport = this.tls ? tls : net;
return transport.Server.prototype.listen.apply(this, args);
};
exports.createServer = function(options, listener) {

@@ -376,39 +396,50 @@ if (typeof options == 'function') {

if (options.key && options.cert) {
return new SecureServer(options, listener);
if (options.enable_proxy_protocol_detection) {
return new ProxySecureServer(options, listener);
} else {
return new SecureServer(options, listener);
}
} else {
if (options.enable_proxy_protocol_detection) {
return new ProxyServer(options, listener);
} else {
return new Server(options, listener);
}
}
return new Server(options, listener);
};
exports.connect = exports.createSession = function(url, listener) {
var options = {};
exports.connect = exports.createSession = function(options, listener) {
var clientOptions = {};
if (arguments.length > 1 && typeof listener != 'function') {
options = {
host: url,
clientOptions = {
host: options,
port: listener
};
listener = arguments[3];
} else if (typeof url == 'string') {
options = parse(url);
options.host = options.slashes ? options.hostname : url;
options.tls = options.protocol === 'ssmpp:';
} else if (typeof url == 'function') {
listener = url;
} else if (typeof options == 'string') {
clientOptions = parse(options);
clientOptions.host = clientOptions.slashes ? clientOptions.hostname : options;
clientOptions.tls = clientOptions.protocol === 'ssmpp:';
} else if (typeof options == 'function') {
listener = options;
} else {
options = url || {};
if (options.url) {
url = parse(options.url);
options.host = url.hostname;
options.port = url.port;
options.tls = url.protocol === 'ssmpp:';
clientOptions = options || {};
if (clientOptions.url) {
options = parse(clientOptions.url);
clientOptions.host = options.hostname;
clientOptions.port = options.port;
clientOptions.tls = options.protocol === 'ssmpp:';
}
}
options.port = options.port || (options.tls ? 3550 : 2775);
options.debug = options.debug || false;
options.connectTimeout = options.connectTimeout || 30000;
if (clientOptions.tls && !clientOptions.hasOwnProperty("rejectUnauthorized")) {
clientOptions.rejectUnauthorized = false; // Allow self signed certificates by default
}
clientOptions.port = clientOptions.port || (clientOptions.tls ? 3550 : 2775);
clientOptions.debug = clientOptions.debug || false;
clientOptions.connectTimeout = clientOptions.connectTimeout || 30000;
var session = new Session(options);
var session = new Session(clientOptions);
if (listener) {
session.on(options.tls ? 'secureConnect' : 'connect', function() {
session.on(clientOptions.tls ? 'secureConnect' : 'connect', function() {
listener(session);

@@ -415,0 +446,0 @@ });

{
"name": "smpp",
"version": "0.6.0-rc.0",
"version": "0.6.0-rc.1",
"description": "SMPP client and server implementation in node.js",

@@ -34,2 +34,3 @@ "homepage": "https://github.com/farhadi/node-smpp",

"dependencies": {
"findhit-proxywrap": "^0.3.12",
"iconv-lite": "0.x",

@@ -36,0 +37,0 @@ "safer-buffer": ">= 2.1.2 < 3"

@@ -1,3 +0,3 @@

node-smpp
=========
# node-smpp
SMPP client and server implementation in node.js.

@@ -10,4 +10,4 @@

Introduction
------------
## Introduction
This is a complete implementation of SMPP v5.0 in node.js, with support for

@@ -25,9 +25,10 @@ custom commands and TLVs.

Installation
------------
## Installation
npm install smpp
```
npm install smpp
```
Usage
-----
## Usage
### Creating a SMPP session

@@ -93,8 +94,11 @@

It's very important to listen for session errors, not listening for error events will terminate the program.
It's very important to listen for session errors, not listening for error events
will terminate the program.
### Debug
To enable a simple debug of ingoing/outgoing messages pass `debug: true` as server/client option. Debug is disabled by default.
Alternatively, you can listen for the `debug` even and write your own implementation:
To enable a simple debug of ingoing/outgoing messages pass `debug: true` as
server/client option. Debug is disabled by default.
Alternatively, you can listen for the `debug` event and write your own implementation:
``` javascript

@@ -106,5 +110,18 @@ session.on('debug', function(type, msg, payload) {

### Handling client connection errors:
In case of errors while trying to connect, an `error` event will be emitted by the session and the program will be terminated if it's not listened. This is how you should check for errors.
A `debugListener` option is also supported:
``` javascript
var options = {
debug: false,
debugListener: function(type, msg, payload) {
console.log({type: type, msg: msg, payload: payload});
}
}
```
### Handling client connection errors
In case of errors while trying to connect, an `error` event will be emitted by the session
and the program will be terminated if it's not listened. This is how you should check for
errors.
``` javascript

@@ -123,22 +140,36 @@ session.on('error', function(e) {

### Connection timeout:
### Connection timeout
By default the socket will be dropped after 30000 ms if it doesn't connect. A `connectTimeout` option can be sent when making connections with the server in order to change this setting.
By default the socket will be dropped after 30000 ms if it doesn't connect.
A `connectTimeout` option can be sent when making connections with the server in order
to change this setting.
### Proxy protocol (v1) support :
[Proxy Protocol header specs](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)
### Proxy protocol
Pass `enable_proxy_protocol_detection: true` as server option.
- Only Proxy protocol v1 is supported
- `session.remote_addr` will contain the proxied source ip.
- `session.socket.remote_addr` will contain the proxy ip.
- Even with proxy protocol detection enabled the server will understand non-proxied requests.
- Tests are provided to make sure TCP4, TCP6 & UNKNOWN proxy headers are handled correctly. Tests work by injecting fake proxy protocol headers upon establishing connection.
[Proxy Protocol v1](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) is now
supported as an _experimental_ feature, both for TCP4 and TCP6.
Pass `enable_proxy_protocol_detection: true` as server option to enable proxy protocol
detection, if the option is not provided the feature is completely ignored.
- `session.remoteAddress` will contain the proxied source IP.
- `session.proxyProtocolProxy` will contain the proxy IP.
- Even with proxy protocol detection enabled the server will understand non-proxied
requests.
- Security: Proxy CIDRs validation is yet to be implemented.
- Uses [findhit-proxywrap](https://www.npmjs.com/package/findhit-proxywrap), a third
party library to wrap the net/tls implementations and decode the proxy-protocol
- header before forwarding it to the standard implementations.
Encodings
---------
#### Compatibility issues
- On proxied, non-tls connections (with Nodejs < v8): Proxywrap shows some misbehaviour
with the way this library inherits from the net server, the socket looses the ability
to emit events. As a minor-fix, the `socket.emit` method is backed up and restored
after the proxying to the net server, making everything work as expected.
## Encodings
This smpp implementation supports 3 encodings: `ASCII` (GSM 03.38), `LATIN1`, and `UCS2`.
Respective data_coding for these encodings are `0x01`, `0x03`, and `0x08`.
data_coding for these encodings are `0x01`, `0x03`, and `0x08` respectively.

@@ -162,4 +193,3 @@ Default encoding for `data_coding:0` is `ASCII`. You can change it as follows:

API
-------
## API

@@ -257,2 +287,3 @@ ### smpp.connect(url, [callback])

If options include `key` and `cert`, a TLS secured server will be created.
Include `rejectUnauthorized: false` to disable the certificate validation.

@@ -275,3 +306,3 @@ ### smpp.Server

*for other server methods/events documentations see node's `net.Server` docs.*
_for other server methods/events documentations see node's `net.Server` docs._

@@ -292,7 +323,7 @@ ### smpp.PDU

* For `Integer` parameters (no matter what the length is) you must specify a
- For `Integer` parameters (no matter what the length is) you must specify a
value of type `number` in JavaScript.
* For `Octet-String` and `COctet-String` parameters you can specify either a
- For `Octet-String` and `COctet-String` parameters you can specify either a
`Buffer` or a `String`.
* For the fields that accept SMPP Time Format (`broadcast_end_time`,
- For the fields that accept SMPP Time Format (`broadcast_end_time`,
`schedule_delivery_time`, `validity_period`, `final_date`) you can specify a

@@ -303,3 +334,3 @@ Javascript Date instance which will be automatically converted to a SMPP

converted to '000000000430000R'.
* For `short_message` and `message_payload` fields you can specify a buffer or a
- For `short_message` and `message_payload` fields you can specify a buffer or a
string or an object containing `udh` and `message` properties, while `udh` is a

@@ -311,15 +342,15 @@ buffer and `message` is either a string or a buffer. strings will be

`esm_class` is automatically set if `udh` exists.
* `sm_length` parameter is not needed. It will be automatically set depending on
- `sm_length` parameter is not needed. It will be automatically set depending on
the length of the `short_message`.
* `dest_address` parameter in `submit_multi` operation must be an array of
- `dest_address` parameter in `submit_multi` operation must be an array of
objects containing either `dest_addr_ton`, `dest_addr_npi` and,
`destination_addr` properties or `dl_name` property for SME addresses or
Distribution Lists respectively.
* `unsuccess_sme` parameter in `submit_multi_resp` operation must be an array of
- `unsuccess_sme` parameter in `submit_multi_resp` operation must be an array of
objects containing `dest_addr_ton`, `dest_addr_npi`, `destination_addr` and,
`error_status_code` properties.
* `number_of_dests` and `no_unsuccess` parameters are not needed. They will be
- `number_of_dests` and `no_unsuccess` parameters are not needed. They will be
automatically set depending on the `dest_address` and `unsuccess_sme` parameters
respectively.
* TLV parameters which can be specified multiple times
- TLV parameters which can be specified multiple times
(e.g. `broadcast_area_identifier`), must be specified as an array, even if you

@@ -355,9 +386,17 @@ want to specifiy just one item.

Roadmap
-------
* More test coverage.
* Add some usage examples (e.g client, server, and cluster examples)
## Upgrade notes
License
-------
### upgrade to version 0.6.0
- Support for Nodejs < v4 (2015) has been dropped due compatibility issues with
findhit-proxywrap library
- Proxy protocol v1 support has been added as an experimental feature. Disabled by default,
will be completely ignored if not enabled.
## Roadmap
- Add some usage examples (e.g client, server, and cluster examples)
## License
node-smpp is released under the MIT license.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc