udphole

Basic UDP wormhole proxy
git clone git://git.finwo.net/app/udphole
Log | Files | Refs | README | LICENSE

listen-relearn-tcp.js (5199B)


      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 sendUdpFromPort(srcPort, dstPort, message) {
     17   return new Promise((resolve, reject) => {
     18     const sock = dgram.createSocket('udp4');
     19     sock.bind(srcPort, '127.0.0.1', () => {
     20       const buf = Buffer.from(message);
     21       sock.send(buf, 0, buf.length, dstPort, '127.0.0.1', (err) => {
     22         if (err) {
     23           sock.close();
     24           reject(err);
     25         } else {
     26           setTimeout(() => {
     27             sock.close();
     28             resolve();
     29           }, 50);
     30         }
     31       });
     32     });
     33     sock.on('error', reject);
     34   });
     35 }
     36 
     37 async function runTest() {
     38   let daemon = null;
     39   let apiSock = null;
     40   let echoServer = null;
     41   let returnCode = 0;
     42 
     43   console.log('=== Listen Socket Re-learn Test ===');
     44   console.log('Testing: listen socket re-learns remote address when different client sends\n');
     45 
     46   try {
     47     console.log('1. Spawning daemon...');
     48     daemon = await spawnDaemon(CONFIG_PATH);
     49     console.log(`   Daemon started (PID: ${daemon.pid})`);
     50 
     51     console.log('2. Connecting to API...');
     52     apiSock = await connectApi(API_PORT);
     53     console.log('   Connected to API');
     54 
     55     console.log('3. Authenticating...');
     56     let resp = await apiCommand(apiSock, 'auth', 'finwo', 'testsecret');
     57     console.log(`   Auth response: ${resp}`);
     58     if (resp !== 'OK') throw new Error('Authentication failed');
     59 
     60     console.log('4. Creating session...');
     61     resp = await apiCommand(apiSock, 'session.create', 'test-relearn', '60');
     62     console.log(`   Session create: ${resp}`);
     63 
     64     console.log('5. Creating listen socket...');
     65     resp = await apiCommand(apiSock, 'session.socket.create.listen', 'test-relearn', 'listener');
     66     const listenPort = resp[0];
     67     console.log(`   Listen socket port: ${listenPort}`);
     68 
     69     console.log('6. Starting echo server...');
     70     echoServer = await createUdpEchoServer();
     71     console.log(`   Echo server on port: ${echoServer.port}`);
     72 
     73     console.log('7. Creating connect socket to echo server...');
     74     resp = await apiCommand(apiSock, 'session.socket.create.connect', 'test-relearn', 'relay', '127.0.0.1', echoServer.port);
     75     console.log(`   Connect socket: ${resp}`);
     76 
     77     console.log('8. Creating forward: listener -> relay...');
     78     resp = await apiCommand(apiSock, 'session.forward.create', 'test-relearn', 'listener', 'relay');
     79     console.log(`   Forward create: ${resp}`);
     80 
     81     console.log('9. First client sending "from-A" from port 50001...');
     82     await sendUdpFromPort(50001, listenPort, 'from-A');
     83     console.log('   Sent "from-A"');
     84 
     85     let messages = echoServer.getMessages();
     86     let start = Date.now();
     87     while (messages.length === 0 && Date.now() - start < TIMEOUT) {
     88       await new Promise(r => setTimeout(r, 50));
     89       messages = echoServer.getMessages();
     90     }
     91 
     92     if (messages.length === 0) {
     93       throw new Error('Timeout: no message received from first client');
     94     }
     95 
     96     const msgA = messages[0];
     97     console.log(`    Received: "${msgA.data}" from ${msgA.rinfo.address}:${msgA.rinfo.port}`);
     98 
     99     if (msgA.data !== 'from-A') {
    100       throw new Error(`Expected "from-A", got "${msgA.data}"`);
    101     }
    102     console.log('   āœ“ First client connection established, listen socket learned address');
    103 
    104     echoServer.clearMessages();
    105 
    106     console.log('10. Second client sending "from-B" from port 50002...');
    107     await sendUdpFromPort(50002, listenPort, 'from-B');
    108     console.log('   Sent "from-B"');
    109 
    110     messages = echoServer.getMessages();
    111     start = Date.now();
    112     while (messages.length === 0 && Date.now() - start < TIMEOUT) {
    113       await new Promise(r => setTimeout(r, 50));
    114       messages = echoServer.getMessages();
    115     }
    116 
    117     if (messages.length === 0) {
    118       throw new Error('Timeout: no message received from second client');
    119     }
    120 
    121     const msgB = messages[0];
    122     console.log(`    Received: "${msgB.data}" from ${msgB.rinfo.address}:${msgB.rinfo.port}`);
    123 
    124     if (msgB.data === 'from-B') {
    125       console.log('\nāœ“ PASS: Listen socket correctly re-learned new remote address');
    126       console.log('   Second client (from port 50002) was able to communicate');
    127       console.log('   through the same listen socket after the first client.');
    128     } else if (msgB.data === 'from-A') {
    129       console.log('\nāœ— FAIL: Listen socket did NOT re-learn new remote address');
    130       console.log('   The second client\'s packet was sent back to the first client');
    131       console.log('   instead of the second client. This is the bug to fix.');
    132       console.log(`   Expected to receive from port 50002, but responses went to port ${msgA.rinfo.port}`);
    133     } else {
    134       throw new Error(`Unexpected message: "${msgB.data}"`);
    135     }
    136 
    137   } catch (err) {
    138     console.error(`\nāœ— FAIL: ${err.message}`);
    139     returnCode = 1;
    140   } finally {
    141     if (echoServer) echoServer.socket.close();
    142     if (apiSock) apiSock.end();
    143     if (daemon) await killDaemon(daemon);
    144     await killAllDaemons();
    145     process.exit(returnCode);
    146   }
    147 }
    148 
    149 runTest();