const fs = require('fs'); const path = require('path'); class CgnatParser { constructor() { this._data = null; } /** * Carrega e parseia o nat_list.txt (lazy load). * Retorna Map> */ load() { if (this._data) return this._data; const filePath = path.join(__dirname, 'nat_list.txt'); const lines = fs.readFileSync(filePath, 'utf8').split('\n'); const data = new Map(); let currentPublicIp = null; for (const line of lines) { const trimmed = line.trim(); const natMatch = trimmed.match(/^NAT Address:\s+(\S+)/); if (natMatch) { currentPublicIp = natMatch[1]; if (!data.has(currentPublicIp)) { data.set(currentPublicIp, []); } continue; } if (currentPublicIp) { const entryMatch = trimmed.match(/^(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+to\s+(\d+)/); if (entryMatch) { data.get(currentPublicIp).push({ privateIP: entryMatch[1], portStart: parseInt(entryMatch[2], 10), portEnd: parseInt(entryMatch[3], 10) }); } } } this._data = data; console.log(`📋 CgnatParser: ${data.size} IPs pĂșblicos carregados`); return data; } /** * Dado um IP pĂșblico e porta, retorna a entrada { privateIP, portStart, portEnd } ou null. */ lookupByPort(publicIp, port) { const data = this.load(); const entries = data.get(publicIp); if (!entries) return null; const portNum = parseInt(port, 10); return entries.find(e => portNum >= e.portStart && portNum <= e.portEnd) || null; } /** * Retorna todos os IPs privados mapeados a um IP pĂșblico, ou null se nĂŁo encontrado. */ lookupAll(publicIp) { const data = this.load(); return data.get(publicIp) || null; } /** * Verifica se um IP pĂșblico existe no mapa CGNAT. */ hasPublicIp(publicIp) { const data = this.load(); return data.has(publicIp); } } module.exports = new CgnatParser();