const axios = require('axios'); const { exec } = require('child_process'); const { promisify } = require('util'); const fs = require('fs').promises; const execAsync = promisify(exec); /** * VPN Diagnostics - Check for IP and DNS leaks */ class VPNDiagnostics { /** * Get current public IP address */ static async getPublicIP() { try { const response = await axios.get('https://api.ipify.org?format=json', { timeout: 10000, validateStatus: () => true }); return response.data.ip; } catch (error) { throw new Error(`Failed to get public IP: ${error.message}`); } } /** * Get detailed IP information (location, ISP, etc.) */ static async getIPInfo(ip) { try { const response = await axios.get(`https://ipinfo.io/${ip || ''}/json`, { timeout: 10000, validateStatus: () => true }); return response.data; } catch (error) { console.warn('Could not get IP info:', error.message); return null; } } /** * Get current DNS servers from resolv.conf */ static async getDNSServers() { try { const resolvConf = await fs.readFile('/etc/resolv.conf', 'utf8'); const nameservers = resolvConf .split('\n') .filter(line => line.trim().startsWith('nameserver')) .map(line => line.split(/\s+/)[1]); return nameservers; } catch (error) { throw new Error(`Failed to read DNS servers: ${error.message}`); } } /** * Check if VPN interface (tun) exists */ static async checkVPNInterface() { try { const { stdout } = await execAsync('ip addr show tun0 2>/dev/null'); return { exists: true, details: stdout.trim() }; } catch (error) { return { exists: false, details: null }; } } /** * Get routing table */ static async getRoutingTable() { try { const { stdout } = await execAsync('ip route'); return stdout.trim().split('\n'); } catch (error) { throw new Error(`Failed to get routing table: ${error.message}`); } } /** * Test DNS resolution through current DNS servers */ static async testDNSResolution(domain = 'google.com') { try { const { stdout } = await execAsync(`nslookup ${domain} | grep -A1 "Name:" | tail -1`); return { success: true, result: stdout.trim() }; } catch (error) { return { success: false, error: error.message }; } } /** * Check for DNS leaks by testing against multiple DNS leak test services */ static async checkDNSLeaks() { const dnsServers = await this.getDNSServers(); // Check if DNS servers are common VPN DNS servers const commonVPNDNS = ['10.2.0.1', '10.2.0.2', '10.8.0.1', '10.0.0.1']; const isUsingVPNDNS = dnsServers.some(dns => commonVPNDNS.includes(dns) || dns.startsWith('10.')); // Check if DNS servers match common public DNS (potential leak) const commonPublicDNS = [ '8.8.8.8', '8.8.4.4', // Google '1.1.1.1', '1.0.0.1', // Cloudflare '9.9.9.9', // Quad9 ]; const isUsingPublicDNS = dnsServers.some(dns => commonPublicDNS.includes(dns)); return { dnsServers, isUsingVPNDNS, isUsingPublicDNS, potentialLeak: !isUsingVPNDNS && isUsingPublicDNS }; } /** * Run comprehensive VPN diagnostics */ static async runFullDiagnostics() { console.log('šŸ” Running VPN diagnostics...'); const results = { timestamp: new Date().toISOString(), publicIP: null, ipInfo: null, dnsServers: [], vpnInterface: null, routingTable: [], dnsLeakCheck: null, summary: { vpnActive: false, ipLeak: false, dnsLeak: false, issues: [] } }; try { // Get public IP results.publicIP = await this.getPublicIP(); console.log(`āœ“ Public IP: ${results.publicIP}`); // Get IP info results.ipInfo = await this.getIPInfo(results.publicIP); if (results.ipInfo) { console.log(`āœ“ Location: ${results.ipInfo.city}, ${results.ipInfo.country}`); console.log(`āœ“ ISP: ${results.ipInfo.org}`); } // Get DNS servers results.dnsServers = await this.getDNSServers(); console.log(`āœ“ DNS Servers: ${results.dnsServers.join(', ')}`); // Check VPN interface results.vpnInterface = await this.checkVPNInterface(); if (results.vpnInterface.exists) { console.log('āœ“ VPN interface (tun0) is UP'); results.summary.vpnActive = true; } else { console.log('āœ— VPN interface (tun0) NOT found'); results.summary.issues.push('VPN interface not found'); } // Get routing table results.routingTable = await this.getRoutingTable(); const defaultRoute = results.routingTable.find(route => route.startsWith('default')); if (defaultRoute) { console.log(`āœ“ Default route: ${defaultRoute}`); if (defaultRoute.includes('tun')) { console.log('āœ“ Traffic is routed through VPN'); } else { console.log('āœ— Traffic NOT routed through VPN'); results.summary.ipLeak = true; results.summary.issues.push('Traffic not routed through VPN interface'); } } // DNS leak check results.dnsLeakCheck = await this.checkDNSLeaks(); if (results.dnsLeakCheck.isUsingVPNDNS) { console.log('āœ“ Using VPN DNS servers'); } else if (results.dnsLeakCheck.potentialLeak) { console.log('āœ— Potential DNS leak detected'); results.summary.dnsLeak = true; results.summary.issues.push('Using non-VPN DNS servers'); } // Overall status if (results.summary.vpnActive && !results.summary.ipLeak && !results.summary.dnsLeak) { console.log('\nāœ“ VPN is working correctly - No leaks detected'); } else { console.log('\nāœ— VPN issues detected:'); results.summary.issues.forEach(issue => console.log(` - ${issue}`)); } } catch (error) { console.error('Error during diagnostics:', error); results.summary.issues.push(error.message); } return results; } /** * Quick check if VPN is working (used by status endpoint) */ static async quickCheck() { try { const [publicIP, dnsServers, vpnInterface] = await Promise.all([ this.getPublicIP(), this.getDNSServers(), this.checkVPNInterface() ]); return { publicIP, dnsServers, vpnInterfaceActive: vpnInterface.exists, timestamp: new Date().toISOString() }; } catch (error) { throw new Error(`Quick check failed: ${error.message}`); } } } module.exports = VPNDiagnostics;