array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w") ); // sanitize + remove single quotes $host = str_replace('\'', '', filter_var($host, FILTER_SANITIZE_URL)); // execute command $process = proc_open("{$cmd} '{$host}'", $spec, $pipes, null); // check pipe exists if (!is_resource($process)) { return false; } // check for mtr/traceroute if (strpos($cmd, 'mtr') !== false) { $type = 'mtr'; } elseif (strpos($cmd, 'traceroute') !== false) { $type = 'traceroute'; } else { $type = ''; } $fail = 0; $match = 0; $traceCount = 0; $lastFail = 'start'; // iterate stdout while (($str = fgets($pipes[1], 4096)) != null) { // check for output buffer if (ob_get_level() == 0) { ob_start(); } // fix RDNS XSS (outputs non-breakble space correctly) $str = htmlspecialchars(trim($str)); // correct output for mtr if ($type === 'mtr') { if ($match < 10 && preg_match('/^[0-9]\. /', $str, $string)) { $str = preg_replace('/^[0-9]\. /', '  ' . $string[0], $str); $match++; } else { $str = preg_replace('/^[0-9]{2}\. /', ' ' . substr($str, 0, 4), $str); } } // correct output for traceroute elseif ($type === 'traceroute') { if ($match < 10 && preg_match('/^[0-9] /', $str, $string)) { $str = preg_replace('/^[0-9] /', ' ' . $string[0], $str); $match++; } // check for consecutive failed hops if (strpos($str, '* * *') !== false) { $fail++; if ($lastFail !== 'start' && ($traceCount - 1) === $lastFail && $fail >= $failCount ) { echo str_pad($str . '
-- Traceroute timed out --
', 4096, ' ', STR_PAD_RIGHT); break; } $lastFail = $traceCount; } $traceCount++; } // pad string for live output echo str_pad($str . '
', 4096, ' ', STR_PAD_RIGHT); // flush output buffering @ob_flush(); flush(); } // iterate stderr while (($err = fgets($pipes[2], 4096)) != null) { // check for IPv6 hostname passed to IPv4 command, and vice versa if (strpos($err, 'Name or service not known') !== false || strpos($err, 'unknown host') !== false) { echo 'Unauthorized request'; break; } } $status = proc_get_status($process); if ($status['running'] == true) { // close pipes that are still open foreach ($pipes as $pipe) { fclose($pipe); } // retrieve parent pid $ppid = $status['pid']; // use ps to get all the children of this process $pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $ppid`); // kill remaining processes foreach($pids as $pid) { if (is_numeric($pid)) { posix_kill($pid, 9); } } proc_close($process); } return true; } }