// Phase 3: Deploy DNS test apps and verify Internal DNS resolution // Strategy: Deploy two apps with same tag, verify DNS discovery const CUMIN_TOKEN = "cumin_GjynCIFJtyoZ_73wasCoWNYf7Y-Pk0jMffHEdRzblBg"; const PROJECT_ID = "178bfad9-5edc-409f-833c-6fffca7aed5a"; const API = "https://api.cumin.dev"; const fs = require("fs"); let SESSION_ID = null; const log = []; function L(msg) { const ts = new Date().toISOString(); const e = `[${ts}] ${msg}`; console.log(e); log.push(e); } async function mcpRequest(method, params, id) { const body = { jsonrpc: "2.0", method, id }; if (params) body.params = params; const headers = { Authorization: `Bearer ${CUMIN_TOKEN}`, "Content-Type": "application/json", Accept: "application/json, text/event-stream" }; if (SESSION_ID) headers["Mcp-Session-Id"] = SESSION_ID; const res = await fetch(`${API}/mcp`, { method: "POST", headers, body: JSON.stringify(body) }); const sid = res.headers.get("Mcp-Session-Id"); if (sid) SESSION_ID = sid; const data = await res.json(); if (data.error) throw new Error(`MCP: ${JSON.stringify(data.error)}`); return data.result; } async function callTool(name, args) { const r = await mcpRequest("tools/call", { name, arguments: args }, Date.now()); const text = r.content?.map(c => c.text || "").join("") || JSON.stringify(r); if (r.isError) { L(` āš ļø ${name}: ${text}`); return null; } return text; } // TCP echo server code - listens on both HTTP (4000) and raw TCP (9000) const TCP_SERVER_CODE = ` const http = require("http"); const net = require("net"); const os = require("os"); const PORT_HTTP = process.env.PORT || 4000; const PORT_TCP = 9000; // HTTP server for health checks and info http.createServer((req, res) => { const ifaces = os.networkInterfaces(); const ips = {}; for (const [name, addrs] of Object.entries(ifaces)) { ips[name] = addrs.filter(a => a.family === 'IPv4').map(a => a.address); } res.writeHead(200, {"Content-Type":"application/json"}); res.end(JSON.stringify({ service: "dns-test-server", hostname: os.hostname(), interfaces: ips, uptime: process.uptime(), env: { PORT: PORT_HTTP, NODE_VERSION: process.version } })); }).listen(PORT_HTTP, () => console.log("HTTP on " + PORT_HTTP)); // Raw TCP echo server const tcpServer = net.createServer(socket => { console.log("TCP connection from " + socket.remoteAddress + ":" + socket.remotePort); socket.write("HELLO_FROM_TCP_SERVER:" + os.hostname() + "\\n"); socket.on("data", data => { console.log("TCP data: " + data.toString().trim()); socket.write("ECHO:" + data.toString()); }); socket.on("error", () => {}); }); tcpServer.listen(PORT_TCP, () => console.log("TCP echo on " + PORT_TCP)); `; // DNS client code - probes DNS and connects to server via DNS names const DNS_CLIENT_CODE = ` const http = require("http"); const net = require("net"); const dns = require("dns"); const { execSync } = require("child_process"); const os = require("os"); const PORT = process.env.PORT || 4000; function getIPs() { const ifaces = os.networkInterfaces(); const ips = {}; for (const [name, addrs] of Object.entries(ifaces)) { ips[name] = addrs.filter(a => a.family === 'IPv4').map(a => a.address); } return ips; } // Try DNS resolution for various patterns async function dnsProbe() { const patterns = [ "dns-test-server", "dns-test-server.mesh", "dns-test-server.local", "dns-test-server.soc-private-net", "dns-test-server.project.mesh", "soc-backend", "soc-backend.mesh", "soc-gateway", "soc-gateway.mesh", ]; const results = {}; for (const name of patterns) { try { const addrs = await new Promise((resolve, reject) => { dns.resolve4(name, (err, addresses) => { if (err) reject(err); else resolve(addresses); }); }); results[name] = { status: "RESOLVED", addresses: addrs }; } catch (e) { results[name] = { status: "FAILED", error: e.code || e.message }; } } // Also check resolv.conf let resolvConf = ""; try { resolvConf = require("fs").readFileSync("/etc/resolv.conf", "utf8"); } catch {} // Try getent const getentResults = {}; for (const name of patterns.slice(0, 4)) { try { const result = execSync("getent hosts " + name, { timeout: 3000 }).toString().trim(); getentResults[name] = result; } catch (e) { getentResults[name] = "NOT_FOUND"; } } return { dnsResolve: results, resolvConf, getent: getentResults, myIPs: getIPs() }; } // Try TCP connection to server async function tcpProbe(host, port) { return new Promise((resolve, reject) => { const socket = new net.Socket(); socket.setTimeout(5000); socket.connect(port, host, () => { let data = ""; socket.on("data", chunk => { data += chunk.toString(); }); socket.write("PING_FROM_CLIENT\\n"); setTimeout(() => { socket.destroy(); resolve({ status: "CONNECTED", response: data.trim() }); }, 1000); }); socket.on("error", err => { socket.destroy(); resolve({ status: "FAILED", error: err.message }); }); socket.on("timeout", () => { socket.destroy(); resolve({ status: "TIMEOUT" }); }); }); } http.createServer(async (req, res) => { if (req.url === "/health") { res.writeHead(200, {"Content-Type":"application/json"}); return res.end(JSON.stringify({ status: "healthy", service: "dns-test-client" })); } if (req.url === "/probe") { const dnsResults = await dnsProbe(); // If DNS resolved, try TCP and HTTP connections const connectResults = {}; for (const [name, result] of Object.entries(dnsResults.dnsResolve)) { if (result.status === "RESOLVED") { const ip = result.addresses[0]; // TCP test connectResults[name + ":tcp:9000"] = await tcpProbe(ip, 9000); // HTTP test try { const r = await fetch("http://" + ip + ":4000/", { signal: AbortSignal.timeout(3000) }); const d = await r.json(); connectResults[name + ":http:4000"] = { status: "OK", data: d }; } catch (e) { connectResults[name + ":http:4000"] = { status: "FAILED", error: e.message }; } } } // Also try direct WireGuard IPs we know connectResults["direct:10.100.0.94:4000"] = {}; try { const r = await fetch("http://10.100.0.94:4000/health", { signal: AbortSignal.timeout(3000) }); const d = await r.json(); connectResults["direct:10.100.0.94:4000"] = { status: "OK", data: d }; } catch (e) { connectResults["direct:10.100.0.94:4000"] = { status: "FAILED", error: e.message }; } res.writeHead(200, {"Content-Type":"application/json"}); return res.end(JSON.stringify({ dns: dnsResults, connections: connectResults }, null, 2)); } res.writeHead(200, {"Content-Type":"application/json"}); res.end(JSON.stringify({ service: "dns-test-client", myIPs: getIPs() })); }).listen(PORT, () => console.log("DNS test client on " + PORT)); `; async function main() { L("=== PHASE 3: DNS & TCP FUNCTIONALITY TESTING ===\n"); // Initialize MCP await mcpRequest("initialize", { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "dns-tester", version: "1.0" } }, 1); L("āœ… MCP Connected\n"); // Step 1: Deploy DNS test server L("šŸ“¦ Deploying dns-test-server (TCP + HTTP)..."); const serverResult = await callTool("create_app", { project_id: PROJECT_ID, name: "dns-test-server", image: "node:22-alpine", cpu: 150, memory: 250, instances: 1, hibernated: false, ports: [{ name: "http", number: 4000, health: { path: "/" } }], env: [ { name: "PORT", value: "4000" }, { name: "APP_CODE_B64", value: Buffer.from(TCP_SERVER_CODE).toString("base64") } ], mounts: [], args: ["sh", "-c", "echo $APP_CODE_B64 | base64 -d > /app.js && node /app.js"], tags: { role: "dns-test", tier: "backend", platform: "soc" } }); L(` Server deploy result: ${serverResult ? serverResult.substring(0, 200) : "null"}`); // Step 2: Deploy DNS test client L("\nšŸ“¦ Deploying dns-test-client..."); const clientResult = await callTool("create_app", { project_id: PROJECT_ID, name: "dns-test-client", image: "node:22-alpine", cpu: 150, memory: 250, instances: 1, hibernated: false, ports: [{ name: "http", number: 4000, health: { path: "/health" } }], env: [ { name: "PORT", value: "4000" }, { name: "APP_CODE_B64", value: Buffer.from(DNS_CLIENT_CODE).toString("base64") } ], mounts: [], args: ["sh", "-c", "echo $APP_CODE_B64 | base64 -d > /app.js && node /app.js"], tags: { role: "dns-test", tier: "frontend", platform: "soc" } }); L(` Client deploy result: ${clientResult ? clientResult.substring(0, 200) : "null"}`); // Step 3: Wait for apps to start L("\nā³ Waiting 25 seconds for apps to boot...\n"); await new Promise(r => setTimeout(r, 25000)); // Step 4: Check app status L("=== Checking deployed app status ==="); const apps = await callTool("list_apps", { project_id: PROJECT_ID }); const appsList = JSON.parse(apps); for (const app of appsList) { L(` ${app.name}: ${app.status} (tags: ${JSON.stringify(app.tags)})`); } // Step 5: Get server IP info const server = appsList.find(a => a.name === "dns-test-server"); const client = appsList.find(a => a.name === "dns-test-client"); if (server) { L("\n=== SERVER NETWORK INFO ==="); const ipAddr = await callTool("exec_in_app", { project_id: PROJECT_ID, id: server.id, command: ["ip", "-4", "addr"] }); L(` ip addr: ${ipAddr}`); const resolv = await callTool("exec_in_app", { project_id: PROJECT_ID, id: server.id, command: ["cat", "/etc/resolv.conf"] }); L(` resolv.conf: ${resolv}`); } if (client) { L("\n=== CLIENT NETWORK INFO ==="); const ipAddr = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["ip", "-4", "addr"] }); L(` ip addr: ${ipAddr}`); const resolv = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["cat", "/etc/resolv.conf"] }); L(` resolv.conf: ${resolv}`); } // Step 6: DNS resolution tests from client if (client) { L("\n=== DNS RESOLUTION TESTS (from client container) ==="); const dnsNames = [ "dns-test-server", "dns-test-server.mesh", "soc-backend", "soc-backend.mesh", "soc-gateway", "soc-gateway.mesh", "dns-test-client", "dns-test-client.mesh" ]; for (const name of dnsNames) { const result = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["getent", "hosts", name] }); L(` getent hosts ${name} → ${JSON.stringify(JSON.parse(result))}`); } // Also try nslookup L("\n--- nslookup tests ---"); for (const name of ["dns-test-server", "soc-backend"]) { const result = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["nslookup", name] }); L(` nslookup ${name} → ${JSON.stringify(JSON.parse(result))}`); } } // Step 7: TCP connection test from client to server if (client && server) { L("\n=== TCP CONNECTION TESTS ==="); // Get server WireGuard IP const serverIpResult = await callTool("exec_in_app", { project_id: PROJECT_ID, id: server.id, command: ["ip", "-4", "addr", "show", "wg0"] }); const serverIp = JSON.parse(serverIpResult).stdout.match(/inet\s+(10\.100\.\d+\.\d+)/)?.[1]; L(` Server WireGuard IP: ${serverIp}`); // TCP test via direct IP if (serverIp) { L(`\n Testing TCP connection to ${serverIp}:9000...`); const tcpTest = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["sh", "-c", `echo "HELLO_TCP" | nc -w3 ${serverIp} 9000`] }); L(` TCP via IP → ${JSON.stringify(JSON.parse(tcpTest))}`); // HTTP test via direct IP L(` Testing HTTP connection to ${serverIp}:4000...`); const httpTest = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["wget", "-qO-", `http://${serverIp}:4000/`] }); L(` HTTP via IP → ${JSON.stringify(JSON.parse(httpTest))}`); } // TCP test via DNS name (if DNS resolves) L(`\n Testing TCP connection via DNS name dns-test-server:9000...`); const tcpDns = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["sh", "-c", `echo "HELLO_DNS_TCP" | nc -w3 dns-test-server 9000 2>&1 || echo "DNS_TCP_FAILED"`] }); L(` TCP via DNS → ${JSON.stringify(JSON.parse(tcpDns))}`); L(`\n Testing HTTP connection via DNS name dns-test-server:4000...`); const httpDns = await callTool("exec_in_app", { project_id: PROJECT_ID, id: client.id, command: ["sh", "-c", `wget -qO- http://dns-test-server:4000/ 2>&1 || echo "DNS_HTTP_FAILED"`] }); L(` HTTP via DNS → ${JSON.stringify(JSON.parse(httpDns))}`); } L("\n=== PHASE 3 COMPLETE ==="); // Save log fs.writeFileSync("d:/cumin/ghaymah_dns_net_sec_tests/logs/03_dns_tcp_test.log", log.join("\n")); L("\nLog written to logs/03_dns_tcp_test.log"); } main().catch(e => { L(`FATAL: ${e.message}`); process.exit(1); });