import TCPOverUDP from './tcp-over-udp'; import crypto from 'crypto'; enum SSTPMessageType { CONNECT_REQUEST = 0x00000001, CONNECT_ACK = 0x00000002, CALL_CONNECT_REQUEST = 0x00000003, CALL_CONNECT_ACK = 0x00000004, CALL_CONNECTED = 0x00000005, CALL_ABORT = 0x00000006, DISCONNECT = 0x00000007, ECHO_REQUEST = 0x00000008, ECHO_RESPONSE = 0x00000009, DATA = 0x0000000A, } class SSTPOverUDP { private tcp: TCPOverUDP; private state: string = 'CLOSED'; private sessionId: Buffer = crypto.randomBytes(4); constructor(private remoteAddress: string, private remotePort: number) { this.tcp = new TCPOverUDP(remoteAddress, remotePort); } async connect(): Promise { await this.tcp.connect(); this.state = 'CONNECT_SENT'; await this.sendConnectRequest(); } private async sendConnectRequest(): Promise { const connectRequest = this.createSSTPPacket(SSTPMessageType.CONNECT_REQUEST, Buffer.alloc(0)); await this.tcp.send(connectRequest); } private createSSTPPacket(messageType: SSTPMessageType, data: Buffer): Buffer { const header = Buffer.alloc(8); header.writeUInt8(1, 0); // Version header.writeUInt8(0, 1); // Reserved header.writeUInt16BE(8 + data.length, 2); // Length header.writeUInt32BE(messageType, 4); // Message Type return Buffer.concat([header, data]); } async send(data: Buffer): Promise { if (this.state !== 'ESTABLISHED') { throw new Error('SSTP connection not established'); } const dataPacket = this.createSSTPPacket(SSTPMessageType.DATA, data); await this.tcp.send(dataPacket); } private async handleIncomingMessage(msg: Buffer): Promise { const messageType = msg.readUInt32BE(4); const data = msg.slice(8); switch (messageType) { case SSTPMessageType.CONNECT_ACK: if (this.state === 'CONNECT_SENT') { this.state = 'CALL_CONNECT_SENT'; await this.sendCallConnectRequest(); } break; case SSTPMessageType.CALL_CONNECT_ACK: if (this.state === 'CALL_CONNECT_SENT') { this.state = 'ESTABLISHED'; console.log('SSTP connection established'); } break; case SSTPMessageType.DATA: console.log('Received data:', data.toString()); break; case SSTPMessageType.DISCONNECT: await this.close(); break; } } private async sendCallConnectRequest(): Promise { const callConnectRequest = this.createSSTPPacket(SSTPMessageType.CALL_CONNECT_REQUEST, this.sessionId); await this.tcp.send(callConnectRequest); } async close(): Promise { const disconnectPacket = this.createSSTPPacket(SSTPMessageType.DISCONNECT, Buffer.alloc(0)); await this.tcp.send(disconnectPacket); this.state = 'CLOSED'; await this.tcp.close(); } } export default SSTPOverUDP;