ws-to-socket-proxy
Version:
A proxy server which can be used to proxy an web socket connection to tcp or udp
273 lines • 13.4 kB
JavaScript
;
var dgram = require("dgram");
var websocket = require("websocket");
var http = require("http");
var chalk = require("chalk");
var net = require("net");
var WebSocketServer = websocket.server;
// chalk instance used for coloring
var font = new chalk.constructor({ enabled: true });
// this is the port used by the client to connect to this proxy
var wsListeningPort = 8778;
var verbose = process.argv.indexOf("-v") != -1;
// TCP SERVER START
var server = net.createServer().listen(5000, "0.0.0.0", undefined, function () {
console.log('server listening to %j', server.address());
log(font.blue("TCP Server") + " > Server listneing on " + JSON.stringify(server.address()));
});
server.on('connection', handleConnection);
function stringToABuffer(str) {
var buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return bufView;
}
function aBufferToString(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function handleConnection(conn) {
var remoteAddress = conn.remoteAddress + ':' + conn.remotePort;
log(font.blue("TCP Server") + " > New client connection from " + remoteAddress);
// conn.write(Buffer.from([ 0x62, 0x75, 0x66, 0x66, 0x65, 0x72 ]));
if (!serverConnection) {
serverConnection = createServerConnectionTCP('192.168.5.205', 5001, {
send: conn.write.bind(conn),
remoteAddress: conn.remoteAddress,
port: conn.remotePort
});
}
conn.on('data', onConnData);
conn.once('close', onConnClose);
conn.on('error', onConnError);
function onConnData(data) {
log(font.blue("TCP Server") + " > Received data from the client sending to server.");
if (serverConnection) {
serverConnection.sendBinary(data);
}
}
function onConnClose() {
log(font.blue("TCP Server") + " > Client " + remoteAddress + " socket closed.");
}
function onConnError(err) {
log(font.red("TCP Server") + " > Client " + remoteAddress + " error: " + err.message + "}.");
}
}
// TCP SERVER END
// WS SERVER START
// set up http server which is needed by the ws
var httpServer = http.createServer(function (request, response) {
log(font.blue("WS httpServer") + " > Received request for " + font.green(request.url));
response.writeHead(404);
response.end();
});
httpServer.on('clientError', function (err, socket) {
console.log("HTTP server error " + err + " on " + JSON.stringify(socket));
});
httpServer.on('error', function (err) {
console.log("HTTP server error " + err);
});
httpServer.listen(wsListeningPort, function () {
log(font.blue("WS httpServer") + " > Listening on port " + font.green(wsListeningPort.toString()));
});
// this is the web socket server instance used to listen for incoming client connections
var proxyServer = new WebSocketServer({
httpServer: httpServer,
autoAcceptConnections: false
});
var serverConnection;
// when there is incoming connection this method will be executed
proxyServer.on('request', function (request) {
var clientConnection = request.accept('echo-protocol', request.origin);
log(font.blue("WS") + " > Connection accepted from " + font.green(request.socket.remoteAddress) + ":" + font.green(request.socket.remotePort.toString()));
clientConnection.on('message', function (message) {
if (message.type === 'utf8') {
var config;
//if a config file has been sent we configure/reconfigure the new connection
config = tryParseConfigFile(message.utf8Data);
if (config) {
if (serverConnection) {
serverConnection.close();
}
// depending on the type property of the config we are either going to open tcp or udp connection to the requested remote server
if (config && config.type == "udp") {
// opening an UDP connection
serverConnection = createServerConnectionUDP(config.ip, config.port, {
send: clientConnection.send.bind(clientConnection),
remoteAddress: clientConnection.socket.remoteAddress,
port: clientConnection.socket.remotePort
});
}
else if (config && config.type == "tcp") {
// opening an TCP connection
serverConnection = createServerConnectionTCP(config.ip, config.port, {
send: clientConnection.send.bind(clientConnection),
remoteAddress: clientConnection.socket.remoteAddress,
port: clientConnection.socket.remotePort
});
}
else {
log(font.blue("WS") + " > no server connection type " + font.red(config && config.type));
}
}
else {
//if a config file has not been sent
if (!serverConnection || !serverConnection.isOpened()) {
log(font.blue("WS") + " > " + font.red("There is no server connection that has been configured.") + " > Please send a string message with config data with type " + font.yellow("{type: \"udp\" | \"tcp\", ip: string, port: number}") + "\")}");
}
else {
serverConnection.sendUTF(message.utf8Data);
}
}
}
else if (message.type === 'binary') {
// binary messages are redirected to the remote server.
// log(`${font.blue("WS")} > Received Binary data from ${font.green(request.socket.remoteAddress)}:${font.green(request.socket.remotePort.toString())} size: ${font.green(message.binaryData.length.toString())}`);
if (!serverConnection || !serverConnection.isOpened()) {
log(font.blue("WS") + " > " + font.red("There is no server connection that has been configured.") + " > Please send a string message with config data with type " + font.yellow("{type: \"udp\" | \"tcp\", ip: string, port: number}") + "\")}");
}
else {
serverConnection.sendBinary(message.binaryData);
}
}
});
clientConnection.on('close', function (reasonCode, description) {
log(font.blue("WS") + " > Peer " + font.green(request.socket.remoteAddress) + ":" + font.green(request.socket.remotePort.toString()) + " disconnected");
});
});
function tryParseConfigFile(msg) {
var config = JSON.parse(msg);
return config.ip && config.port && config.type ? config : null;
}
// WS SERVER END
/**
* Creates and udp connection and returns and wrapper object for sending data to this connection.
*
* @param ip the ip of the remote server
* @param port the port f the remote
* @param wsConnection the WS socket of the connected client. will be used to automatically redirect messages from the udp server to the client.
* @returns a wrapper object for sending data to this connection
*/
function createServerConnectionUDP(ip, port, clientConnection) {
log(font.blue("Server Connection UDP") + " > create " + font.green(ip) + ":" + font.green(port.toString()));
var bindAddress = '0.0.0.0';
var udpSocket = dgram.createSocket('udp4');
var socketOpened = false;
try {
udpSocket.bind(port, bindAddress, start);
}
catch (err) {
log(font.blue("Server Connection UDP") + " > Socket bind error " + font.red(err.toString()));
}
// this callback will be called when the binding is done
function start() {
log(font.blue("Server Connection UDP") + " > bind on " + font.green(bindAddress) + " " + font.green(port.toString()) + " done");
var broadcast = true;
udpSocket.setBroadcast(broadcast);
log(font.blue("Server Connection UDP") + " > broadcast " + font.green(broadcast.toString()));
}
// error handler
udpSocket.on('error', function (err) {
log(font.blue("Server Connection UDP") + " > Error " + font.red(err.toString()));
udpSocket.close();
});
// handles incomming messages form the udp server and redirects them to the websocket client.
udpSocket.on('message', function (msg, rinfo) {
// log(`${font.blue("Server Connection UDP")} > Message "${font.green(msg.toString())}" from ${font.green(rinfo.address)} ${font.green(rinfo.port.toString())}`);
// log(`${font.blue("Server Connection UDP")} > resending to ${font.green(clientConnection.remoteAddress)}:${font.green(clientConnection.port.toString())}`);
clientConnection && clientConnection.send(msg);
});
udpSocket.on('listening', function () {
log(font.blue("Server Connection UDP") + " > Listening");
socketOpened = true;
});
udpSocket.on("close", function () {
socketOpened = false;
log(font.blue("Server Connection UDP") + " > Close");
});
// callback called when a message us send to the udp server.
function sendCallback(err) {
if (err) {
log(font.blue("Server Connection UDP") + " > Send error " + err);
console.log(err);
}
}
return {
isOpened: function () {
return socketOpened;
},
sendBinary: function (data) {
log(font.blue("Server Connection UDP") + " > Send data to " + font.green(ip) + ":" + font.green(port.toString()) + " , data length " + font.green(data.length.toString()));
udpSocket.send(data, port, ip, sendCallback);
},
sendUTF: function (data) {
log(font.blue("Server Connection UDP") + " > Sending string data to " + font.green(ip) + ":" + font.green(port.toString()) + "}");
udpSocket.send(data, port, ip, sendCallback);
},
close: function () {
udpSocket.close();
}
};
}
/**
* Creates and TCP connection and returns and wrapper object for sending data to this connection.
*
* @param ip the ip of the remote server
* @param port the port f the remote
* @param wsConnection the WS socket of the connected client. will be used to automatically redirect messages from the udp server to the client.
* @returns a wrapper object for sending data to this connection
*/
function createServerConnectionTCP(ip, port, clientConnection) {
log(font.blue("Server Connection TCP") + " > " + font.green(ip) + ":" + font.green(port.toString()) + " > create");
var tcpSocket = new net.Socket();
tcpSocket.connect(port, ip, start);
// callback executed on successful connect
function start() {
log(font.blue("Server Connection TCP") + " > " + font.green(ip) + ":" + font.green(port.toString()) + " > connected");
}
// error handler
tcpSocket.on('error', function (err) {
log(font.blue("Server Connection TCP") + " > " + font.green(ip) + ":" + font.green(port.toString()) + " > error " + font.red(err.toString()));
});
// handler for incoming server data. It will be redirected to the ws client
tcpSocket.on('data', function (data) {
// log(`${font.blue("Server Connection TCP")} > ${font.green(ip)}:${font.green(port.toString())} > data received, length ${font.green(data.length.toString())} ${data}`);
// log(`${font.blue("Server Connection TCP")} > ${font.green(ip)}:${font.green(port.toString())} > resending to ${font.green(clientConnection.remoteAddress)}:${font.green(clientConnection.port.toString())}`);
clientConnection && clientConnection.send(data);
});
// connection close handler
tcpSocket.on("close", function (hadError) {
var color = hadError ? font.red : font.green;
log(font.blue("Server Connection TCP") + " > " + font.green(ip) + ":" + font.green(port.toString()) + " > closed, because of an error: " + color(hadError.toString()));
});
return {
isOpened: function () {
return tcpSocket.writable;
},
sendBinary: function (data) {
// log(`${font.blue("Server Connection TCP")} > ${font.green(ip)}:${font.green(port.toString())} > Send data, data length ${font.green(data.length.toString())}`);
tcpSocket.write(data);
},
sendUTF: function (data) {
log(font.blue("Server Connection TCP") + " > " + font.green(ip) + ":" + font.green(port.toString()) + " > Sending string data }");
tcpSocket.write(data, 'UTF8');
},
close: function () {
tcpSocket.end();
}
};
}
/**
* Utility function for logging messages in the console.
* Will write each message with a timestamp in front will have this format [HH:MM:SS] : msg
*
* @param msg the message to be displayed
*/
function log(msg) {
if (verbose) {
console.log("[" + font.gray(new Date().toTimeString().substr(0, 8)) + "] : " + msg);
}
}
//# sourceMappingURL=wsproxy.js.map