&1', $output, $code); $text = implode("\n", $output); if ($code === 124) { $text .= ($text === '' ? '' : "\n") . '--- aborted: exceeded the ' . CMD_TIMEOUT . 's time limit ---'; } elseif ($code === 127) { $text = 'The "' . $binary . '" command is not installed on this server.'; } // Display the command without the timeout wrapper — that is plumbing, not diagnostics. $display = $binary; foreach ($args as $arg) { $display .= ' ' . (string) $arg; } return ['command' => $display, 'output' => $text, 'code' => $code]; } function performPing(string $target): array { return runCommand('ping', ['-c', (string) PING_COUNT, '-W', (string) PING_WAIT, $target]); } function performTraceroute(string $target): array { return runCommand('traceroute', [ '-m', (string) TRACE_MAX_HOPS, '-q', (string) TRACE_QUERIES, '-w', (string) TRACE_WAIT, $target, ]); } function performDns(string $target, string $type, string $resolver): array { $args = []; if ($resolver !== '') { $args[] = '@' . $resolver; } if ($type === 'PTR' && filter_var($target, FILTER_VALIDATE_IP)) { $args[] = '-x'; $args[] = $target; } else { $args[] = $target; $args[] = $type; } $args[] = '+timeout=3'; $args[] = '+tries=2'; return runCommand('dig', $args); } /** * Pulls the responding IP out of each traceroute hop line so the hops can be * enriched the same way spf-check.php and mail-delivery-check.php enrich theirs. * * @return array */ function parseTracerouteHops(string $output): array { $hops = []; foreach (explode("\n", $output) as $line) { if (!preg_match('/^\s*(\d+)\s+(.*)$/', $line, $m)) { continue; } $rest = trim($m[2]); $first = explode(' ', $rest)[0]; // A hop reads either "host (ip) 1.2 ms" or "ip 1.2 ms", or "* * *" when it stays silent. if (preg_match('/\(([0-9a-fA-F:.]+)\)/', $rest, $paren)) { $ip = $paren[1]; } else { $ip = $first; } if (!filter_var($ip, FILTER_VALIDATE_IP)) { continue; } $hops[] = ['hop' => $m[1], 'host' => $first === $ip ? '' : $first, 'ip' => $ip]; } return $hops; } /** * Enriches IPs with ASN / country / company via ip-api.com's free batch endpoint. * @param string[] $ips * @return array */ function lookupIpInfo(array $ips): array { $out = []; $ips = array_values(array_unique(array_filter($ips))); if (empty($ips)) { return $out; } $fields = 'query,status,message,country,countryCode,as,asname,isp,org,city,regionName,reverse'; foreach (array_chunk($ips, 100) as $chunk) { $payload = array_map(static fn($ip) => ['query' => $ip, 'fields' => $fields], $chunk); $ch = curl_init('http://ip-api.com/batch'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 20, ]); $resp = curl_exec($ch); curl_close($ch); foreach ((json_decode((string) $resp, true) ?: []) as $item) { if (isset($item['query'])) { $out[$item['query']] = $item; } } } return $out; } /** True for addresses that are not routable on the public internet. */ function isReservedIp(string $ip): bool { return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) === false; } /** @return string[] */ function resolveHost(string $host): array { $ips = []; foreach ((@dns_get_record($host, DNS_A) ?: []) as $r) { if (!empty($r['ip'])) { $ips[] = $r['ip']; } } foreach ((@dns_get_record($host, DNS_AAAA) ?: []) as $r) { if (!empty($r['ipv6'])) { $ips[] = $r['ipv6']; } } return $ips; } $target = trim((string) ($_GET['target'] ?? '')); $action = (string) ($_GET['action'] ?? 'ping'); $dnsType = strtoupper(trim((string) ($_GET['type'] ?? 'A'))); $resolver = strtolower(trim((string) ($_GET['resolver'] ?? ''))); $inputError = null; $result = null; $hops = []; $hopInfo = []; if (!in_array($action, ['ping', 'traceroute', 'dns'], true)) { $action = 'ping'; } if (!in_array($dnsType, DNS_TYPES, true)) { $dnsType = 'A'; } if ($target !== '') { $target = strtolower(rtrim($target, '.')); $isIp = (bool) filter_var($target, FILTER_VALIDATE_IP); if (!$isIp && !preg_match('/^(?=.{1,253}$)([a-z0-9](-?[a-z0-9])*\.)+[a-z]{2,}$/', $target)) { $inputError = 'Please enter a valid hostname (e.g. example.com) or IP address (e.g. 8.8.8.8).'; } elseif ($resolver !== '' && !filter_var($resolver, FILTER_VALIDATE_IP) && !preg_match('/^(?=.{1,253}$)([a-z0-9](-?[a-z0-9])*\.)+[a-z]{2,}$/', $resolver)) { $inputError = 'The resolver must be an IP address (e.g. 9.9.9.9) or a hostname.'; } elseif ($isIp && isReservedIp($target)) { $inputError = 'Private, loopback and reserved addresses cannot be probed from this tool.'; } elseif ($resolver !== '' && filter_var($resolver, FILTER_VALIDATE_IP) && isReservedIp($resolver)) { $inputError = 'Private, loopback and reserved addresses cannot be used as a resolver.'; } elseif ($action === 'dns' && $dnsType === 'PTR' && !$isIp) { $inputError = 'A PTR lookup needs an IP address as the target.'; } // A hostname that only resolves into reserved space is the same problem one step removed. if ($inputError === null && !$isIp) { $resolved = resolveHost($target); if (!empty($resolved) && count(array_filter($resolved, 'isReservedIp')) === count($resolved)) { $inputError = 'That hostname only resolves to private or reserved addresses, which cannot be probed.'; } } if ($inputError === null) { if ($action === 'ping') { $result = performPing($target); } elseif ($action === 'traceroute') { $result = performTraceroute($target); $hops = parseTracerouteHops($result['output']); $hopInfo = lookupIpInfo(array_column($hops, 'ip')); } else { $result = performDns($target, $dnsType, $resolver); } } } $clientIp = (string) ($_SERVER['REMOTE_ADDR'] ?? ''); $clientInfo = $clientIp !== '' && !isReservedIp($clientIp) ? (lookupIpInfo([$clientIp])[$clientIp] ?? null) : null; ?> Network Trace

📡 Network Trace

Runs ping, traceroute and DNS lookups from this server against a public host, and enriches every traceroute hop with its ASN, country and company. Probes are capped at s and hops.

—

⚠️ The command exited with code .

Hops ()

# IP Reverse DNS ASN Company / ISP Country

Hops answering with * * * are omitted from this table. IP intelligence via ip-api.com (free tier).

Enter a hostname or IP address above to run a probe.

Your connection

IP address:

ISP:

ASN:

Location:

No public IP intelligence available for this address.