const https = require('https');
const httpProxy = require('http-proxy');
const url = require('url');
const path = require('path');
const fs = require('fs');
const os = require('os');
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const chalk = require('chalk');
const { EventEmitter } = require('events');
const axios = require('axios');
const geoip = require('geoip-lite');  // Importing geoip-lite for GeoIP blocking
const exec = require('child_process').exec; // For executing shell commands
const { spawn } = require('child_process');
const dgram = require('dgram'); // Importing dgram for UDP handling
const basicAuth = require('express-basic-auth');
const compression = require('compression');
const osu = require('os-utils');
const { netstat } = require('node-os-utils');  // Import netstat from node-os-utils
const mem = require('node-os-utils').mem;
// Create an Agent instance configured for keep-alive
const keepAliveAgent = new https.Agent({
    keepAlive: true,
    maxSockets: 100, // Control the number of sockets to handle concurrent connections
    maxFreeSockets: 10, // Control the number of free sockets to keep open
  });

  axios.defaults.httpsAgent = keepAliveAgent;

let logMessages = [];

const originalLog = console.log;
console.log = (...args) => {
  originalLog(...args);
  logMessages.push(args.join(' '));
};

const synFloodTracker = {};
const MAX_SYN_REQUESTS = 10; // Max allowed per minute
const SYN_BLOCK_TIME_MS = 60000; // Block time if SYN flood detected

// Rate Limiting Configurations
const rateLimit = {};
const MAX_REQUESTS_PER_MINUTE = 100; // Set your limit here (100)
const BLOCK_TIME_MS = 60000; // Time to block the IP (1 minute) (60000)

// DNS attack prevention configurations
const dnsWhitelist = ['8.8.8.8', '1.1.1.1']; // Trusted DNS resolvers (Google DNS and Cloudflare DNS)
const MAX_DNS_REQUESTS_PER_MINUTE = 50; // Limit DNS-related requests
const dnsRateLimit = {};

// UDP Rate Limiting Configurations
const udpRateLimit = {};
const MAX_UDP_REQUESTS_PER_MINUTE = 100; // Adjust as needed
const UDP_BLOCK_TIME_MS = 60000; // 1 minute

// Network Usage Limiting Configurations
const networkUsage = {};
const MAX_BYTES_PER_MINUTE = 1024 * 1024; // 1 MB per minute per IP (adjust as needed)
const NETWORK_BLOCK_TIME_MS = 60000; // 1 minute

// Cloud Server IP Ranges
const cloudServerIpRanges = ['35.', '52.', '169.', '198.', '199.', '200.', '216.', '47.'];

// Allowed Countries for GeoIP
const allowedCountries = ['ID', 'SG', 'PH']; // Add allowed countries by their ISO code

const configApp = express();
const CONFIG_PORT = 6969;

// Middleware
configApp.use(compression());
configApp.set('view engine', 'ejs');
configApp.use(bodyParser.urlencoded({ extended: true }));
configApp.use(express.static('public'));



// Function to check and update DNS rate limits
const isSuspiciousDNSRequest = (ip, requestUrl) => {
  if (!requestUrl.toLowerCase().includes('dns')) return false; // Only check DNS-related requests
  if (!dnsWhitelist.includes(ip)) {
    const now = Date.now();
    if (!dnsRateLimit[ip]) {
      dnsRateLimit[ip] = { count: 1, timestamp: now };
    } else {
      const timePassed = now - dnsRateLimit[ip].timestamp;
      if (timePassed > BLOCK_TIME_MS) {
        dnsRateLimit[ip] = { count: 1, timestamp: now };
      } else {
        dnsRateLimit[ip].count++;
      }
      
      if (dnsRateLimit[ip].count > MAX_DNS_REQUESTS_PER_MINUTE) {
        return true; // DNS request limit exceeded
      }
    }
  }
  return false;
};

