pipboyrelay
Version:
Relay for the pip boy from Fallout 4
180 lines (149 loc) • 4.87 kB
JavaScript
var dgram = require('dgram');
var net = require('net');
/**
* Provides data from clients and servers going through the relay
* @callback relayCallback
* @param {Buffer} data
* @param {Object} telemetry
* @param {Object} telemetry.src
* @param {string} telemetry.src.address
* @param {number} telemetry.src.port
* @param {Object} telemetry.dst
* @param {string} telemetry.dst.address
* @param {number} telemetry.dst.port
*/
/**
* Create a UDP Relay
* @constructor
*/
var UDPRelay = function UDPRelay () {
this.server = dgram.createSocket('udp4');
};
/**
* Bind the UDP Relay using upstreamInfo
* @param {Object} upstreamInfo
* @param {string} upstreamInfo.address
* @param {number} upstreamInfo.port
* @param {relayCallback} - callback that receives data
*/
UDPRelay.prototype.bind = function UDPRelay (upstreamInfo, cb) {
var server = this.server;
this.server.on('message', function (message, clientInfo) {
var fakeClient = dgram.createSocket('udp4');
fakeClient.on('message', function (message, serverInfo) {
// Every time the fake client gets a message, we expect it from the upstream server
if (serverInfo.address === upstreamInfo.address &&
serverInfo.port === upstreamInfo.port) {
var copiedBuffer = new Buffer(message.length);
message.copy(copiedBuffer);
// Now emulate our server
server.send(message, 0, message.length, clientInfo.port, clientInfo.address);
var telemetry = {
'src': serverInfo,
'dst': clientInfo
};
cb(copiedBuffer, telemetry);
} else {
// Ignore responses not coming from the upstream server
}
});
// As soon as our client is ready, go ahead and send their message onward
fakeClient.bind(undefined, undefined, function () {
var copiedBuffer = new Buffer(message.length);
message.copy(copiedBuffer);
fakeClient.send(message, 0, message.length, upstreamInfo.port, upstreamInfo.address);
var telemetry = {
'src': clientInfo,
'dst': upstreamInfo
};
cb(copiedBuffer, telemetry);
});
});
this.server.bind(upstreamInfo.port, '0.0.0.0');
};
/**
* Create a TCP relay for an upstream server
* @constructor
*/
var TCPRelay = function TCPRelay () {
this.server = net.createServer({'allowHalfOpen': true});
};
/**
* Listen for traffic to relay to/from an upstream server
* @param {Object} upstreamInfo
* @param {string} upstreamInfo.address
* @param {number} upstreamInfo.port
* @param {relayCallback} - callback that handles new data
*/
TCPRelay.prototype.listen = function listen (upstreamInfo, cb) {
this.server.on('connection', function (client) {
// Now we create our fake client
var fakeClient = new net.Socket();
fakeClient.connect(upstreamInfo.port, upstreamInfo.address);
var actualClientInfo = {};
actualClientInfo.address = client.remoteAddress;
actualClientInfo.port = client.remotePort;
actualClientInfo.family = client.remoteFamily;
fakeClient.on('connect', function () {
// Once we're connected, we can get each message from the client
client.on('data', function (message) {
var copiedBuffer = new Buffer(message.length);
message.copy(copiedBuffer);
// To the server
fakeClient.write(message);
var serverInfo = {};
serverInfo.address = fakeClient.remoteAddress;
serverInfo.port = fakeClient.remotePort;
serverInfo.family = fakeClient.remoteFamily;
var telemetry = {
'src': actualClientInfo,
'dst': serverInfo
};
cb(copiedBuffer, telemetry);
});
});
fakeClient.on('data', function (message) {
var copiedBuffer = new Buffer(message.length);
message.copy(copiedBuffer);
var serverInfo = {};
serverInfo.address = fakeClient.remoteAddress;
serverInfo.port = fakeClient.remotePort;
serverInfo.family = fakeClient.remoteFamily;
var telemetry = {
'src': serverInfo,
'dst': actualClientInfo
};
client.write(message);
cb(copiedBuffer, telemetry);
});
client.on('error', function (err) {
throw err;
});
client.on('close', function (hadError) {
fakeClient.close();
});
client.on('end', function () {
fakeClient.end();
});
fakeClient.on('close', function (hadError) {
client.close();
});
fakeClient.on('end', function () {
client.end();
});
fakeClient.on('error', function (err) {
throw err;
});
});
this.server.on('error', function (err) {
throw err;
});
this.server.listen(upstreamInfo.port, '0.0.0.0');
};
TCPRelay.prototype.close = function close (cb) {
this.server.close(cb);
};
module.exports = {
UDPRelay: UDPRelay,
TCPRelay: TCPRelay
};