1<?php 2 3/** 4 * This script does the following: 5 * - fetches all subnets that are marked for discovering new hosts 6 * - Scans each subnet for new hosts 7 * - If new host is discovered it will be added to database 8 * 9 * Scan type be used as defined under administration: 10 * - ping 11 * - pear ping 12 * - fping 13 * 14 * Fping is new since version 1.2, it will work faster because it has built-in threading 15 * so we are only forking separate subnets 16 * 17 * Script must be run from cron, here is a crontab example, 1x/day should be enough: 18 * 0 1 * * * /usr/local/bin/php /<sitepath>/functions/scripts/pingCheck.php > /dev/null 2>&1 19 * 20 * 21 * In case of problems set reset_debugging to true 22 * 23 */ 24 25# include required scripts 26require_once( dirname(__FILE__) . '/../functions.php' ); 27require( dirname(__FILE__) . '/../../functions/classes/class.Thread.php'); 28 29# initialize objects 30$Database = new Database_PDO; 31$Subnets = new Subnets ($Database); 32$Addresses = new Addresses ($Database); 33$Tools = new Tools ($Database); 34$Scan = new Scan ($Database); 35$DNS = new DNS ($Database); 36$Result = new Result(); 37 38// set exit flag to true 39$Scan->ping_set_exit(true); 40// set debugging 41$Scan->set_debugging(false); 42// change scan type? 43if(@$config['discovery_check_method']) 44$Scan->reset_scan_method ($config['discovery_check_method']); 45// set ping statuses 46$statuses = explode(";", $Scan->settings->pingStatus); 47// set mail override flag 48if(!isset($config['discovery_check_send_mail'])) { 49 $config['discovery_check_send_mail'] = true; 50} 51 52// set now for whole script 53$now = time(); 54$nowdate = date ("Y-m-d H:i:s"); 55 56// response for mailing 57$address_change = array(); // Array with differences, can be used to email to admins 58$hostnames = array(); // Array with detected hostnames 59 60 61// script can only be run from cli 62if(php_sapi_name()!="cli") { die("This script can only be run from cli!"); } 63// test to see if threading is available 64if(!PingThread::available($errmsg)) { die("Threading is required for scanning subnets - Error: $errmsg\n"); } 65// verify ping path 66if ($Scan->icmp_type=="ping") { 67if(!file_exists($Scan->settings->scanPingPath)) { die("Invalid ping path!"); } 68} 69// verify fping path 70if ($Scan->icmp_type=="fping") { 71if(!file_exists($Scan->settings->scanFPingPath)){ die("Invalid fping path!"); } 72} 73 74 75//first fetch all subnets to be scanned 76$scan_subnets = $Subnets->fetch_all_subnets_for_discoveryCheck (1); 77//set addresses 78if ($scan_subnets!==false) { 79 // initial array 80 $addresses_tmp = array(); 81 // loop 82 foreach($scan_subnets as $i => $s) { 83 // if subnet has slaves dont check it 84 if ($Subnets->has_slaves ($s->id) === false) { 85 $addresses_tmp[$s->id] = $Scan-> prepare_addresses_to_scan ("discovery", $s->id, false); 86 // save discovery time 87 $Scan->update_subnet_discoverytime ($s->id, $nowdate); 88 } else { 89 unset( $scan_subnets[$i] ); 90 } 91 } 92 93 //reindex 94 if(sizeof($addresses_tmp)>0) { 95 foreach($addresses_tmp as $s_id=>$a) { 96 foreach($a as $ip) { 97 $addresses[] = array("subnetId"=>$s_id, "ip_addr"=>$ip); 98 } 99 } 100 } 101} 102 103 104if($Scan->get_debugging()==true) { print_r($scan_subnets); } 105if($scan_subnets===false || !count($scan_subnets)) { die("No subnets are marked for new hosts checking\n"); } 106 107 108//scan 109if($Scan->get_debugging()==true) { print "Using $Scan->icmp_type\n--------------------\n\n"; } 110 111 112$z = 0; //addresses array index 113 114// let's just reindex the subnets array to save future issues 115$scan_subnets = array_values($scan_subnets); 116$size_subnets = count($scan_subnets); 117$size_addresses = max(array_keys($addresses)); 118 119//different scan for fping 120if($Scan->icmp_type=="fping") { 121 //run per MAX_THREADS 122 for ($m=0; $m<=$size_subnets; $m += $Scan->settings->scanMaxThreads) { 123 // create threads 124 $threads = array(); 125 //fork processes 126 for ($i = 0; $i <= $Scan->settings->scanMaxThreads && $i <= $size_subnets; $i++) { 127 //only if index exists! 128 if(isset($scan_subnets[$z])) { 129 //start new thread 130 $threads[$z] = new PingThread( 'fping_subnet' ); 131 $threads[$z]->start_fping( $Subnets->transform_to_dotted($scan_subnets[$z]->subnet)."/".$scan_subnets[$z]->mask ); 132 $z++; //next index 133 } 134 } 135 // wait for all the threads to finish 136 while( !empty( $threads ) ) { 137 foreach($threads as $index => $thread) { 138 $child_pipe = "/tmp/pipe_".$thread->getPid(); 139 140 if (file_exists($child_pipe)) { 141 $file_descriptor = fopen( $child_pipe, "r"); 142 $child_response = ""; 143 while (!feof($file_descriptor)) { 144 $child_response .= fread($file_descriptor, 8192); 145 } 146 //we have the child data in the parent, but serialized: 147 $child_response = unserialize( $child_response ); 148 //store 149 $scan_subnets[$index]->discovered = $child_response; 150 //now, child is dead, and parent close the pipe 151 unlink( $child_pipe ); 152 unset($threads[$index]); 153 } 154 } 155 usleep(200000); 156 } 157 } 158 159 //fping finds all subnet addresses, we must remove existing ones ! 160 foreach($scan_subnets as $sk=>$s) { 161 if(isset($s->discovered)) { 162 foreach($s->discovered as $rk=>$result) { 163 if(!in_array($Subnets->transform_to_decimal($result), $addresses_tmp[$s->id])) { 164 unset($scan_subnets[$sk]->discovered[$rk]); 165 } 166 } 167 //rekey 168 $scan_subnets[$sk]->discovered = array_values($scan_subnets[$sk]->discovered); 169 } 170 } 171} 172//ping, pear 173else { 174 //run per MAX_THREADS 175 for ($m=0; $m<=$size_addresses; $m += $Scan->settings->scanMaxThreads) { 176 // create threads 177 $threads = array(); 178 179 //fork processes 180 for ($i = 0; $i <= $Scan->settings->scanMaxThreads && $i <= $size_addresses; $i++) { 181 //only if index exists! 182 if(isset($addresses[$z])) { 183 //start new thread 184 $threads[$z] = new PingThread( 'ping_address' ); 185 $threads[$z]->start( $Subnets->transform_to_dotted($addresses[$z]['ip_addr']) ); 186 $z++; //next index 187 } 188 } 189 190 // wait for all the threads to finish 191 while( !empty( $threads ) ) { 192 foreach( $threads as $index => $thread ) { 193 if( ! $thread->isAlive() ) { 194 //unset dead hosts 195 if($thread->getExitCode() != 0) { 196 unset($addresses[$index]); 197 } 198 //remove thread 199 unset( $threads[$index]); 200 } 201 } 202 usleep(200000); 203 } 204 } 205 206 //ok, we have all available addresses, rekey them 207 foreach($addresses as $a) { 208 $add_tmp[$a['subnetId']][] = $Subnets->transform_to_dotted($a['ip_addr']); 209 } 210 //add to scan_subnets as result 211 foreach($scan_subnets as $sk=>$s) { 212 if(isset($add_tmp[$s->id])) { 213 $scan_subnets[$sk]->discovered = $add_tmp[$s->id]; 214 } 215 } 216} 217 218 219# print change 220if($Scan->get_debugging()==true) { "\nDiscovered addresses:\n----------\n"; print_r($scan_subnets); } 221 222 223 224# reinitialize objects 225$Database = new Database_PDO; 226$Admin = new Admin ($Database, false); 227$Addresses = new Addresses ($Database); 228$Subnets = new Subnets ($Database); 229$DNS = new DNS ($Database); 230$Scan = new Scan ($Database); 231$Result = new Result(); 232 233# insert to database 234$discovered = 0; //for mailing 235 236foreach($scan_subnets as $s) { 237 if(isset($s->discovered)) { 238 foreach($s->discovered as $ip) { 239 // fetch subnet 240 $subnet = $Subnets->fetch_subnet ("id", $s->id); 241 $nsid = $subnet===false ? false : $subnet->nameserverId; 242 // try to resolve hostname 243 $hostname = $DNS->resolve_address ($ip, false, true, $nsid); 244 // save to hostnames 245 $hostnames[$ip] = $hostname['name']==$ip ? "" : $hostname['name']; 246 247 //set update query 248 $values = array( 249 "subnetId" =>$s->id, 250 "ip_addr" =>$ip, 251 "hostname" =>$hostname['name'], 252 "description" =>"-- autodiscovered --", 253 "note" =>"This host was autodiscovered on ".$nowdate, 254 "lastSeen" =>$nowdate, 255 "state" =>"2", 256 "action" =>"add" 257 ); 258 //insert 259 $Addresses->modify_address($values); 260 261 //set discovered 262 $discovered++; 263 } 264 } 265} 266 267# update scan time 268$Scan->ping_update_scanagent_checktime (1, $nowdate); 269 270 271 272# send mail 273if($discovered>0 && $config['discovery_check_send_mail']) { 274 275 # check for recipients 276 foreach($Admin->fetch_multiple_objects ("users", "role", "Administrator") as $admin) { 277 if($admin->mailNotify=="Yes") { 278 $recepients[] = array("name"=>$admin->real_name, "email"=>$admin->email); 279 } 280 } 281 # none? 282 if(!isset($recepients)) { die(); } 283 284 # fake user object, needed for create_link 285 $User = new StdClass(); 286 @$User->settings->prettyLinks = $Scan->settings->prettyLinks; 287 288 # try to send 289 try { 290 # fetch mailer settings 291 $mail_settings = $Admin->fetch_object("settingsMail", "id", 1); 292 # initialize mailer 293 $phpipam_mail = new phpipam_mail($Scan->settings, $mail_settings); 294 295 // set subject 296 $subject = "phpIPAM new addresses detected ".date("Y-m-d H:i:s"); 297 298 //html 299 $content[] = "<h3>phpIPAM found $discovered new hosts</h3>"; 300 $content[] = "<table style='margin-left:10px;margin-top:5px;width:auto;padding:0px;border-collapse:collapse;border:1px solid gray;'>"; 301 $content[] = "<tr>"; 302 $content[] = " <th style='padding:3px 8px;border:1px solid silver;border-bottom:2px solid gray;'>IP</th>"; 303 $content[] = " <th style='padding:3px 8px;border:1px solid silver;border-bottom:2px solid gray;'>Hostname</th>"; 304 $content[] = " <th style='padding:3px 8px;border:1px solid silver;border-bottom:2px solid gray;'>Subnet</th>"; 305 $content[] = " <th style='padding:3px 8px;border:1px solid silver;border-bottom:2px solid gray;'>Section</th>"; 306 $content[] = "</tr>"; 307 //plain 308 $content_plain[] = "phpIPAM found $discovered new hosts\r\n------------------------------"; 309 //Changes 310 foreach($scan_subnets as $s) { 311 if(is_array($s->discovered)) { 312 foreach($s->discovered as $ip) { 313 //set subnet 314 $subnet = $Subnets->fetch_subnet(null, $s->id); 315 //set section 316 $section = $Admin->fetch_object("sections", "id", $s->sectionId); 317 318 $content[] = "<tr>"; 319 $content[] = " <td style='padding:3px 8px;border:1px solid silver;'>$ip</td>"; 320 $content[] = " <td style='padding:3px 8px;border:1px solid silver;'>".$hostnames[$ip]."</td>"; 321 $content[] = " <td style='padding:3px 8px;border:1px solid silver;'><a href='".rtrim($Scan->settings->siteURL, "/")."".create_link("subnets",$section->id,$subnet->id)."'>".$Subnets->transform_to_dotted($subnet->subnet)."/".$subnet->mask." - ".$subnet->description."</a></td>"; 322 $content[] = " <td style='padding:3px 8px;border:1px solid silver;'><a href='".rtrim($Scan->settings->siteURL, "/")."".create_link("subnets",$section->id)."'>$section->name $section->description</a></td>"; 323 $content[] = "</tr>"; 324 325 //plain content 326 $content_plain[] = "\t * $ip (".$Subnets->transform_to_dotted($subnet->subnet)."/".$subnet->mask.")"; 327 } 328 } 329 } 330 $content[] = "</table>"; 331 332 333 # set content 334 $content = $phpipam_mail->generate_message (implode("\r\n", $content)); 335 $content_plain = implode("\r\n",$content_plain); 336 337 $phpipam_mail->Php_mailer->setFrom($mail_settings->mAdminMail, $mail_settings->mAdminName); 338 //add all admins to CC 339 foreach($recepients as $admin) { 340 $phpipam_mail->Php_mailer->addAddress(addslashes($admin['email']), addslashes($admin['name'])); 341 } 342 $phpipam_mail->Php_mailer->Subject = $subject; 343 $phpipam_mail->Php_mailer->msgHTML($content); 344 $phpipam_mail->Php_mailer->AltBody = $content_plain; 345 //send 346 $phpipam_mail->Php_mailer->send(); 347 } catch (phpmailerException $e) { 348 $Result->show_cli("Mailer Error: ".$e->errorMessage(), true); 349 } catch (Exception $e) { 350 $Result->show_cli("Mailer Error: ".$e->getMessage(), true); 351 } 352}