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/index.js

143 lines
3.2 KiB
JavaScript

const net = require('net');
const EventEmitter = require('events');
const NodeRSA = require('node-rsa');
const log = require('signale').scope('stp');
const {
KeyExchangePacket,
AckPacket
} = require('./packets');
const { rejects } = require('assert');
module.exports.createServer = function({identity = {}, port = 5000} = {}, cb = _ => _) {
const server = new Server(identity, port);
server.on('connection', connection => {
cb(connection);
});
// return 5;
}
module.exports.connect = function({
identity,
port,
ip
}) {
return new STPSocket(net.connect(port, ip), identity);
}
class Server extends EventEmitter {
tcpServer;
identity;
port;
constructor(identity, port) {
super();
this.identity = identity;
this.port = port;
this.openServer();
}
openServer() {
log.info(`opening STP server on ${this.port}`);
this.tcpServer = net.createServer(this.tcpConnectClient.bind(this));
this.tcpServer.on('error', e => {
log.warn(e)
setTimeout(this.openServer.bind(this), 5000);
})
this.tcpServer.listen(this.port);
}
tcpConnectClient(tcpSocket) {
const socket = new STPSocket(tcpSocket, this.identity);
socket.on('ready', () => {
this.emit('connection', socket);
})
}
}
class STPSocket extends EventEmitter {
tcpSocket;
readyState = 0;
buffer = '';
externalKey;
identity;
get loopback() {
return this.identity.publicKey ===
this.externalKey.exportKey('pkcs8-public-pem');
}
get remoteAddress() {
return this.tcpSocket.remoteAddress;
}
get remoteIdentity() {
return this.externalKey.exportKey('pkcs8-public-pem');
}
get open() {
return this.tcpSocket.readyState === 'open'
}
get secured() {
return this.readyState;
}
constructor(tcpSocket, identity) {
super();
this.tcpSocket = tcpSocket;
this.identity = identity;
if(this.open) this.handshake();
else this.tcpSocket.on('connect', this.handshake.bind(this));
this.tcpSocket.on('data', this.data.bind(this));
this.tcpSocket.on('error', (...args) => this.emit('error', ...args));
this.tcpSocket.on('close', (...args) => this.emit('close', ...args));
}
data(evt) {
this.buffer += evt.toString();
this.processBuffer();
}
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;
}
}
}
processMessage(obj) {
switch(obj.cmd) {
case 'KEY': {
if(this.readyState === 0) {
this.externalKey = new NodeRSA();
this.externalKey.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;
this.emit('ready');
}
break;
}
}
}
handshake() {
const pk = this.identity.publicKey;
const packet = new KeyExchangePacket(pk);
const buffer = packet.toBuffer();
this.tcpSocket.write(buffer);
}
}