1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4/** 5 * Simple wrapper interface for the Nmap utility. 6 * 7 * PHP version 5 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330,Boston,MA 02111-1307 USA 22 * 23 * @category Net 24 * @package Net_Nmap 25 * @author Luca Corbo <lucor@ortro.net> 26 * @copyright 2008 Luca Corbo 27 * @license GNU/LGPL v2.1 28 * @link http://pear.php.net/packages/Net_Nmap 29 */ 30 31require_once 'System.php'; 32require_once 'Net/Nmap/Parser.php'; 33require_once 'Net/Nmap/Exception.php'; 34 35/** 36 * Simple wrapper interface for the Nmap utility. 37 * 38 * @category Net 39 * @package Net_Nmap 40 * @author Luca Corbo <lucor@ortro.net> 41 * @copyright 2008 Luca Corbo 42 * @license GNU/LGPL v2.1 43 * @link http://pear.php.net/packages/Net_Nmap 44 */ 45class Net_Nmap 46{ 47 48 /** 49 * Location of the nmap binary 50 * 51 * @var string $nmap_path 52 * @see Net_Nmap::__construct() 53 */ 54 private $_nmap_binary; 55 56 /** 57 * Absolute path to store the Nmap XML output file. 58 * 59 * @var string 60 * @see Net_Nmap::__construct() 61 */ 62 private $_output_file = null; 63 64 /** 65 * Delete Nmap output file after parsing 66 * 67 * @var bool 68 */ 69 private $_delete_output_file = true; 70 71 /** 72 * The hostname/IP failed to resolve 73 * 74 * @var array 75 */ 76 private $_failed_to_resolve = array(); 77 78 /** 79 * Nmap option arguments 80 * 81 * @var array 82 * @link http://nmap.org/book/man-briefoptions.html 83 */ 84 private $_nmap_options = array(); 85 86 /** 87 * Statistics object from Nmap scan 88 * 89 * @var Net_Nmap_Stats 90 */ 91 private $_nmap_stats = null; 92 93 /** 94 * Creates a new Nmap object 95 * 96 * Available options are: 97 * 98 * - string nmap_binary: The location of the Nmap binary. 99 * If not specified, defaults to '/usr/bin/nmap'. 100 * 101 * - string output_file: Path to store the Nmap XML output file. 102 * If not specified, a temporary file is created. 103 * 104 * @param array $options optional. An array of options used to create the 105 * Nmap object. All options must be optional and are 106 * represented as key-value pairs. 107 */ 108 public function __construct(array $options = array()) 109 { 110 if (array_key_exists('nmap_binary', $options)) { 111 $this->_nmap_binary = (string)$options['nmap_binary']; 112 } else { 113 $this->_nmap_binary = System::which('nmap'); 114 } 115 116 if (array_key_exists('output_file', $options)) { 117 $this->_output_file = (string)$options['output_file']; 118 } 119 } 120 121 /** 122 * Prepare the command to execute 123 * 124 * @param array $targets contains hostnames, IP addresses, networks to scan 125 * 126 * @return string 127 */ 128 protected function createCommandLine($targets) 129 { 130 131 if ($this->_output_file === null) { 132 $this->_output_file = tempnam(System::tmpdir(), __CLASS__); 133 } else { 134 $this->_delete_output_file = false; 135 } 136 137 $cmd = escapeshellarg($this->_nmap_binary); 138 $cmd .= ' ' . implode(' ', $this->_nmap_options); 139 $cmd .= ' -oX ' . escapeshellarg($this->_output_file) . ' '; 140 foreach ($targets as $target) { 141 $cmd .= escapeshellarg($target) . ' '; 142 } 143 $cmd .= '2>&1'; 144 if (OS_WINDOWS) { 145 $cmd = '"' . $cmd . '"'; 146 } 147 return $cmd; 148 } 149 150 /** 151 * Scan the specified target 152 * 153 * @param array $targets Array contains hostnames, IP addresses, 154 * networks to scan 155 * @param bool $with_sudo Boolean to enable or not sudo scan 156 * 157 * @return true | PEAR_Error 158 * @throws Net_Nmap_Exception If Nmap binary does not exist or 159 * the command failed to execute. 160 */ 161 public function scan($targets, $with_sudo = false) 162 { 163 $sudo = $with_sudo 164 ? 'sudo ' : ''; 165 166 exec($sudo . $this->createCommandLine($targets), $out, $ret_var); 167 168 if ($ret_var > 0) { 169 throw new Net_Nmap_Exception(implode(' ', $out)); 170 } else { 171 foreach ($out as $row) { 172 preg_match( 173 '@^Failed to resolve given hostname/IP:\s+(.+)\.\s+Note@', 174 $row, 175 $matches 176 ); 177 178 if (count($matches) > 0) { 179 $this->_failed_to_resolve[] = $matches[1]; 180 } 181 } 182 return true; 183 } 184 } 185 186 /** 187 * Get all the discovered hosts 188 * 189 * @param string $output_file Absolute path of the file to parse (optional) 190 * 191 * @return ArrayIterator Returns Hosts Object on success. 192 * @throws Net_Nmap_Exception If a parsing error occurred. 193 */ 194 public function parseXMLOutput($output_file = null) 195 { 196 if ($output_file === null) { 197 $output_file = $this->_output_file; 198 } else { 199 $this->_delete_output_file = false; 200 } 201 $parse = new Net_Nmap_Parser(); 202 $parse->setInputFile($output_file); 203 $parse->folding = false; 204 205 $res = $parse->parse(); 206 if (PEAR::isError($res)) { 207 throw new Net_Nmap_Exception($res); 208 } 209 if ($this->_delete_output_file) { 210 unlink($this->_output_file); 211 } 212 $this->_nmap_stats = $parse->getStats(); 213 return $parse->getHosts(); 214 } 215 216 /** 217 * Get all the hostnames/IPs failed to resolve during scanning operation 218 * 219 * @return Array Returns array 220 */ 221 public function getFailedToResolveHosts() 222 { 223 return $this->_failed_to_resolve; 224 } 225 226 /** 227 * Get Nmap Statistics 228 * 229 * @return Net_Nmap_Stats Return an Nmap Stats Object 230 */ 231 public function getNmapStats() 232 { 233 return $this->_nmap_stats; 234 } 235 236 /** 237 * Enable Nmap options 238 * Available nmap options are: 239 * 240 * - boolean os_detection: Enable the OS detection (-O). 241 * - boolean service_info: Probe open ports to determine 242 * service/version info (-sV) 243 * - string port_ranges : Port ranges, only scan 244 * specified ports (-p <port ranges>) 245 * Ex: 22; 1-65535; U:53,111,137,T:21-25,80,139,8080 246 * - boolean all_options : Enables OS detection and Version detection, 247 * Script scanning and Traceroute (-A) 248 * 249 * @param array $nmap_options Nmap options to enable 250 * 251 * @return void 252 * @link http://nmap.org/book/man-briefoptions.html 253 * @throws Net_Nmap_Exception If the option argument is not valid. 254 */ 255 public function enableOptions($nmap_options) 256 { 257 $enable_os_detection = array_key_exists('os_detection', $nmap_options); 258 $enable_service_info = array_key_exists('service_info', $nmap_options); 259 $enable_port_ranges = array_key_exists('port_ranges', $nmap_options); 260 $enable_all_options = array_key_exists('all_options', $nmap_options); 261 262 if ($enable_os_detection && $nmap_options['os_detection']) { 263 $this->_nmap_options[] = '-O'; 264 } 265 if ($enable_service_info && $nmap_options['service_info']) { 266 $this->_nmap_options[] = '-sV'; 267 } 268 if ($enable_port_ranges) { 269 $port_ranges = $nmap_options['port_ranges']; 270 $allowed_format = '([U,T]\:)*[0-9]+(-[0-9]+)*'; 271 272 $regexp = '/^' . $allowed_format . '(,' . $allowed_format . ')*$/'; 273 if (preg_match($regexp, $port_ranges)) { 274 $this->_nmap_options[] = '-p ' . $port_ranges; 275 } else { 276 throw new Net_Nmap_Exception('Port ranges: not valid format.'); 277 } 278 } 279 if ($enable_all_options && $nmap_options['all_options']) { 280 $this->_nmap_options[] = '-A'; 281 } 282 } 283} 284