// Function to check and update UDP rate limits
const isUdpRateLimited = (ip) => {
  const now = Date.now();
  if (!udpRateLimit[ip]) {
    udpRateLimit[ip] = { count: 1, timestamp: now };
    return false;
  } else {
    const timePassed = now - udpRateLimit[ip].timestamp;
    if (timePassed > UDP_BLOCK_TIME_MS) {
      udpRateLimit[ip] = { count: 1, timestamp: now };
      return false;
    } else {
      udpRateLimit[ip].count++;
      if (udpRateLimit[ip].count > MAX_UDP_REQUESTS_PER_MINUTE) {
        return true;
      }
    }
  }
  return false;
};

// Function to check and update network usage
const isNetworkUsageLimited = (ip, bytes) => {
  const now = Date.now();
  if (!networkUsage[ip]) {
    networkUsage[ip] = { sent: bytes, received: bytes, timestamp: now };
    return false;
  } else {
    const timePassed = now - networkUsage[ip].timestamp;
    if (timePassed > NETWORK_BLOCK_TIME_MS) {
      networkUsage[ip] = { sent: bytes, received: bytes, timestamp: now };
      return false;
    } else {
      networkUsage[ip].sent += bytes;
      networkUsage[ip].received += bytes;
      if (networkUsage[ip].sent > MAX_BYTES_PER_MINUTE || networkUsage[ip].received > MAX_BYTES_PER_MINUTE) {
        return true;
      }
    }
  }
  return false;
};

// Function to log blocked IPs
const logBlockedIP = (ip, protocol = 'TCP') => {
  fs.appendFile('blocked_ips.log', `${new Date().toISOString()} - Blocked IP: ${ip} [${protocol}]\n`, (err) => {
    if (err) console.error(chalk.red('Error logging blocked IP:'), err);
  });
};

// Function to normalize IP addresses
const normalizeIp = (ip) => {
  if (ip.startsWith("::ffff:")) {
    return ip.substring(7);
  }
  if (ip === "::1") {
    return "127.0.0.1";
  }
  return ip;
};

// Function to check if IP is from cloud/server ranges
const isCloudOrServerIp = (ip) => {
  return cloudServerIpRanges.some((range) => ip.startsWith(range));
};

// Helper function to check GeoIP blocking
const isBlockedGeoIp = (ip) => {
  const geo = geoip.lookup(ip);
  if (geo && !allowedCountries.includes(geo.country)) {
    return true;
  }
  return false;
};

