96 lines
3.4 KiB
JavaScript
96 lines
3.4 KiB
JavaScript
import debug from 'debug';
|
|
import net from 'net';
|
|
const log = debug('pipenet:tunnel-server');
|
|
/**
|
|
* A single TCP server that handles all tunnel connections.
|
|
* Clients send their ID as the first message, and the server routes
|
|
* the connection to the appropriate handler.
|
|
*/
|
|
export class TunnelServer {
|
|
handlers;
|
|
server;
|
|
constructor() {
|
|
this.handlers = new Map();
|
|
this.server = net.createServer((socket) => this.onConnection(socket));
|
|
this.server.on('error', (err) => {
|
|
if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT')
|
|
return;
|
|
console.error('TunnelServer error:', err);
|
|
});
|
|
}
|
|
close() {
|
|
this.server.close();
|
|
}
|
|
listen(port, address) {
|
|
return new Promise((resolve) => {
|
|
if (address) {
|
|
this.server.listen(port, address, () => {
|
|
log('tunnel server listening on %s:%d', address, port);
|
|
resolve();
|
|
});
|
|
}
|
|
else {
|
|
this.server.listen(port, () => {
|
|
log('tunnel server listening on port %d', port);
|
|
resolve();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
registerHandler(clientId, handler) {
|
|
log('registering handler for client: %s', clientId);
|
|
this.handlers.set(clientId, handler);
|
|
}
|
|
unregisterHandler(clientId) {
|
|
log('unregistering handler for client: %s', clientId);
|
|
this.handlers.delete(clientId);
|
|
}
|
|
onConnection(socket) {
|
|
log('new connection from %s:%s', socket.remoteAddress, socket.remotePort);
|
|
// Set a timeout for receiving the client ID
|
|
const timeout = setTimeout(() => {
|
|
log('timeout waiting for client ID');
|
|
socket.destroy();
|
|
}, 5000);
|
|
// Buffer for accumulating data until we get a newline
|
|
let buffer = '';
|
|
const onData = (data) => {
|
|
buffer += data.toString();
|
|
const newlineIndex = buffer.indexOf('\n');
|
|
if (newlineIndex === -1) {
|
|
// Haven't received a complete client ID yet
|
|
if (buffer.length > 100) {
|
|
// Protect against buffer overflow
|
|
log('client ID too long');
|
|
clearTimeout(timeout);
|
|
socket.destroy();
|
|
}
|
|
return;
|
|
}
|
|
// Got the client ID
|
|
clearTimeout(timeout);
|
|
socket.removeListener('data', onData);
|
|
const clientId = buffer.substring(0, newlineIndex).trim();
|
|
const remaining = buffer.substring(newlineIndex + 1);
|
|
log('received client ID: %s', clientId);
|
|
const handler = this.handlers.get(clientId);
|
|
if (!handler) {
|
|
log('no handler for client: %s', clientId);
|
|
socket.destroy();
|
|
return;
|
|
}
|
|
// If there's remaining data after the client ID, unshift it back
|
|
if (remaining.length > 0) {
|
|
socket.unshift(Buffer.from(remaining));
|
|
}
|
|
// Pass the socket to the handler
|
|
handler(socket);
|
|
};
|
|
socket.on('data', onData);
|
|
socket.once('error', () => {
|
|
clearTimeout(timeout);
|
|
socket.destroy();
|
|
});
|
|
}
|
|
}
|
|
//# sourceMappingURL=TunnelServer.js.map
|