From 0650417d938eda896b0008c6d94419f1836dbbdf Mon Sep 17 00:00:00 2001 From: git Date: Sun, 15 Jun 2025 13:58:33 +0100 Subject: [PATCH] Add main.js --- main.js | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 main.js diff --git a/main.js b/main.js new file mode 100644 index 0000000..c597908 --- /dev/null +++ b/main.js @@ -0,0 +1,148 @@ +// mdns-sync.js +// A single-file mDNS sync tool acting as client or server +// Uses mdns-server for mDNS and Express for HTTP + + +/********************************* +* +* 1. LIBRARY FUNCTIONS +* +*********************************/ + +// 0. HELPER FUNCTIONS +function convertBufferToString(a) {if (a.data) for (i in a.data) a.data[i] = a.data[i].toString(); return a;} + +// 1. Database functions +function saveDbToFile(data, filename) {return fs.writeFileSync(filename,typeof data === 'object'?JSON.stringify(data):data);} +function loadDbFromFile(filename) {if (!fs.existsSync(filename)) return {local:{},remote:{}}; else return JSON.parse(fs.readFileSync(filename));} + +// 2. Network functions +function getLocalNetworkAddressesIPs() { + const os = require('os'); + + const interfaces = os.networkInterfaces(); + const addresses = []; + + for (const name of Object.keys(interfaces)) { + for (const iface of interfaces[name]) { + if (iface.family === 'IPv4' && !iface.internal) { + addresses.push(iface.address); + } + } + } + return addresses; + +} + +function sniffmDNSLocalPackets() { + + // 1. Listen for responses + mdns.on('response', function(response) { + // console.log("got a new mDNS response.",response); + for (k in response.answers) { + let answer = response.answers[k]; + + // 1. Handle DEVICES for a given SERVICE + if (answer.type == "PTR") { + // 1.1 Filter by RELEVANT SERVICE TYPES + if (!["in-addr.arpa","_googlecast._tcp.local","_tcp.local"].some(suffix => answer.name.endsWith(suffix))) return; + + // 1.2 Initialize POINTERS (services) sub-database + if (!db.local.PTR) db.local.PTR = {}; + + // 1.3 Initialize this SERVICE TYPE (e.g. _googlecast._tcp.local), if needed + if (!db.local.PTR[answer.name]) { + db.local.PTR[answer.name] = [answer.data]; + } else { + // Add a new DEVICE to this SERVICE TYPE if needed + if (!db.local.PTR[answer.name].includes(answer.data)) + db.local.PTR[answer.name].push(answer.data); + } + } + // 2. Handle DEVICE IP resolution + if (answer.type == "A") { + // 2.1 Initialize ADDRESSES sub-database + if (!db.local.A) db.local.A = {}; + db.local.A[answer.name] = answer.data; + } + + if (["TXT","SRV"].includes(answer.type)) { + // Initialize this device / entry, if needed + if (db.local[answer.name] === undefined) db.local[answer.name] = {"TXT":null,"SRV":null}; + + db.local[answer.name][answer.type] = answer.data; + } + saveDbToFile(db,DB_FILENAME); + } + }) + + // 2. Listen for queries + mdns.on('query', function(query) {/* console.log("query requested",query);*/}); + + // 3. Handle the server being destroyed + mdns.on('destroyed', function () {console.log('Server destroyed.');process.exit(0);}); + + // 4. Handle the onReady event + mdns.on('ready', function () { + + console.log("mDNS server is ready..."); + // mdns.query({questions:[{ name: '_:googlecast._tcp.local', type: 'PTR', class: 'IN'}]} ); + }) + + // initialize the server now that we are watching for events + mdns.initServer() + +} +function injectmDNSPacketLocally(type,name,data,flush = true,_class = 'IN',ttl = 120) { + console.log(`sending packet ${name} of ${type} with data: ${data}`); + mdns.respond({answers:[ + { + name: name, + type: type, + ttl: ttl, + class: _class, + flush: flush, + data: data + } +]});} + + + +/********************************* +* +* 100. MAIN / IMPERATIVE CODE +* +*********************************/ + +const fs = require("fs"); +const DB_FILENAME = 'database.json'; +const db = loadDbFromFile(DB_FILENAME); +const LOCAL_IP_ADDR = getLocalNetworkAddressesIPs()[0]; +const mdns = require('mdns-server')({interface: LOCAL_IP_ADDR,reuseAddr: true,loopback: false,noInit: true}); +sniffmDNSLocalPackets(); + +setInterval(function(){ + // INJECT PTR - Pointers services + for (name in db.remote.PTR) { + for (i in db.remote.PTR[name]) + injectmDNSPacketLocally('PTR',name,db.remote.PTR[name][i]); + } + // INJECT A - Addresses + for (name in db.remote.A) { + injectmDNSPacketLocally('A',name,db.remote.A[name]); + } + + + // INJECT SRV,TXT + for (name in db.remote) { + if (["PTR","A"].includes(name)) continue; + if (db.remote[name].SRV !== null) + injectmDNSPacketLocally('SRV',name,db.remote[name].SRV); + if (db.remote[name].TXT !== null) + injectmDNSPacketLocally('TXT',name,Buffer.from(db.remote[name].TXT, 'utf8')); + + } + + + +},5000); \ No newline at end of file