UNPKG

absenat

Version:

dedicated messaging service core

248 lines (245 loc) 8.79 kB
/** * @author mr-exception * @description this method connects to a client * @param {array} arguments * @param {function} finished */ const connect = (arguments, finished) => { // --------- validation inputs ---------- const ip = arguments[0]; if (!(new RegExp('(localhost)|(^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$)').test(ip))) { console.log(chalk.red('Err: ip format is not correct')); return finished(); } const port = arguments[1]; if (parseInt(port) < 3000 || parseInt(port) > 9000) { console.log(chalk.red('Err: port is not correct. valid port range is between 3000 and 9000')); return finished(); } // -------- connecting to dst client -------- console.log(`connecting to ${ip}:${port}...`); const socket = ioClient(`http://${ip}:${port}`); /** * =============================================== * Step 0: sends public key to dst node * =============================================== */ socket.emit('connect_0', config.nickname, config.public_key); // sets timeout for connection ack and ans event const connection_timeout = setInterval(() => { socket.removeEventListener('connect_1'); console.log(chalk.red(`NetErr: connection to ${ip}:${port} timed out`)); clearInterval(connection_timeout); // remoev the timeout finished(); }, config.max_timeout); /** * =============================================== * Step 1: recieves dst public key and dtn encrypted by src public key * =============================================== */ socket.on('connect_1', (from, cipher) => { clearInterval(connection_timeout); // cancel the timeout const answer = JSON.parse(myPrivateKey.decrypt(cipher)); const contactPublicKey = answer.public_key; const contactPublicKeyNode = createPublicKey(contactPublicKey); console.log(`${socket.id} started connection as ${from}`); connections[socket.id] = { nickname: from, socket, public_key: contactPublicKey, }; /** * =============================================== * Step 2: sends dtn and dst encrypted by dst public key to dst * =============================================== */ const stn = parseInt(Math.random() * 99999); const message = contactPublicKeyNode.encrypt(JSON.stringify({ dst: from, stn, dtn: answer.dtn, })); socket.emit('connect_2', config.nickname, message); /** * =============================================== * Step 3: recieves stn from dst to complete the trust level * =============================================== */ socket.on('connect_3', (title, cipher) => { console.log(chalk.green('DTN is valid. finished the trusting level!')); const answer = parseInt(myPrivateKey.decrypt(cipher).toString()); if (answer === stn) { db.addBridge(title, 'NuLL', contactPublicKey, ip, port); console.log(chalk.green('STN is valid.')); // hearbeat progress setInterval(() => { socket.emit('hi_im_normal'); }, config.max_timeout); socket.on('hi_im_bridge', () => { db.bridgeHeartBeat(contactPublicKey); }); handlers.messages(socket); /** * =============================================== * Step 4: tells dst node that finished the trusing level * =============================================== */ socket.emit('connect_4', config.nickname); finished(); } else { console.log(chalk.red('Err: invalid STN.')); } }); }); }; const requestContact = (arguments, finished) => { const contact_public_key = arguments[0]; if (!contact_public_key) { console.log(chalk.red('Err: please enter contact public key')); return finished(); } // generating trust string const ts = uuidv4(); // unique universal id for message message_id = uuidv4(); // creating message for dst node const chunks = chunkMessage(message_id, { a: 'indirect_connection', // action pk: config.public_key, // source node public key ts, // trust string }, contact_public_key); db.getBridges(bridges => { let found_active_bridge = false; for (const i in bridges) { const bridge = bridges[i]; const bridgePublicKey = createPublicKey(bridge.public_key); const socket = getSocket(bridge.public_key); if (socket) { let success_count = 0; let count = chunks.length; for (const i in chunks) { socket.emit('indirect_message', config.public_key, bridgePublicKey.encrypt(chunks[i])); socket.once('indirect_packet_recieved', () => { success_count++; if (success_count === count) console.log(`\rpacket sent (c: ${count}, id: ${message_id}).`); finished(); }); } socket.once('indirect_packet_recieved', () => { console.log(`\rrequested contact from bridge ${chalk.blue(bridge.title)}.`); }); found_active_bridge = true; } } if (!found_active_bridge) { console.log(chalk.red("Err: you don't have any active bridge.")); } return finished(); }, () => { console.log(chalk.red("Err: you don't have any active bridge, please connect to a bridge first.")); }); } /** * @author mr-exception * @description this method shows brdiges list * @param {array} arguments * @param {function} finished */ const showBridges = (arguments, finished) => { db.getBridges(bridges => { for (const i in bridges) { const bridge = bridges[i]; console.log(`${bridge.id} => ${bridge.title} : (${(bridge.last_time_connected < Date.now() - config.max_timeout) ? 'offline' : 'online'})`); } return finished(); }, () => { console.log(chalk.red(`Err: you don't have any bridge, plaese enter connect <ip> <port> to add new bridge`)); return finished(); }) } /** * @author mr-exception * @description connects to a specified bridge by its id * @param {array} arguments * @param {function} finished */ const connectBridge = (arguments, finished) => { const bridge_id = arguments[0]; if (!bridge_id) { console.log(chalk.red('Err: bridge enter required.')); return finished(); } db.getBridge(bridge_id, bridge => { if (bridge.last_time_connected < Date.now() - config.max_timeout) { connect([bridge.ip, bridge.port], finished); } else { console.log(chalk.blue('bridge is connected!')); return finished(); } }, () => { console.log(chalk.red('Err: bridge not found.')); return finished(); }); } /** * @author mr-exception * @description connects to all bridges * @param {*} arguments * @param {*} finished */ const connectAllBridges = (arguments, finished) => { db.getBridges(bridges => { bridges.forEach(bridge => { if (bridge.last_time_connected < Date.now() - config.max_timeout) { console.log(`connecting bridge ${chalk.blue(bridge.title)}...`); connect([bridge.ip, bridge.port], finished); } else { console.log(`bridge ${chalk.blue(bridge.title)} is connected!`); return finished(); } }); }, () => { console.log(chalk.red(`Err: you don't have any bridge, plaese enter connect <ip> <port> to add new bridge`)); return finished(); }); } /** * @author mr-exception * @description gets latest information of a user by it's public key * @param {array} arguments * @param {function} finished */ const getProfile = (arguments, finished) => { const public_key = arguments[0]; if (!public_key) { console.log(chalk.red('Err: public key required, you have to enter the valid public key')); return finished(); } for (const index in connections) { const connection = connections[index]; console.log(`asking profile from ${connection.nickname}`); const public_key_node = createPublicKey(connection.public_key); const cipher = public_key_node.encrypt(public_key); connection.socket.emit('get_profile', config.public_key, cipher); connection.socket.once('contact_not_found', (contact_public_key, cipher) => { console.log(chalk.red(`Err: node ${connection.nickname} did not found you target.`)); finished(); }); connection.socket.once('contact_found', (contact_public_key, cipher) => { const response = JSON.parse(myPrivateKey.decrypt(cipher)); // db.addIndirectConnection(response.nickname, response.public_key); console.log(chalk.green(`contact found.`)); console.log(response); finished(); }); } } module.exports = { getProfile, showBridges, connectBridge, connectAllBridges, connect, requestContact, }