const configPath = path.resolve(__dirname, './config/main.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));

// Fungsi untuk membaca konfigurasi
const readConfig = () => {
  try {
    const data = fs.readFileSync(configPath, 'utf8');
    return JSON.parse(data);
  } catch (err) {
    console.error(chalk.red('Error membaca main.json:'), err);
    return {};
  }
};

// Fungsi untuk menulis konfigurasi
const writeConfig = (config) => {
  try {
    fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
    console.log(chalk.green('Configuration updated.'));
    console.clear();
  } catch (err) {
    console.error(chalk.red('Error writing to Config:'), err);
  }
};

// Middleware Autentikasi
configApp.use('/config', basicAuth({
  users: { [config.username]: config.password }, // Use credentials from config
  challenge: true,
  realm: 'Config Area'
}));

// Route untuk menampilkan form konfigurasi
configApp.get('/config', (req, res) => {
  const config = readConfig();
  res.render('config', { config });
});

// Route untuk memproses perubahan konfigurasi
configApp.post('/config', (req, res) => {
  const { ip, port, loginurl, cdn, username, password } = req.body;
  const newConfig = { ip, port: parseInt(port), loginurl, cdn, username, password };
  
  writeConfig(newConfig);
  
  res.redirect('/config');
});


configApp.get('/logs', (req, res) => {
  res.json({
    logs: logMessages
  });
});

// Endpoint to gather system stats
configApp.get('/stats', async (req, res) => {
    try {
      // Use os-utils or os for retrieving CPU and memory information
      const freeMemory = os.freemem();
      const totalMemory = os.totalmem();
      const usedMemory = totalMemory - freeMemory;
      const memoryUsagePercent = ((usedMemory) / totalMemory) * 100;

  
      osu.cpuUsage(async (cpuPercentage) => {
        
        const network = await netstat.inOut();
       
  
        res.json({
          memoryUsage: {
            free: freeMemory,
            total: totalMemory,
            usedPercent: memoryUsagePercent.toFixed(2),
          },
          cpuUsage: (cpuPercentage * 100).toFixed(2),
          network: {
            inputMb: network.inputMb,
            outputMb: network.outputMb
          }
        });
      });
    } catch (error) {
      console.error('Error fetching system statistics:', error);
      res.json({ error: 'Unable to fetch system statistics' });
    }
  });
configApp.post('/restart', (req, res) => {
  console.log('Restarting server...');
  res.send('Server restarting...');
  
 
  process.exit(1);
});


const isSynFlood = (ip) => {
  const now = Date.now();
  if (!synFloodTracker[ip]) {
    synFloodTracker[ip] = { count: 1, timestamp: now };
    return false;
  }

  const timePassed = now - synFloodTracker[ip].timestamp;
  if (timePassed > SYN_BLOCK_TIME_MS) {
    synFloodTracker[ip] = { count: 1, timestamp: now };
    return false;
  } else {
    synFloodTracker[ip].count++;
    if (synFloodTracker[ip].count > MAX_SYN_REQUESTS) {
      console.log(chalk.red(`SYN flood detected from IP: ${ip}`));
      logBlockedIP(ip, 'SYN Flood');
      return true;
    }
  }

  return false;
};

// Start UI Server
configApp.listen(CONFIG_PORT, () => {
  console.log(chalk.blue(`Admin dashboard is running on http://localhost:${CONFIG_PORT}/config`));
});


// Start Server Function
const startServer = async () => {
  const proxy = httpProxy.createProxyServer({});
  const pk = fs.readFileSync(path.join(__dirname, 'server.key'));
  const pc = fs.readFileSync(path.join(__dirname, 'server.crt'));
  const optss = { key: pk, cert: pc };
  const trustedIPs = ['127.0.0.1'];
  const port = process.argv[2] || 443;
  

  EventEmitter.defaultMaxListeners = 100;

  const packet = `server|${config.ip}\nport|${config.port}\ntype|1\n#maint|Server is currently initializing or re-syncing with sub servers. Please try again in a minute.\n\n\nloginurl|${config.loginurl}\nbeta_server|127.0.0.1\nbeta_port|17091\nbeta_type|1\nbeta2_server|127.0.0.1\nbeta2_port|17099\nbeta2_type|1\nmeta|${Math.floor(Date.now() / 1000)}\nRTENDMARKERBS1001`;

  const server = https.createServer(optss, function (req, res) {
    let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
    const normalizedIp = normalizeIp(ip);
    const hostHeader = req.headers.host;

// Set keepAliveTimeout, which sets how long a connection should remain open in milliseconds.
server.keepAliveTimeout = 60 * 1000; // Keep alive for 60 seconds

// Set the headers timeout slightly longer than keepAliveTimeout to accommodate requests that arrive just before the keep alive timeout
server.headersTimeout = 65 * 1000; // 65 seconds

    console.log(chalk.green(`[DEVELOPER MODE]`, req.headers['user-agent']));
    console.log(chalk.green(`Request received from IP: ${normalizedIp}`));

    if (isSynFlood(ip)) {
        console.log(chalk.red(`SYN flood detected from IP: ${normalizedIp}`));
        logBlockedIP(normalizedIp, 'SYN Flood');
        
        // Use the underlying socket to destroy the connection
        req.socket.end(); // Gracefully end the connection
        req.socket.destroy(); // Forcefully close the connection, ignoring any pending data or errors
        return;
    }
    // Rate Limiting Logic
    if (!rateLimit[normalizedIp]) {
      rateLimit[normalizedIp] = { count: 1, timestamp: Date.now() };
    } else {
      const currentTime = Date.now();
      const timePassed = currentTime - rateLimit[normalizedIp].timestamp;
  
      // Reset count if more than 1 minute has passed
      if (timePassed > BLOCK_TIME_MS) {
        rateLimit[normalizedIp] = { count: 1, timestamp: currentTime };
      } else {
        rateLimit[normalizedIp].count++;
      }
  
      // Check if the limit is reached
      if (rateLimit[normalizedIp].count > MAX_REQUESTS_PER_MINUTE) {
        console.log(chalk.red(`Rate limit exceeded for IP: ${normalizedIp}`));
        logBlockedIP(normalizedIp, 'TCP');
        res.statusCode = 429; // Too Many Requests
        res.end('Too many requests. Your connection is being throttled.');
        return;
      }
    }
  
    // DNS attack protection
    if (isSuspiciousDNSRequest(normalizedIp, req.url)) {
      console.log(chalk.red(`DNS DDoS detected from IP: ${normalizedIp}`));
      logBlockedIP(normalizedIp, 'TCP-DNS');
      res.statusCode = 403; // Forbidden
      res.end('Forbidden: DNS DDoS attack detected.');
      return;
    }

    // Network Usage Limiting for TCP
    const requestBytes = Buffer.byteLength(req.url); // Approximate
    if (isNetworkUsageLimited(normalizedIp, requestBytes)) {
      console.log(chalk.red(`Network usage limit exceeded for IP: ${normalizedIp} (TCP)`));
      logBlockedIP(normalizedIp, 'TCP-NETWORK');
      res.statusCode = 429; // Too Many Requests
      res.end('Too many requests. Your connection is being throttled.');
      return;
    }
  
    // Check for cloud or server IP range
    if (!trustedIPs.includes(normalizedIp) && isCloudOrServerIp(normalizedIp)) {
      console.log(chalk.red(`Blocking cloud/server IP: ${normalizedIp}`));
      logBlockedIP(normalizedIp, 'TCP-Cloud');
      res.statusCode = 403;
      res.end('Forbidden: Access from cloud/server IP is blocked.');
      return;
    }
  
    // Check for GeoIP block
    if (isBlockedGeoIp(normalizedIp)) {
      console.log(chalk.red(`Blocking access from IP: ${normalizedIp} (GeoIP Block)`));
      logBlockedIP(normalizedIp, 'TCP-GeoIP');
      res.statusCode = 403;
      res.end('Forbidden: Access from your location is blocked.');
      return;
    }
  
    const parsedUrl = url.parse(req.url);
    const pathname = `.${parsedUrl.pathname}`;
    const ext = path.parse(pathname).ext;
  
    console.log(chalk.blue(`Connection Request from: ${normalizedIp} URL: ${req.url} Method: ${req.method}`));
  
    const map = {
      '.ico': 'image/x-icon',
      '.html': 'text/html',
      '.js': 'text/javascript',
      '.json': 'application/json',
      '.css': 'text/css',
      '.png': 'image/png',
      '.jpg': 'image/jpeg',
      '.wav': 'audio/wav',
      '.mp3': 'audio/mpeg',
      '.svg': 'image/svg+xml',
      '.pdf': 'application/pdf',
      '.doc': 'application/msword'
    };
  
    const allowedUserAgents = [
      'UbiServices_SDK_2022.Release.9_PC64_ansi_static',
      'UbiServices_SDK_2022.Release.9_PC32_ansi_static',
      'UbiServices_SDK_2022.Release.9_ANDROID64_static',
      'UbiServices_SDK_2022.Release.9_ANDROID32_static',
      'UbiServices_SDK_2022.Release.9_IOS64',
      'UbiServices_SDK_2022.Release.9_IOS32',
      'UbiServices_SDK_2022.Release.9_MAC64',
      'UbiServices_SDK_2022.Release.9_MAC32',
    ];
  
    const userAgent = req.headers['user-agent'];
  
    // Handle /growtopia/server_data.php - Only POST and allowed User-Agents
// Handle /growtopia/server_data.php - Only POST and allowed User-Agents
if (req.url === "/growtopia/server_data.php") {
    if (req.method !== 'POST' || hostHeader !== 'www.growtopia2.com') {
      console.log(chalk.red(`Mata mata :  ${req.url}`));
      res.statusCode = 403;
      res.end(`
server|${normalizedIp}
port|${Math.floor(Date.now() / 69)}
type|1
#maint|Server is currently initializing or re-syncing with sub servers. Please try again in a minute.
  
loginurl|http-king.com
beta_server|${normalizedIp}
beta_port|${Math.floor(Date.now() / 69)}
beta_type|1
beta2_server|${normalizedIp}
beta2_port|${Math.floor(Date.now() / 69)}
beta2_type|1
meta|69-SCP-PROTECTION
RTENDMARKERBS1001
      `);
      return;
    }
  
    res.write(packet, function (err) {
      if (err) console.log(chalk.red(err));
    });
    res.end();
  }
    // Handle /cache and /0098 - Only GET and allowed User-Agents
    else if (req.url.indexOf("/cache") !== -1 || req.url.indexOf("/0098") !== -1) {
      if (req.method !== 'GET' || !allowedUserAgents.includes(userAgent)) {
        console.log(chalk.red(`Blocked: Invalid method or User-Agent for ${req.url}`));
        res.statusCode = 403;
        res.end('Hello world');
        return;
      }
  
      console.log(chalk.blue(`[CACHE-SPEED] Connection from: ${normalizedIp}\n Downloading assets: ${req.url}`));
  
      fs.exists(pathname, function (exist) {
        if (!exist) {
          res.statusCode = 301;
          res.writeHead(301, {
            Location: `https://ubistatic-a.akamaihd.net/${config.cdn}${req.url}`
          }).end();
          return;
        }
  
        fs.readFile(pathname, function (err, data) {
          if (err) {
            res.statusCode = 404;
            res.end(`error`);
          } else {
            res.setHeader('Content-type', map[ext] || 'text/plain');
            res.end(data);
          }
        });
      });
    }
    // Block all other requests
    else {
      console.log(chalk.red(`🛡 [PROTECTION] Blocked request: ${req.method} ${req.url} from IP: ${normalizedIp}`));
      logBlockedIP(normalizedIp, 'TCP-Other');
      res.statusCode = 403;
      res.end('Hello world');
    }
  });

  server.listen(port, () => {
    console.log(chalk.green(`Server is running on port ${port}`));
  });

  // -------------------- UDP Server Setup --------------------
  
  const udpServer = dgram.createSocket('udp4');
  const UDP_PORT = 53; // DNS typically uses port 53

  udpServer.on('error', (err) => {
    console.error(`UDP server error:\n${err.stack}`);
    udpServer.close();
  });

  udpServer.on('message', (msg, rinfo) => {
    const ip = rinfo.address;
    const normalizedIp = normalizeIp(ip);
    console.log(chalk.yellow(`UDP request from IP: ${normalizedIp}, Port: ${rinfo.port}`));

    // Rate Limiting
    if (isUdpRateLimited(normalizedIp)) {
      console.log(chalk.red(`UDP Rate limit exceeded for IP: ${normalizedIp}`));
      logBlockedIP(normalizedIp, 'UDP');
      return; // Drop the packet silently or send a response indicating rate limiting
    }

    // GeoIP Blocking
    if (isBlockedGeoIp(normalizedIp)) {
      console.log(chalk.red(`Blocking UDP access from IP: ${normalizedIp} (GeoIP Block)`));
      logBlockedIP(normalizedIp, 'UDP-GeoIP');
      return; // Drop the packet silently or send a forbidden response
    }

    // Network Usage Limiting for UDP
    const messageBytes = msg.length;
    if (isNetworkUsageLimited(normalizedIp, messageBytes)) {
      console.log(chalk.red(`Network usage limit exceeded for IP: ${normalizedIp} (UDP)`));
      logBlockedIP(normalizedIp, 'UDP-NETWORK');
      return; // Drop the packet
    }

    // Implement your DNS handling logic here
    // For example, respond with a simple DNS response or forward the request to a legitimate DNS server

    // Example: Forward the DNS request to an external DNS server (e.g., Google DNS)
    const externalDns = '8.8.8.8';
    const externalDnsPort = 53;
    const externalClient = dgram.createSocket('udp4');

    externalClient.send(msg, 0, msg.length, externalDnsPort, externalDns, (error) => {
      if (error) {
        console.error(`Error forwarding UDP request: ${error}`);
        externalClient.close();
      }
    });

    externalClient.on('message', (response) => {
      udpServer.send(response, 0, response.length, rinfo.port, rinfo.address, (err) => {
        if (err) {
          console.error(`Error sending UDP response: ${err}`);
        }
        externalClient.close();
      });
    });

    // Optional: Implement timeouts to prevent hanging
    externalClient.on('error', (err) => {
      console.error(`External DNS client error: ${err}`);
      externalClient.close();
    });

    // Set a timeout for the external DNS response
    externalClient.setTimeout(5000, () => { // 5 seconds timeout
      console.error(`External DNS request timed out for IP: ${normalizedIp}`);
      externalClient.close();
    });
  });

  udpServer.on('listening', () => {
    const address = udpServer.address();
    console.log(chalk.green(`UDP Server listening on ${address.address}:${address.port}`));
  });

  udpServer.bind(UDP_PORT);
  const shutdown = () => {
    console.log('Shutting down the server...');
  
    // Stop the server from accepting new connections
    server.close(() => {
      console.log('HTTP server closed.');
  
      // Close the UDP server
      udpServer.close(() => {
        console.log('UDP server closed.');
  
        // Exit the process
        process.exit(0);
      });
    });
  
    // Ensure shutdown if connections remain
    setTimeout(() => {
      console.error('Forced shutdown due to open connections.');
      process.exit(1); // exit with error code if connections remain
    }, 5000).unref();
  };
  
  configApp.post('/shutdown', (req, res) => {
    res.send('Server is shutting down...');
    shutdown();
  });
  
  // Capture abort signals
  process.on('SIGINT', shutdown);
  process.on('SIGTERM', shutdown);
  
  console.log('')
  console.log(chalk.magenta('========================================================'));
  console.log(chalk.magenta(`- CONFIGURATION LOADS : `));
  console.log(chalk.magenta(`[@] Login URL : ${config.loginurl}`));
  console.log(chalk.magenta(`[@] HTTP Port ${port}`));
  console.log(chalk.magenta(`[@] ANTI-HOSTCHECK ${port}`));
  console.log(chalk.magenta(`[@] ANTI-SCAN SERVER DATA PHP STATUS : ENABLED [PENTEST-SECURITY]`));
  console.log(chalk.magenta(`[@] OPERATION SYSTEM : WINDOWS`));
  console.log(chalk.magenta(`[@] HTTP KING VERSION : 3.3.8`));
  console.log(chalk.greenBright(`[@] SUBSCRIPTION : VIP-SUPER`));
  console.log(chalk.greenBright(`[@] LAST HTTP UPDATE : 6/10/2024`));
  console.log(chalk.magenta('========================================================'));
  console.log('')
  console.log(chalk.green(`
  
  
      :::    ::: ::::::::::: ::::    :::  ::::::::       :::     :::     ::::::::       :::::::: 
     :+:   :+:      :+:     :+:+:   :+: :+:    :+:      :+:     :+:    :+:    :+:     :+:    :+: 
    +:+  +:+       +:+     :+:+:+  +:+ +:+             +:+     +:+           +:+            +:+  
   +#++:++        +#+     +#+ +:+ +#+ :#:             +#+     +:+        +#++:          +#++:    
  +#+  +#+       +#+     +#+  +#+#+# +#+   +#+#       +#+   +#+            +#+            +#+    
 #+#   #+#      #+#     #+#   #+#+# #+#    #+#         #+#+#          #+#    #+#     #+#    #+#  
###    ### ########### ###    ####  ########            ###          ########      ########

    `));
}

startServer();


untuk post /growtopia/server_data.php adalah jalur menuju login ke game private server nya, www.growtopia2.com adalah hostnya jika saya menggunakan www.growtopia2.com tanpa http debugger pro saya tidak bisa login, namun sambil membuka http debugger pro saya bisa login. untuk host lain nya adalah www.growtopia1.com tapi saya tidak mau menggunakan nya, bisakah kamu bantu saya