File: 1.13.3b/server/base/tests/ppping.inc.php (View as HTML)

  1: <?php
  2: /* -------------------------------------------------------------
  3: This file is part of PurplePixie Ping (PPPing)
  4: 
  5: PPPing is (C) Copyright 2010 PurplePixie Systems
  6: 
  7: PPPing is free software: you can redistribute it and/or modify
  8: it under the terms of the GNU General Public License as published by
  9: the Free Software Foundation, either version 3 of the License, or
 10: (at your option) any later version.
 11: 
 12: PPPing is distributed in the hope that it will be useful,
 13: but WITHOUT ANY WARRANTY; without even the implied warranty of
 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15: GNU General Public License for more details.
 16: 
 17: You should have received a copy of the GNU General Public License
 18: along with PPPing.  If not, see www.gnu.org/licenses
 19: 
 20: For more information see www.purplepixie.org/phpping
 21: -------------------------------------------------------------- */
 22: 
 23: /**
 24:  * PPPing PHP Ping Utility
 25:  * @package PPPing PHP Ping Utility
 26:  * @author David Cutting
 27:  * @version 0.01
 28: **/
 29: 
 30: /**
 31:  * Main PPPing Class
 32:  * @package PPPing PHP Ping Utility
 33: **/
 34: class PPPing
 35: {
 36: /**
 37:  * Time-to-Live for IP Packet (DO NOT USE)
 38:  *
 39:  * -1 Uses system default (usually 64). Please note that this is currently
 40:  * not functional.
 41: **/
 42: var $ttl=-1;
 43: 
 44: /**
 45:  * Hostname to ping (resolvable host or IP address)
 46: **/
 47: var $hostname="";
 48: 
 49: /**
 50:  * Identifier - will fill with random content (16 bits)
 51: **/
 52: var $identity=0;
 53: 
 54: /**
 55:  * Sequence number in decimal (16 bits)
 56: **/
 57: var $sequence=0;
 58: 
 59: /**
 60:  * Timeout in seconds - maximum wait for a response before timeout
 61: **/
 62: var $timeout=10;
 63: 
 64: /**
 65:  * Timer start seconds
 66: **/
 67: var $timer_start_sec=0;
 68: 
 69: /**
 70:  * Timer start mseconds
 71: **/
 72: var $timer_start_msec=0;
 73: 
 74: /**
 75:  * Data package for the ping
 76: **/
 77: var $data_package = "PPPing";
 78: 
 79: /**
 80:  * Debug - prints output to the screen
 81: **/
 82: var $debug=false;
 83: 
 84: /**
 85:  * Holds information on last result
 86: **/
 87: var $Last = array();
 88: 
 89: /**
 90:  * Clears last data
 91: **/
 92: function clearLast()
 93: 	{
 94: 	$this->last = array(
 95: 		"set" => false,
 96: 		"result" => 0,
 97: 		"ttl" => 0,
 98: 		"hops" => 0,
 99: 		"source" => "",
100: 		"destination" => "" );
101: 	}
102: /**
103:  * Get a padded hex identifier
104: **/
105: function getIdentity()
106: 	{
107: 	if ( (is_numeric($this->identity)) && ($this->identity>=0) && ($this->identity<65535) )
108: 		$id=$this->identity;
109: 	else $id=0;
110: 	
111: 	$id=dechex($id);
112: 	$id=str_pad($id,4,"0",STR_PAD_LEFT);
113: 	$id=pack("H*",$id);
114: 	
115: 	return $id;
116: 	}
117: 
118: /**
119:  * Get a padded hex sequence
120: **/
121: function getSequence()
122: 	{
123: 	if ( (is_numeric($this->sequence)) && ($this->sequence>=0) && ($this->sequence<65535) )
124: 		$seq=$this->sequence;
125: 	else $seq=0;
126: 	$seq=dechex($seq);
127: 	$seq=str_pad($seq,4,"0",STR_PAD_LEFT);
128: 	$seq=pack("H*",$seq);
129: 	
130: 	return $seq;
131: 	}
132: 	
133: /**
134:  * Returns a hex string of the binary data for debug purposes
135: **/
136: function getHex($data)
137: 	{
138: 	$parts=unpack("H*",$data);
139: 	return $parts[1];
140: 	}
141: 	
142: /**
143:  * Randomise identity and/or sequence within 16 bit parameters
144: **/
145: function Randomise($identity=true,$sequence=false)
146: 	{
147: 	mt_srand(microtime()*1000000);
148: 	if ($identity) $this->identity=mt_rand(0,65534);
149: 	if ($sequence) $this->sequence=mt_rand(0,65534);
150: 	}
151: 	
152: /**
153:  * Start timer (reset values)
154: **/
155: function startTimer()
156: 	{
157: 	$now=microtime();
158: 	$timearray=explode(" ",$now);
159: 	$this->timer_start_sec=$timearray[1];
160: 	$this->timer_start_msec=$timearray[0];
161: 	}
162: 	
163: /**
164:  * Stop timer (return result)
165: **/
166: function stopTimer()
167: 	{
168: 	$now=microtime();
169: 	$timearray=explode(" ",$now);
170: 	
171: 	$finish_secs=$timearray[1];
172: 	$finish_msecs=$timearray[0];
173: 	
174: 	$elapsed_seconds = $finish_secs - $this->timer_start_sec;
175: 	$elapsed_time = $elapsed_seconds + $finish_msecs - $this->timer_start_msec;
176: 	
177: 	$elapsed_ms = $elapsed_time * 1000;
178: 	
179: 	$elapsed_ms = round($elapsed_ms,3);
180: 	
181: 	return $elapsed_ms;
182: 	}
183: 	
184: 	
185: /**
186:  * Constructor - randomises ID
187: **/
188: function PPPing()
189: 	{
190: 	$this->Randomise();
191: 	}
192: 	
193: /**
194:  * Returns a dotted quad from hex format IPv4 address
195: **/
196: function ipAddress($hexip)
197: 	{
198: 	$quad="";
199: 	for($a=0; $a<=6; $a+=2)
200: 		{
201: 		$portion=substr($hexip,$a,2);
202: 		$decimal=hexdec($portion);
203: 		if ($quad!="") $quad.=".";
204: 		$quad.=$decimal;
205: 		}
206: 	return $quad;
207: 	}
208: 	
209: /**
210:  * Generate an ICMP checksum
211: **/
212: function Checksum($data)
213:     {
214:     if (strlen($data)%2)
215: 		$data .= "\x00";
216:     
217:     $bit = unpack('n*', $data);
218:     $sum = array_sum($bit);
219:     
220:     while ($sum >> 16)
221:     $sum = ($sum >> 16) + ($sum & 0xffff);
222:     
223:     return pack('n*', ~$sum);
224:     }
225: 
226: /**
227:  * Do a ping of the set hostname
228:  *
229:  * @return float
230:  * Returns a negative number of failure which can be turned into text with
231:  * the strError method. A positive number is a response in milliseconds (ms)
232: **/
233: function Ping()
234: 	{
235: 	$this->clearLast();
236:     $type = "\x08"; // icmp echo
237:     $code = "\x00"; 
238:     $checksum = "\x00\x00"; // initial
239:     $identifier = $this->getIdentity();
240: 	$dec_identity = $this->identity;
241: 	//$identifier = "\x00\x00";
242: 	//$seqNumber = "\x00\x00";
243:     $seqNumber = $this->getSequence();
244: 	$dec_sequence = $this->sequence;
245:     $data = $this->data_package;
246:     $package = $type.$code.$checksum.$identifier.$seqNumber.$data;
247:     $checksum = $this->Checksum($package); // proper checksum
248:     $package = $type.$code.$checksum.$identifier.$seqNumber.$data;
249: 	
250: 	$ip_protocol_code = getprotobyname("ip");
251: 	$ip_ttl_code = 7;
252:     
253: 	// Lookup hostname
254: 	$ips=str_replace(".","",$this->hostname);
255: 	if (!is_numeric($ips))
256: 		{
257: 		$host=gethostbyname($this->hostname);
258: 		if ($host==$this->hostname) return -5;
259: 		}
260: 	else $host=$this->hostname;
261:     
262:     // Create Socket
263:     $socket = socket_create(AF_INET, SOCK_RAW, 1); // @
264:     	//or die(socket_strerror(socket_last_error()));
265:     if (!$socket) return -3;
266:     
267:     // Set Non-Blocking
268:     socket_set_nonblock($socket); // @
269: 	
270: 	$socket_ttl = socket_get_option($socket,$ip_protocol_code,$ip_ttl_code);
271: 	
272: 	//for ($a=0; $a<64; $a++)
273: 	//	echo $a." - ".@socket_get_option($socket,$ip_protocol_code,$a)."\n";
274: 	
275: 	if ($this->ttl>0)
276: 		{
277: 		socket_set_option($socket,$ip_protocol_code,$ip_ttl_code,128);
278: 		$socket_ttl = socket_get_option($socket,$ip_protocol_code,$ip_ttl_code);
279: 		//socket_set_option($socket,Socket::IPPROTO_IP,Socket::IP_TTL,128);
280: 		//$socket_ttl = socket_get_option($socket,Socket::IPPROTO_IP,Socket::IP_TTL);
281: 		
282: 		}
283: 	else $socket_ttl = 64; // standard TTL
284: 		
285:     	
286:     // Connect Socket
287:     $sconn=socket_connect($socket, $host, null); // @
288:     if (!$sconn) return 0;
289:     
290: 	// Package Size
291: 	//$package_size = 8+strlen($data);
292: 	$package_size = strlen($package);
293: 	
294:     // Send Data
295:     socket_send($socket, $package, $package_size, 0); // @
296:         
297:     // Start Timer
298:     $this->startTimer();
299:     $startTime = microtime(true); // need this for the looping section
300:     
301: 
302:     // Read Data
303:     $keepon=true;
304: 
305:     while( (false===($echo_reply=socket_read($socket, 255))) && $keepon) // @socket_read
306:     	{ 
307:     	
308:     	if ( (microtime(true) - $startTime) > $this->timeout )
309:     		$keepon=false;
310: 			
311: 		}
312:     	
313: 		
314: 	if ($keepon) // didn't time out - read data
315:     	{
316: 	    $elapsed=$this->stopTimer();
317: 		
318: 		socket_close($socket); // @
319: 		
320: 		if ( $echo_reply === false ) return -4;
321: 		else if (strlen($echo_reply)<2) return -2;
322: 	
323: 		$rx_parts = unpack("C*",$echo_reply);
324: 		$tx_parts = unpack("C*",$package);
325: 		$ipheader="";
326: 		$ipheader_hex="";
327: 		
328: 		if ($rx_parts[1] == 0x45) // IP Header Information
329: 			{
330: 			$ipheader=substr($echo_reply,0,20);
331: 			$ipheader_hex = $this->getHex($ipheader);
332: 			$echo_reply=substr($echo_reply,20);
333: 			$rx_parts = unpack("C*",$echo_reply);
334: 			}
335: 			
336: 		if ($this->debug)
337: 			{
338: 			echo "\n";
339: 			echo "    TyCoChksIdenSequData\n";
340: 			echo "TX: ".$this->getHex($package)."\n";
341: 			echo "RX: ".$this->getHex($echo_reply)."\n";
342: 			if ($ipheader!="") echo "HR: ".$ipheader_hex."\n";
343: 			}
344: 			
345: 		$echo_reply_hex = $this->getHex($echo_reply);
346: 		$reply_type = $rx_parts[1];
347: 		$reply_code = $rx_parts[2];
348: 		$reply_identity = hexdec(substr($echo_reply_hex,8,4));
349: 		$reply_sequence = hexdec(substr($echo_reply_hex,12,4));
350: 			
351: 		$match=true;
352: 		if ($ipheader!="")
353: 			{
354: 			$source=substr($ipheader_hex,24,8);
355: 			$dest=substr($ipheader_hex,32,8);
356: 			$ttl=hexdec(substr($ipheader_hex,16,2));
357: 			if ($this->debug) echo $this->ipAddress($source)." => ".$this->ipAddress($dest)." | ttl: ".$ttl."\n";
358: 			if ($source==$dest) $match=true;
359: 			else $match=false;
360: 			
361: 			$this->last["set"]=true;
362: 			$this->last["source"]=$this->ipAddress($source);
363: 			$this->last["destination"]=$this->ipAddress($dest);
364: 			$this->last["ttl"]=$ttl;
365: 			$this->last["hops"]=$socket_ttl - $ttl;
366: 			
367: 			
368: 			}
369: 	
370: 		if ( (($rx_parts[1]==0) || (($rx_parts[1]==8)&&($match))) && ($rx_parts[2]==0) )
371: 			{ // is echo_reply (0) or is echo_request (8) AND match (from same host)
372: 			  // and has code of 0
373: 			// valid response
374: 			if ($reply_identity != $dec_identity) return -8; // ID mismatch
375: 			else if ($reply_sequence != $dec_sequence) return -7; // sequence mismatch
376: 			else
377: 				{
378: 				$this->last["result"]=$elapsed;
379: 				return $elapsed;
380: 				}
381: 			}
382: 		else
383: 			{ // ICMP Error
384: 			return -9;
385: 			}
386: 	
387: 		}
388:     socket_close($socket); // @
389:     return -1; // timeout
390: 	}
391: 	
392: /**
393:  * Returns textual error for code
394: **/
395: function strError($code)
396: 	{
397: 	switch($code)
398: 		{
399: 		case -1: return "Timed Out"; break;
400: 		case -2: return "Reply Too Short"; break;
401: 		case -3: return "Failed to Open Socket"; break;
402: 		case -4: return "Invalid (false) Response"; break;
403: 		case -5: return "Hostname Lookup Failed"; break;
404: 		case -7: return "Sequence Mismatch"; break;
405: 		case -8: return "Identity Mismatch"; break;
406: 		case -9: return "ICMP Generic Error"; break;
407: 		
408: 		default: return "Unknown Error"; break;
409: 		}
410: 	}
411: 
412: 
413: }
414: 
415: ?>