245 lines
6.7 KiB
JavaScript
245 lines
6.7 KiB
JavaScript
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;
|