connect-drop-unknown.js (9138B)
1 const path = require('path'); 2 const dgram = require('dgram'); 3 const { 4 spawnDaemon, 5 killDaemon, 6 killAllDaemons, 7 connectApi, 8 apiCommand, 9 createUdpEchoServer, 10 TIMEOUT 11 } = require('./helpers'); 12 13 const CONFIG_PATH = path.join(__dirname, 'config-tcp.ini'); 14 const API_PORT = 9123; 15 16 function sendAndRecvUdp(srcPort, dstPort, message) { 17 return new Promise((resolve, reject) => { 18 const sock = dgram.createSocket('udp4'); 19 let resolved = false; 20 21 const timer = setTimeout(() => { 22 if (!resolved) { 23 resolved = true; 24 sock.close(); 25 resolve(null); 26 } 27 }, TIMEOUT); 28 29 sock.on('message', (msg, rinfo) => { 30 if (!resolved) { 31 resolved = true; 32 clearTimeout(timer); 33 sock.close(); 34 resolve({ data: msg.toString(), rinfo }); 35 } 36 }); 37 38 sock.bind(srcPort, '127.0.0.1', () => { 39 const buf = Buffer.from(message); 40 sock.send(buf, 0, buf.length, dstPort, '127.0.0.1', (err) => { 41 if (err && !resolved) { 42 resolved = true; 43 clearTimeout(timer); 44 sock.close(); 45 reject(err); 46 } 47 }); 48 }); 49 50 sock.on('error', (err) => { 51 if (!resolved) { 52 resolved = true; 53 clearTimeout(timer); 54 sock.close(); 55 reject(err); 56 } 57 }); 58 }); 59 } 60 61 function sendUdpFromPort(srcPort, dstPort, message) { 62 return new Promise((resolve, reject) => { 63 const sock = dgram.createSocket('udp4'); 64 sock.bind(srcPort, '127.0.0.1', () => { 65 const buf = Buffer.from(message); 66 sock.send(buf, 0, buf.length, dstPort, '127.0.0.1', (err) => { 67 if (err) { 68 sock.close(); 69 reject(err); 70 } else { 71 setTimeout(() => { 72 sock.close(); 73 resolve(); 74 }, 50); 75 } 76 }); 77 }); 78 sock.on('error', reject); 79 }); 80 } 81 82 async function runTest() { 83 let daemon = null; 84 let apiSock = null; 85 let echoServer = null; 86 let returnCode = 0; 87 88 console.log('=== Connect Socket Drop Unknown Test ==='); 89 console.log('Testing: connect mode sockets drop traffic from unknown remote addresses\n'); 90 91 try { 92 console.log('1. Spawning daemon...'); 93 daemon = await spawnDaemon(CONFIG_PATH); 94 console.log(` Daemon started (PID: ${daemon.pid})`); 95 96 console.log('2. Connecting to API...'); 97 apiSock = await connectApi(API_PORT); 98 console.log(' Connected to API'); 99 100 console.log('3. Authenticating...'); 101 let resp = await apiCommand(apiSock, 'auth', 'finwo', 'testsecret'); 102 console.log(` Auth response: ${resp}`); 103 if (resp !== 'OK') throw new Error('Authentication failed'); 104 105 console.log('4. Creating session...'); 106 resp = await apiCommand(apiSock, 'session.create', 'test-connect-drop', '60'); 107 console.log(` Session create: ${resp}`); 108 109 console.log('5. Creating listen socket...'); 110 resp = await apiCommand(apiSock, 'session.socket.create.listen', 'test-connect-drop', 'listen'); 111 const listenPort = resp[0]; 112 console.log(` Listen socket port: ${listenPort}`); 113 114 console.log('6. Starting echo server...'); 115 echoServer = await createUdpEchoServer(); 116 console.log(` Echo server on port: ${echoServer.port}`); 117 118 console.log('7. Creating connect socket to echo server...'); 119 resp = await apiCommand(apiSock, 'session.socket.create.connect', 'test-connect-drop', 'connect', '127.0.0.1', echoServer.port); 120 const connectPort = resp[0]; 121 console.log(` Connect socket local port: ${connectPort}`); 122 123 console.log('8. Creating forward: listen -> connect...'); 124 resp = await apiCommand(apiSock, 'session.forward.create', 'test-connect-drop', 'listen', 'connect'); 125 console.log(` Forward create: ${resp}`); 126 127 console.log('9. Creating forward: connect -> listen (for echo responses)...'); 128 resp = await apiCommand(apiSock, 'session.forward.create', 'test-connect-drop', 'connect', 'listen'); 129 console.log(` Forward create: ${resp}`); 130 131 console.log('10. Waiting for session to initialize...'); 132 await new Promise(r => setTimeout(r, 100)); 133 134 console.log('\n=== Step 1: Happy path - active to listen, through proxy, back to active ==='); 135 console.log('11. Sending "step1-test" from port 50001 to listen socket...'); 136 137 const recvPromise1 = sendAndRecvUdp(50001, listenPort, 'step1-test'); 138 console.log(' Sent "step1-test", waiting for response...'); 139 140 let messages = echoServer.getMessages(); 141 let start = Date.now(); 142 while (messages.length === 0 && Date.now() - start < TIMEOUT) { 143 await new Promise(r => setTimeout(r, 50)); 144 messages = echoServer.getMessages(); 145 } 146 147 if (messages.length === 0) { 148 throw new Error('Timeout: no message received by echo server in step 1'); 149 } 150 151 const msgStep1 = messages[0]; 152 console.log(` Echo server received: "${msgStep1.data}" from ${msgStep1.rinfo.address}:${msgStep1.rinfo.port}`); 153 154 if (msgStep1.data !== 'step1-test') { 155 throw new Error(`Expected "step1-test", got "${msgStep1.data}"`); 156 } 157 console.log(' ✓ Echo server received the packet'); 158 159 echoServer.clearMessages(); 160 161 const responseToSender1 = await recvPromise1; 162 if (responseToSender1 === null) { 163 throw new Error('Timeout: no response received back at sender port 50001'); 164 } 165 console.log(` ✓ Sender received echo response: "${responseToSender1.data}" from ${responseToSender1.rinfo.address}:${responseToSender1.rinfo.port}`); 166 167 if (responseToSender1.data !== 'step1-test') { 168 throw new Error(`Expected echo "step1-test", got "${responseToSender1.data}"`); 169 } 170 171 console.log('\n=== Step 2: Verify connect socket drops unknown source ==='); 172 console.log('12. Sending "step2-direct" directly to connect socket from port 50002...'); 173 174 const recvPromise2 = sendAndRecvUdp(50002, connectPort, 'step2-direct'); 175 console.log(' Sent "step2-direct", waiting for response...'); 176 177 messages = echoServer.getMessages(); 178 start = Date.now(); 179 while (Date.now() - start < 500) { 180 await new Promise(r => setTimeout(r, 50)); 181 messages = echoServer.getMessages(); 182 } 183 184 if (messages.length > 0) { 185 console.log(` ✗ FAIL: Echo server received packet from connect socket (should have been dropped)`); 186 console.log(` Received: "${messages[0].data}" from ${messages[0].rinfo.address}:${messages[0].rinfo.port}`); 187 throw new Error('Connect socket did not drop traffic from unknown source'); 188 } 189 console.log(' ✓ Echo server did NOT receive the packet (correctly dropped)'); 190 191 const responseToSender2 = await recvPromise2; 192 if (responseToSender2 !== null) { 193 console.log(` ✗ FAIL: Sender received response from connect socket (should have been dropped)`); 194 console.log(` Received: "${responseToSender2.data}" from ${responseToSender2.rinfo.address}:${responseToSender2.rinfo.port}`); 195 throw new Error('Connect socket did not drop traffic - response sent back to sender'); 196 } 197 console.log(' ✓ Sender did NOT receive any response (correctly dropped)'); 198 console.log('\n✓ PASS: Connect socket correctly dropped traffic from unknown source'); 199 200 console.log('\n=== Step 3: Verify listen->connect forward still works ==='); 201 echoServer.clearMessages(); 202 203 console.log('13. Sending "step3-retry" from port 50001 to listen socket again...'); 204 205 const recvPromise3 = sendAndRecvUdp(50001, listenPort, 'step3-retry'); 206 console.log(' Sent "step3-retry", waiting for response...'); 207 208 messages = echoServer.getMessages(); 209 start = Date.now(); 210 while (messages.length === 0 && Date.now() - start < TIMEOUT) { 211 await new Promise(r => setTimeout(r, 50)); 212 messages = echoServer.getMessages(); 213 } 214 215 if (messages.length === 0) { 216 throw new Error('Timeout: no message received by echo server in step 3'); 217 } 218 219 const msgStep3 = messages[0]; 220 console.log(` Echo server received: "${msgStep3.data}" from ${msgStep3.rinfo.address}:${msgStep3.rinfo.port}`); 221 222 if (msgStep3.data !== 'step3-retry') { 223 throw new Error(`Expected "step3-retry", got "${msgStep3.data}"`); 224 } 225 console.log(' ✓ Echo server received the packet'); 226 227 echoServer.clearMessages(); 228 229 const responseToSender3 = await recvPromise3; 230 if (responseToSender3 === null) { 231 throw new Error('Timeout: no response received back at sender port 50001 in step 3'); 232 } 233 console.log(` ✓ Sender received echo response: "${responseToSender3.data}" from ${responseToSender3.rinfo.address}:${responseToSender3.rinfo.port}`); 234 235 if (responseToSender3.data !== 'step3-retry') { 236 throw new Error(`Expected echo "step3-retry", got "${responseToSender3.data}"`); 237 } 238 239 console.log('\n✓ PASS: Connect socket drop unknown test completed successfully'); 240 241 } catch (err) { 242 console.error(`\n✗ FAIL: ${err.message}`); 243 returnCode = 1; 244 } finally { 245 if (echoServer) echoServer.socket.close(); 246 if (apiSock) apiSock.end(); 247 if (daemon) await killDaemon(daemon); 248 await killAllDaemons(); 249 process.exit(returnCode); 250 } 251 } 252 253 runTest();