Working basic sessions

This commit is contained in:
2015-04-06 19:49:51 +02:00
parent 281b9c1d93
commit 54c3386a4f
3 changed files with 281 additions and 45 deletions
+170 -36
View File
@@ -374,7 +374,7 @@ function session(sock) {
* Always use this function to close the socket so we get it on log
*/
sessionEmitter.closeSocket = function() {
log.verbose('larvitsmpp: session() - closeSocket() - Closing socket for ' + sock.remoteAddress + ':' + sock.remotePort);
log.debug('larvitsmpp: session() - closeSocket() - Closing socket for ' + sock.remoteAddress + ':' + sock.remotePort);
if (sessionEmitter.enqLinkTimer) {
clearTimeout(sessionEmitter.enqLinkTimer);
}
@@ -382,26 +382,26 @@ function session(sock) {
};
/**
* Send PDU to socket
* Write PDU to socket
*
* @param buf pdu - can also take PDU object
* @param buf or obj pdu - can also take PDU object
* @param bol closeAfterSend - if true will close the socket after sending
*/
sessionEmitter.send = function(pdu, closeAfterSend) {
sessionEmitter.sockWrite = function(pdu, closeAfterSend) {
if ( ! Buffer.isBuffer(pdu)) {
objToPdu(pdu, function(err, buffer) {
if (err) {
log.warn('larvitsmpp: session() - send() - Could not convert PDU to buffer');
log.warn('larvitsmpp: session() - sockWrite() - Could not convert PDU to buffer');
sessionEmitter.closeSocket();
return;
}
sessionEmitter.send(buffer);
sessionEmitter.sockWrite(buffer);
});
return;
}
log.verbose('larvitsmpp: session() - send() - sending PDU. SeqNr: ' + pdu.readUInt32BE(12) + ' cmd: ' + defs.cmdsById[pdu.readUInt32BE(4)].command + ' cmdStatus: ' + defs.errorsById[parseInt(pdu.readUInt32BE(8))] + ' hex: ' + pdu.toString('hex'));
log.verbose('larvitsmpp: session() - sockWrite() - sending PDU. SeqNr: ' + pdu.readUInt32BE(12) + ' cmd: ' + defs.cmdsById[pdu.readUInt32BE(4)].command + ' cmdStatus: ' + defs.errorsById[parseInt(pdu.readUInt32BE(8))] + ' hex: ' + pdu.toString('hex'));
sock.write(pdu);
if (closeAfterSend) {
@@ -409,6 +409,84 @@ function session(sock) {
}
};
/**
* Send a PDU to the remote
*
* @param buf or obj pdu
* @param bol closeAfterSend - Will close after return is fetched. Defaults to false (OPTIONAL)
* @param func callback(err, retPdu) (OPTIONAL)
*/
sessionEmitter.send = function(pdu, closeAfterSend, callback) {
var pduObj = pdu,
err = null;
// Make sure the sequence number is set and is correct
if ( ! Buffer.isBuffer(pdu)) {
pdu.seqNr = sessionEmitter.ourSeqNr;
}
// Make sure the pdu is an object
if (Buffer.isBuffer(pdu)) {
pduToObj(pdu, function(err, pduObj) {
if (err) {
callback(err);
return;
}
sessionEmitter.send(pduObj, closeAfterSend, callback);
});
return;
}
log.debug('larvitsmpp: session() - sessionEmitter.send() - Sending PDU to remote. cmdName: ' + pdu.cmdName + ' seqNr: ' + pdu.seqNr);
// If closeAndSend is omitted, put callback in its place
if (typeof closeAfterSend === 'function') {
callback = closeAfterSend;
closeAfterSend = undefined;
}
// Make sure the callack is a function
if (typeof callback !== 'function') {
callback = function(){};
}
// Response PDUs are not allowed with the send() command, they should use the sendReturn()
if (pduObj.cmdName.substring(pduObj.cmdName - 5) === '_resp') {
err = new Error('larvitsmpp: session() - sessionEmitter.send() - Given pduObj is a response, use sendReturn() instead. cmdName: ' + pduObj.cmdName);
callback(err);
return;
}
// When the return is fetched, call the callback
sessionEmitter.on('incomingPdu' + pduObj.seqNr, function(incPduObj) {
log.debug('larvitsmpp: session() - sessionEmitter.send() - sessionEmitter.on(incomingPdu) - cmdName: ' + incPduObj.cmdName + ' seqNr: ' + incPduObj.seqNr + ' cmdStatus: ' + incPduObj.cmdStatus);
// Clean up by removing this listener or else it will lurk along forever
sessionEmitter.removeAllListeners('incomingPdu' + pduObj.seqNr);
// Make sure this is the actual response to the sent PDU
if (incPduObj.isResponse() && incPduObj.seqNr === pduObj.seqNr) {
callback(null, incPduObj);
if (closeAfterSend) {
sessionEmitter.closeSocket();
}
} else {
err = new Error('larvitsmpp: session() - sessionEmitter.send() - sessionEmitter.on(incomingPdu) - Event triggered but incoming PDU is not a response or seqNr does not match. isResponse: ' + incPduObj.isResponse().toString() + ' incSeqNr: ' + incPduObj.seqNr + ' expected seqNr: ' + pduObj.seqNr);
log.warn(err.message);
callback(err);
}
});
// Increase our internal sequence number
sessionEmitter.incOurSeqNr();
// Write the PDU to socket
sessionEmitter.sockWrite(pduObj);
};
/**
* Send a return to given PDU
*
@@ -425,7 +503,7 @@ function session(sock) {
pduReturn(pdu, status, function(err, retPdu) {
if (err) {
log.error('larvitsmpp: session() - Could not create return PDU: ' + err.message);
log.error('larvitsmpp: session() - sessionEmitter.sendReturn() - Could not create return PDU: ' + err.message);
sessionEmitter.closeSocket();
if (typeof callback === 'function') {
@@ -435,7 +513,7 @@ function session(sock) {
return;
}
sessionEmitter.send(retPdu, closeAfterSend);
sessionEmitter.sockWrite(retPdu, closeAfterSend);
if (typeof callback === 'function') {
callback();
@@ -443,11 +521,6 @@ function session(sock) {
});
};
sessionEmitter.handleResponse = function(pduObj) {
console.log(pduObj);
console.log('HANDLE IT motha fukkah!');
};
sessionEmitter.deliverSm = function(pduObj) {
console.log(pduObj);
sessionEmitter.sendReturn(pduObj);
@@ -469,9 +542,7 @@ function session(sock) {
sessionEmitter.loggedIn = true;
};
/**
* Dummy method - should be used by serverSession or clientSession
*/
// Dummy method - should be used by serverSession or clientSession
sessionEmitter.resetEnqLinkTimer = function() {
log.silly('larvitsmpp: session() - resetEnqLinkTimer() - Resetting the kill timer');
};
@@ -482,6 +553,12 @@ function session(sock) {
sessionEmitter.sendReturn(pduObj);
};
sessionEmitter.unbind = function() {
sessionEmitter.send({
'cmdName': 'unbind'
}, true);
};
// Add a 'data' event handler to this instance of socket
sock.on('data', function(pduBuf) {
// Pass the data along to the sessionEmitter
@@ -500,22 +577,11 @@ function session(sock) {
} else {
log.verbose('larvitsmpp: session() - sock.on(data) - Incoming PDU. Seqnr: ' + pduObj.seqNr + ' cmd: ' + pduObj.cmdName + ' cmdStatus: ' + pduObj.cmdStatus + ' hex: ' + pduBuf.toString('hex'));
if (sessionEmitter.loggedIn === false) {
log.verbose('larvitsmpp: session() - sock.on(data) - Not logged in, running login function');
sessionEmitter.login(pduObj);
} else if (pduObj.isResponse()) {
sessionEmitter.handleResponse(pduObj);
} else if (pduObj.cmdName === 'deliver_sm') {
sessionEmitter.deliverSm(pduObj);
} else if (pduObj.cmdName === 'enquire_link') {
sessionEmitter.enquireLink(pduObj);
} else if (pduObj.cmdName === 'submit_sm') {
sessionEmitter.submitSm(pduObj);
} else if (pduObj.cmdName === 'unbind') {
sessionEmitter.sendReturn(pduObj, 'ESME_ROK', true);
if (pduObj.isResponse()) {
// We do this so we can remove the dynamic event listeners to not have a memory leak
sessionEmitter.emit('incomingPdu' + pduObj.seqNr, pduObj);
} else {
// All other commands we do not support
sessionEmitter.sendReturn(pduObj, 'ESME_RINVCMDID');
sessionEmitter.emit('incomingPdu', pduObj);
}
}
});
@@ -524,7 +590,7 @@ function session(sock) {
// Add a 'close' event handler to this instance of socket
sock.on('close', function() {
sessionEmitter.emit('close');
log.silly('larvitsmpp: session() - socket closed');
log.debug('larvitsmpp: session() - socket closed');
});
return sessionEmitter;
@@ -562,7 +628,7 @@ function serverSession(sock, options) {
}
parent.sock.resume();
parent.send(retPdu);
parent.sockWrite(retPdu);
});
return;
@@ -616,6 +682,24 @@ function serverSession(sock, options) {
};
parent.resetEnqLinkTimer();
parent.on('incomingPdu', function(pduObj) {
if (pduObj.cmdName === 'unbind') {
parent.sendReturn(pduObj, 'ESME_ROK', true);
} else if (parent.loggedIn === false) {
log.debug('larvitsmpp: serverSession() - parent.handleIncomingPdu() - Not logged in, running login function');
parent.login(pduObj);
} else if (pduObj.cmdName === 'deliver_sm') {
parent.deliverSm(pduObj);
} else if (pduObj.cmdName === 'enquire_link') {
parent.enquireLink(pduObj);
} else if (pduObj.cmdName === 'submit_sm') {
parent.submitSm(pduObj);
} else {
// All other commands we do not support
parent.sendReturn(pduObj, 'ESME_RINVCMDID');
}
});
return parent;
}
@@ -629,6 +713,49 @@ function serverSession(sock, options) {
function clientSession(sock, options) {
var parent = session(sock);
parent.login = function() {
var loginPdu = {
'cmdName': 'bind_transceiver',
'seqNr': parent.ourSeqNr,
'params': {
'system_id': options.username,
'password': options.password
}
};
parent.send(loginPdu, function(err, retPduObj) {
if (err) {
parent.emit('loginFailed');
return;
}
if (retPduObj.cmdStatus === 'ESME_ROK') {
log.info('larvitsmpp: clientSession() - parent.login() - Successful login!');
parent.loggedIn = true;
parent.emit('loggedIn');
} else {
log.info('larvitsmpp: clientSession() - parent.login() - Login failed. Status msg: ' + retPduObj.cmdStatus);
parent.emit('loginFailed');
}
});
};
parent.login();
parent.on('incomingPdu', function(pduObj) {
if (pduObj.cmdName === 'deliver_sm') {
parent.deliverSm(pduObj);
} else if (pduObj.cmdName === 'enquire_link') {
parent.enquireLink(pduObj);
} else if (pduObj.cmdName === 'submit_sm') {
parent.submitSm(pduObj);
} else if (pduObj.cmdName === 'unbind') {
parent.sendReturn(pduObj, 'ESME_ROK', true);
} else {
// All other commands we do not support
parent.sendReturn(pduObj, 'ESME_RINVCMDID');
}
});
return parent;
}
@@ -670,7 +797,7 @@ function server(options, callback) {
* Setup a client
*
* @param obj options - host, port, username, password
* @param func callback(err, connection)
* @param func callback(err, session)
*/
function client(options, callback) {
var sock = new net.Socket();
@@ -689,8 +816,15 @@ function client(options, callback) {
log.info('larvitsmpp: client() - Connected to ' + sock.remoteAddress + ':' + sock.remotePort);
session.on('loggedIn', function() {
callback(null, session);
sock.write('I am Chuck Norris!');
});
session.on('loginFailed', function() {
var err = new Error('larvitsmpp: client() - Remote host refused login.');
log.warn(err.message);
callback(err);
});
});
}
+7 -4
View File
@@ -7,12 +7,15 @@
"private": false,
"contributors": [],
"dependencies": {
"winston": "x",
"utils-merge": "x",
"iconv-lite": "x"
"winston": "latest",
"utils-merge": "latest",
"iconv-lite": "latest"
},
"description": "Simplified SMPP implementation",
"devDependencies": {},
"devDependencies": {
"mocha": "latest",
"portfinder": "latest"
},
"keywords": [
"smpp",
"pdu",
+103 -4
View File
@@ -1,7 +1,21 @@
'use strict';
var larvitsmpp = require('../larvitsmpp'),
assert = require('assert');
assert = require('assert'),
portfinder = require('portfinder'),
log = require('winston');
// Remove log output to the console
log.remove(log.transports.Console);
// Very advanced auth system
function checkuserpass(username, password, callback) {
if (username === 'foo' && password === 'bar') {
callback(null, true);
} else {
callback(null, false);
}
}
describe('PDU convertion', function() {
@@ -107,8 +121,94 @@ describe('PDU convertion', function() {
});
/*
describe('Sessions', function() {
it('should setup a basic server and client and then directly unbinding again', function(done) {
portfinder.getPort(function(err, freePort) {
assert( ! err, 'Error should be negative');
larvitsmpp.server({
'port': freePort
}, function(err, serverSession) {
assert( ! err, 'Error should be negative');
serverSession.on('close', function() {
// Manually destroy the server socket
serverSession.sock.destroy();
});
});
larvitsmpp.client({
'port': freePort
}, function(err, clientSession) {
assert( ! err, 'Error should be negative');
// Gracefully close connection
clientSession.unbind();
done();
});
});
});
it('should setup a server with auth and client trying to connect with wrong username and password', function(done) {
portfinder.getPort(function(err, freePort) {
assert( ! err, 'Error should be negative');
larvitsmpp.server({
'port': freePort,
'checkuserpass': checkuserpass
}, function(err, serverSession) {
assert( ! err, 'Error should be negative');
serverSession.on('close', function() {
// Manually destroy the server socket
serverSession.sock.destroy();
});
});
larvitsmpp.client({
'port': freePort
}, function(err) {
assert(err, 'Error should be set since login should have failed');
done();
});
});
});
it('should setup a server with auth and client trying to connect with correct username and password', function(done) {
portfinder.getPort(function(err, freePort) {
assert( ! err, 'Error should be negative');
larvitsmpp.server({
'port': freePort,
'checkuserpass': checkuserpass
}, function(err, serverSession) {
assert( ! err, 'Error should be negative');
serverSession.on('close', function() {
// Manually destroy the server socket
serverSession.sock.destroy();
});
});
larvitsmpp.client({
'port': freePort,
'username': 'foo',
'password': 'bar'
}, function(err, clientSession) {
assert( ! err, 'Error should be negative');
// Gracefully close connection
clientSession.unbind();
done();
});
});
});
/*
it('should try to send submit_sm while not logged in and get a failure return PDU back', function(done) {
});
@@ -123,6 +223,5 @@ describe('Sessions', function() {
it('should send a bind_transceiver to a new session with a login-method and succeed with correct username and password', function(done) {
});
});*/
});
*/