This repository has been archived on 2023-11-14. You can view files and clone it, but cannot push or open issues/pull-requests.
valnet/lib/STP.js

143 lines
3.0 KiB
JavaScript
Raw Normal View History

2020-11-11 19:38:39 -05:00
const net = require('net');
const EventEmitter = require('events');
const NodeRSA = require('node-rsa');
2020-11-13 10:19:44 -05:00
const log = require('signale').scope('stp');
2020-11-11 19:38:39 -05:00
module.exports.createServer = function(keys, onConnect) {
const server = new Server(keys);
2020-11-13 10:19:44 -05:00
server.on('connection', onConnect);
2020-11-11 19:38:39 -05:00
return server;
// return 5;
}
2020-11-13 10:19:44 -05:00
module.exports.connect = function(identity, port, ip) {
const client = net.connect(port, ip);
return new STPSocket(client, identity);
2020-11-11 19:38:39 -05:00
}
2020-11-13 10:19:44 -05:00
class Server extends EventEmitter {
tcpServer;
identity;
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
constructor(identity) {
2020-11-11 19:38:39 -05:00
super();
2020-11-13 10:19:44 -05:00
this.identity = identity;
this.tcpServer = net.createServer(this.tcpConnectClient.bind(this));
2020-11-11 19:38:39 -05:00
}
2020-11-13 10:19:44 -05:00
tcpConnectClient(tcpSocket) {
const socket = new STPSocket(tcpSocket, this.identity);
socket.on('ready', () => {
this.emit('connection', socket);
})
2020-11-11 19:38:39 -05:00
}
2020-11-13 10:19:44 -05:00
listen(...args) {
this.tcpServer.listen(...args);
2020-11-11 19:38:39 -05:00
}
}
2020-11-13 10:19:44 -05:00
class STPSocket extends EventEmitter {
tcpSocket;
readyState = 0;
buffer = '';
externalIdentity;
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
get remoteAddress() {
return this.tcpSocket.remoteAddress;
}
get remoteIdentity() {
return this.externalIdentity.exportKey('pkcs8-public-pem');
}
constructor(tcpSocket, identity) {
super();
this.tcpSocket = tcpSocket;
this.identity = identity;
if(tcpSocket.readyState === 'open') this.handshake();
else this.tcpSocket.on('connect', this.handshake.bind(this));
this.tcpSocket.on('data', this.data.bind(this));
}
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
data(evt) {
// log.debug(evt.toString());
this.buffer += evt.toString();
this.processBuffer();
}
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
processBuffer() {
const parts = this.buffer.split(/(\x02[^\x02\x03]*\x03)/g);
this.buffer = '';
for(const message of parts) {
if(message.endsWith('\x03')) {
const obj = JSON.parse(message.substr(1, message.length - 2));
this.processMessage(obj);
} else {
this.buffer += message;
}
2020-11-11 19:38:39 -05:00
}
2020-11-13 10:19:44 -05:00
}
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
processMessage(obj) {
switch(obj.cmd) {
case 'KEY': {
if(this.readyState === 0) {
// log.debug('registering external key');
this.externalIdentity = new NodeRSA();
this.externalIdentity.importKey(obj.data.key, 'pkcs8-public-pem');
this.tcpSocket.write(new AckPacket().toBuffer());
this.readyState = 1;
}
break;
}
case 'ACK': {
if(this.readyState === 1) {
this.readyState = 2;
// log.debug('socket ready!');
this.emit('ready');
}
break;
}
2020-11-11 19:38:39 -05:00
}
2020-11-13 10:19:44 -05:00
}
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
handshake() {
// log.debug('begin handshake on socket');
const pk = this.identity.publicKey;
const packet = new KeyExchangePacket(pk);
const buffer = packet.toBuffer();
this.tcpSocket.write(buffer);
2020-11-11 19:38:39 -05:00
}
2020-11-13 10:19:44 -05:00
}
class STPPacket {
cmd = 'NOOP';
data = {};
meta = {};
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
toBuffer() {
return Buffer.from(`\x02${JSON.stringify({
cmd: this.cmd,
data: this.data,
meta: this.meta
})}\x03`);
2020-11-11 19:38:39 -05:00
}
2020-11-13 10:19:44 -05:00
}
2020-11-11 19:38:39 -05:00
2020-11-13 10:19:44 -05:00
class KeyExchangePacket extends STPPacket {
constructor(key, type = 'pkcs8-pem') {
super();
this.cmd = 'KEY';
this.data.key = key;
this.meta.type = type;
2020-11-11 19:38:39 -05:00
}
}
2020-11-13 10:19:44 -05:00
class AckPacket extends STPPacket {
constructor() {
super();
this.cmd = 'ACK';
}
}