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.5.1 to 0.6.0-rc.0

176

lib/smpp.js

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

var transport = net;
var connectTimeout;
this._extractPDUs = this._extractPDUs.bind(self);
this.sequence = 0;
this.paused = false;
this.closed = false;
this.remoteAddress = null;
this._proxyProtocolChecked = false;
this._busy = false;

@@ -21,11 +26,30 @@ this._callbacks = {};

this._command_length = null;
this._mode = null;
this._id = Math.floor(Math.random() * (999999 - 100000)) + 100000; // random session id
this._prevBytesRead = 0;
if (options.socket) {
// server mode
this._mode = "server";
this.socket = options.socket;
} else {
// client mode
this._mode = "client";
if (options.tls) {
transport = 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);
this.socket.on('connect', function() {
self.emit('connect');
this.socket.on('connect', (function() {
clearTimeout(connectTimeout);
self.remoteAddress = self.socket.remoteAddress;
self.debug("server.connected", "connected to server", {secure: options.tls});
self.emit('connect'); // @todo should emmit the session, but it would break BC
if(self.options.auto_enquire_link_period) {

@@ -36,9 +60,27 @@ self._interval = setInterval(function() {

}
});
this.socket.on('secureConnect', function() {
self.emit('secureConnect');
});
}).bind(this));
this.socket.on('secureConnect', (function() {
self.emit('secureConnect'); // @todo should emmit the session, but it would break BC
}).bind(this));
}
this.socket.on('readable', this._extractPDUs.bind(this));
this.remoteAddress = this.socket.remoteAddress;
this.socket.on('open', function() {
self.debug("socket.open");
});
this.socket.on('readable', function() {
if ( (self.socket.bytesRead - self._prevBytesRead) > 0 ) {
// on disconnections the readable event receives 0 bytes, we do not want to debug that
self.debug("socket.data.in", null, {bytes: self.socket.bytesRead - self._prevBytesRead});
self._prevBytesRead = self.socket.bytesRead;
}
self._extractPDUs();
});
this.socket.on('close', function() {
self.closed = true;
clearTimeout(connectTimeout);
if (self._mode === "server") {
self.debug("client.disconnected", "client has disconnected");
} else {
self.debug("server.disconnected", "disconnected from server");
}
self.emit('close');

@@ -51,7 +93,9 @@ if(self._interval) {

this.socket.on('error', function(e) {
self.emit('error', e);
if(self._interval) {
clearTimeout(connectTimeout);
self.debug("socket.error", e.message, e);
if (self._interval) {
clearInterval(self._interval);
self._interval = 0;
}
self.emit('error', e); // Emitted errors will kill the program if they're not captured.
});

@@ -62,2 +106,30 @@ }

Session.prototype.debug = function(type, msg, payload) {
if (type === undefined) type = null;
if (msg === undefined) msg = null;
if (this.options.debug) {
var coloredTypes = {
"reset": "\x1b[0m",
"dim": "\x1b[2m",
"client.connected": "\x1b[1m\x1b[34m",
"client.disconnected": "\x1b[1m\x1b[31m",
"server.connected": "\x1b[1m\x1b[34m",
"server.disconnected": "\x1b[1m\x1b[31m",
"pdu.command.in": "\x1b[36m",
"pdu.command.out": "\x1b[32m",
"socket.error": "\x1b[41m\x1b[30m"
}
var now = new Date();
var logBuffer = now.toISOString() +
" - " + (this._mode === "server" ? "srv" : "cli") +
" - " + this._id +
" - " + (coloredTypes.hasOwnProperty(type) ? coloredTypes[type] + type + coloredTypes.reset : type) +
" - " + (msg !== null ? msg : "" ) +
" - " + coloredTypes.dim + (payload !== undefined ? JSON.stringify(payload) : "") + coloredTypes.reset;
if (this.remoteAddress) logBuffer += " - [" + this.remoteAddress + "]"
console.log( logBuffer );
}
this.emit('debug', type, msg, payload);
}
Session.prototype.connect = function() {

@@ -79,2 +151,6 @@ this.sequence = 0;

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

@@ -89,3 +165,5 @@ this._command_length = PDU.commandLength(this.socket);

}
this.debug("pdu.command.in", pdu.command, pdu);
} catch (e) {
this.debug("pdu.command.error", e.message, e);
this.emit('error', e);

@@ -105,2 +183,49 @@ return;

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) {

@@ -127,3 +252,6 @@ if (!this.socket.writable) {

}
this.socket.write(pdu.toBuffer(), function() {
this.debug("pdu.command.out", pdu.command, pdu);
var buffer = pdu.toBuffer();
this.socket.write(buffer, (function() {
this.debug("socket.data.out", null, {bytes: buffer.length});
this.emit('send', pdu);

@@ -133,3 +261,3 @@ if (sendCallback) {

}
}.bind(this));
}).bind(this));
return true;

@@ -149,3 +277,7 @@ };

if (callback) {
this.socket.once('close', callback);
if (this.closed) {
this.socket.once('close', callback);
} else {
callback();
}
}

@@ -157,3 +289,7 @@ this.socket.end();

if (callback) {
this.socket.once('close', callback);
if (this.closed) {
this.socket.once('close', callback);
} else {
callback();
}
}

@@ -196,5 +332,7 @@ this.socket.destroy();

var transport = this.tls ? tls : net;
this.options = options;
transport.Server.call(this, options, function(socket) {
var session = new Session({socket: 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;

@@ -256,3 +394,3 @@ self.sessions.push(session);

options.host = options.slashes ? options.hostname : url;
options.tls = options.protocol == 'ssmpp:';
options.tls = options.protocol === 'ssmpp:';
} else if (typeof url == 'function') {

@@ -266,10 +404,14 @@ listener = url;

options.port = url.port;
options.tls = url.protocol == 'ssmpp:';
options.tls = url.protocol === 'ssmpp:';
}
}
options.port = options.port || (options.tls ? 3550 : 2775);
options.debug = options.debug || false;
options.connectTimeout = options.connectTimeout || 30000;
var session = new Session(options);
if (listener) {
session.on(options.tls ? 'secureConnect' : 'connect', listener);
session.on(options.tls ? 'secureConnect' : 'connect', function() {
listener(session);
});
}

@@ -276,0 +418,0 @@

2

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

@@ -5,0 +5,0 @@ "homepage": "https://github.com/farhadi/node-smpp",

@@ -37,21 +37,23 @@ node-smpp

url: 'smpp://example.com:2775',
auto_enquire_link_period: 10000
auto_enquire_link_period: 10000,
debug: true
}, function() {
session.bind_transceiver({
system_id: 'YOUR_SYSTEM_ID',
password: 'YOUR_PASSWORD'
}, function(pdu) {
if (pdu.command_status === 0) {
// Successfully bound
session.submit_sm({
destination_addr: 'DESTINATION NUMBER',
short_message: 'Hello!'
}, function(pdu) {
if (pdu.command_status === 0) {
// Message successfully sent
console.log(pdu.message_id);
}
});
}
});
});
session.bind_transceiver({
system_id: 'YOUR_SYSTEM_ID',
password: 'YOUR_PASSWORD'
}, function(pdu) {
if (pdu.command_status == 0) {
// Successfully bound
session.submit_sm({
destination_addr: 'DESTINATION NUMBER',
short_message: 'Hello!'
}, function(pdu) {
if (pdu.command_status == 0) {
// Message successfully sent
console.log(pdu.message_id);
}
});
}
});
```

@@ -63,3 +65,8 @@

var smpp = require('smpp');
var server = smpp.createServer(function(session) {
var server = smpp.createServer({
debug: true
}, function(session) {
session.on('error', function (err) {
// Something ocurred, not listening for this event will terminate the program
});
session.on('bind_transceiver', function(pdu) {

@@ -82,5 +89,49 @@ // we pause the session to prevent further incoming pdu events,

});
server.listen(2775);
```
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:
``` javascript
session.on('debug', 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
session.on('error', function(e) {
// empty callback to catch emitted errors to prevent exit due unhandled errors
if (e.code === "ETIMEOUT") {
// TIMEOUT
} else if (e.code === "ECONNREFUSED" {
// CONNECTION REFUSED
} else {
// OTHER ERROR
}
});
```
### 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.
### Proxy protocol (v1) support :
[Proxy Protocol header specs](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)
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.
- Security: Proxy CIDRs validation is yet to be implemented.
Encodings

@@ -87,0 +138,0 @@ ---------

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