sf-static/test/node/lib/node_modules/net-browserify/api.js
2023-02-28 19:21:07 +01:00

190 lines
4.4 KiB
JavaScript

var net = require('net');
var http = require('http');
var crypto = require('crypto');
var express = require('express');
var expressWs = require('express-ws');
var bodyParser = require('body-parser');
function generateToken() {
return crypto.randomBytes(32).toString('hex');
}
function checkTo(allowed, requested) {
if (!(allowed instanceof Array)) {
allowed = [allowed];
}
// For each rule
for (var i = 0; i < allowed.length; i++) {
var to = allowed[i];
if ((to.host == requested.host || !to.host) && (to.port == requested.port || !to.port)) {
if (to.blacklist) { // This item is blacklisted
return false;
} else { // Otheriwse, it's whitelisted
return true;
}
}
}
// No rule found, access denied
return false;
}
module.exports = function (options, connectionListener) {
options = options || {};
var app = express();
var jsonParser = bodyParser.json();
var urlRoot = options.urlRoot || '/api/vm/net';
var server;
if (options.server) {
server = options.server;
} else {
server = http.createServer();
}
var sockets = {};
if (options.allowOrigin) {
var allowOrigin = options.allowOrigin;
if (typeof options.allowOrigin != 'string') {
allowOrigin = (options.allowOrigin === true) ? '*' : '';
}
if (allowOrigin) {
// Set Access-Control headers (CORS)
app.use(function (req, res, next) {
if (req.path.indexOf(urlRoot) !== 0) {
next();
return;
}
res.header('Access-Control-Allow-Origin', allowOrigin);
if (req.method.toUpperCase() == 'OPTIONS') { // Preflighted requests
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Max-Age', 1728000); // Access-Control headers cached for 20 days
}
next();
});
}
}
app.post(urlRoot + '/connect', jsonParser, function (req, res) {
var host = req.body.host,
port = req.body.port;
if (!host || !port) {
res.status(400).send({
code: 400,
error: 'No host and port specified'
});
return;
}
if (options.to) {
if (!checkTo(options.to, { host: host, port: port })) {
res.status(403).send({
code: 403,
error: 'Destination not allowed'
});
return;
}
}
var socket = net.connect({
host: host,
port: port
}, function (err) {
if (err) {
res.status(500).send({
code: 500,
error: err
});
return;
}
// Generate a token for this connection
var token = generateToken();
sockets[token] = socket;
// Remove the socket from the list when closed
socket.on('end', function () {
if (sockets[token]) {
delete sockets[token];
}
});
console.log('Connected to '+req.body.host+':'+req.body.port+' ('+token+')');
var remote = socket.address();
res.send({
token: token,
remote: remote
});
});
socket.on('error', function (err) {
if (res.finished) {
console.log("Socket error after response closed: "+err);
return;
}
res.status(502).send({
code: 502,
error: 'Socket error: '+err.code,
details: err
});
});
if (connectionListener) {
connectionListener(socket)
}
});
var wss = expressWs(app, server);
app.ws(urlRoot + '/socket', function (ws, req) {
var token = req.query.token;
if (!sockets[token]) {
console.warn('WARN: Unknown TCP connection with token "'+token+'"');
ws.close();
return;
}
var socket = sockets[token];
//delete sockets[token];
console.log('Forwarding socket with token '+token);
ws.on('message', function (chunk, flags) {
socket.write(flags.buffer || chunk, 'binary', function () {
//console.log('Sent: ', (flags.buffer || chunk).toString());
});
});
socket.on('data', function (chunk) {
//console.log('Received: ', chunk.toString());
// Providing a callback is important, otherwise errors can be thrown
ws.send(chunk, { binary: true }, function (err) {});
});
socket.on('end', function () {
console.log('TCP connection closed by remote ('+token+')');
ws.close();
});
ws.on('close', function () {
socket.end();
console.log('Websocket connection closed ('+token+')');
});
});
app.on('mount', function (parentApp) {
// @see https://github.com/strongloop/express/blob/master/lib/application.js#L615
parentApp.listen = function listen() {
server.addListener('request', this);
return server.listen.apply(server, arguments);
};
});
return app;
};