5f2545fccab69590a3579fdd80e6c77e65bcac5a
[p5sagit/p5-mst-13.2.git] / lib / Net / Ping.pm
1 package Net::Ping;
2
3 # $Id: Ping.pm,v 1.5 2001/11/19 09:44:18 rob Exp $
4
5 require 5.002;
6 require Exporter;
7
8 use strict;
9 use vars qw(@ISA @EXPORT $VERSION
10             $def_timeout $def_proto $max_datasize);
11 use FileHandle;
12 use Socket qw( SOCK_DGRAM SOCK_STREAM SOCK_RAW PF_INET
13                inet_aton sockaddr_in );
14 use Carp;
15
16 @ISA = qw(Exporter);
17 @EXPORT = qw(pingecho);
18 $VERSION = 2.06;
19
20 # Constants
21
22 $def_timeout = 5;           # Default timeout to wait for a reply
23 $def_proto = "udp";         # Default protocol to use for pinging
24 $max_datasize = 1024;       # Maximum data bytes in a packet
25
26 # Description:  The pingecho() subroutine is provided for backward
27 # compatibility with the original Net::Ping.  It accepts a host
28 # name/IP and an optional timeout in seconds.  Create a tcp ping
29 # object and try pinging the host.  The result of the ping is returned.
30
31 sub pingecho
32 {
33     my ($host,              # Name or IP number of host to ping
34         $timeout            # Optional timeout in seconds
35         ) = @_;
36     my ($p);                # A ping object
37
38     $p = Net::Ping->new("tcp", $timeout);
39     $p->ping($host);        # Going out of scope closes the connection
40 }
41
42 # Description:  The new() method creates a new ping object.  Optional
43 # parameters may be specified for the protocol to use, the timeout in
44 # seconds and the size in bytes of additional data which should be
45 # included in the packet.
46 #   After the optional parameters are checked, the data is constructed
47 # and a socket is opened if appropriate.  The object is returned.
48
49 sub new
50 {
51     my ($this,
52         $proto,             # Optional protocol to use for pinging
53         $timeout,           # Optional timeout in seconds
54         $data_size          # Optional additional bytes of data
55         ) = @_;
56     my  $class = ref($this) || $this;
57     my  $self = {};
58     my ($cnt,               # Count through data bytes
59         $min_datasize       # Minimum data bytes required
60         );
61
62     bless($self, $class);
63
64     $proto = $def_proto unless $proto;          # Determine the protocol
65     croak("Protocol for ping must be \"tcp\", \"udp\" or \"icmp\"")
66         unless $proto =~ m/^(tcp|udp|icmp)$/;
67     $self->{"proto"} = $proto;
68
69     $timeout = $def_timeout unless $timeout;    # Determine the timeout
70     croak("Default timeout for ping must be greater than 0 seconds")
71         if $timeout <= 0;
72     $self->{"timeout"} = $timeout;
73
74     $min_datasize = ($proto eq "udp") ? 1 : 0;  # Determine data size
75     $data_size = $min_datasize unless defined($data_size) && $proto ne "tcp";
76     croak("Data for ping must be from $min_datasize to $max_datasize bytes")
77         if ($data_size < $min_datasize) || ($data_size > $max_datasize);
78     $data_size-- if $self->{"proto"} eq "udp";  # We provide the first byte
79     $self->{"data_size"} = $data_size;
80
81     $self->{"data"} = "";                       # Construct data bytes
82     for ($cnt = 0; $cnt < $self->{"data_size"}; $cnt++)
83     {
84         $self->{"data"} .= chr($cnt % 256);
85     }
86
87     $self->{"seq"} = 0;                         # For counting packets
88     if ($self->{"proto"} eq "udp")              # Open a socket
89     {
90         $self->{"proto_num"} = (getprotobyname('udp'))[2] ||
91             croak("Can't udp protocol by name");
92         $self->{"port_num"} = (getservbyname('echo', 'udp'))[2] ||
93             croak("Can't get udp echo port by name");
94         $self->{"fh"} = FileHandle->new();
95         socket($self->{"fh"}, &PF_INET(), &SOCK_DGRAM(),
96                $self->{"proto_num"}) ||
97             croak("udp socket error - $!");
98     }
99     elsif ($self->{"proto"} eq "icmp")
100     {
101         croak("icmp ping requires root privilege") if $>;
102         $self->{"proto_num"} = (getprotobyname('icmp'))[2] ||
103                     croak("Can't get icmp protocol by name");
104         $self->{"pid"} = $$ & 0xffff;           # Save lower 16 bits of pid
105         $self->{"fh"} = FileHandle->new();
106         socket($self->{"fh"}, &PF_INET(), &SOCK_RAW(), $self->{"proto_num"}) ||
107             croak("icmp socket error - $!");
108     }
109     elsif ($self->{"proto"} eq "tcp")           # Just a file handle for now
110     {
111         $self->{"proto_num"} = (getprotobyname('tcp'))[2] ||
112             croak("Can't get tcp protocol by name");
113         $self->{"port_num"} = (getservbyname('echo', 'tcp'))[2] ||
114             croak("Can't get tcp echo port by name");
115         $self->{"fh"} = FileHandle->new();
116     }
117
118
119     return($self);
120 }
121
122 # Description: Ping a host name or IP number with an optional timeout.
123 # First lookup the host, and return undef if it is not found.  Otherwise
124 # perform the specific ping method based on the protocol.  Return the
125 # result of the ping.
126
127 sub ping
128 {
129     my ($self,
130         $host,              # Name or IP number of host to ping
131         $timeout            # Seconds after which ping times out
132         ) = @_;
133     my ($ip,                # Packed IP number of $host
134         $ret                # The return value
135         );
136
137     croak("Usage: \$p->ping(\$host [, \$timeout])") unless @_ == 2 || @_ == 3;
138     $timeout = $self->{"timeout"} unless $timeout;
139     croak("Timeout must be greater than 0 seconds") if $timeout <= 0;
140
141     $ip = inet_aton($host);
142     return(undef) unless defined($ip);      # Does host exist?
143
144     if ($self->{"proto"} eq "udp")
145     {
146         $ret = $self->ping_udp($ip, $timeout);
147     }
148     elsif ($self->{"proto"} eq "icmp")
149     {
150         $ret = $self->ping_icmp($ip, $timeout);
151     }
152     elsif ($self->{"proto"} eq "tcp")
153     {
154         $ret = $self->ping_tcp($ip, $timeout);
155     }
156     else
157     {
158         croak("Unknown protocol \"$self->{proto}\" in ping()");
159     }
160     return($ret);
161 }
162
163 sub ping_icmp
164 {
165     my ($self,
166         $ip,                # Packed IP number of the host
167         $timeout            # Seconds after which ping times out
168         ) = @_;
169
170     my $ICMP_ECHOREPLY = 0; # ICMP packet types
171     my $ICMP_ECHO = 8;
172     my $icmp_struct = "C2 S3 A";  # Structure of a minimal ICMP packet
173     my $subcode = 0;        # No ICMP subcode for ECHO and ECHOREPLY
174     my $flags = 0;          # No special flags when opening a socket
175     my $port = 0;           # No port with ICMP
176
177     my ($saddr,             # sockaddr_in with port and ip
178         $checksum,          # Checksum of ICMP packet
179         $msg,               # ICMP packet to send
180         $len_msg,           # Length of $msg
181         $rbits,             # Read bits, filehandles for reading
182         $nfound,            # Number of ready filehandles found
183         $finish_time,       # Time ping should be finished
184         $done,              # set to 1 when we are done
185         $ret,               # Return value
186         $recv_msg,          # Received message including IP header
187         $from_saddr,        # sockaddr_in of sender
188         $from_port,         # Port packet was sent from
189         $from_ip,           # Packed IP of sender
190         $from_type,         # ICMP type
191         $from_subcode,      # ICMP subcode
192         $from_chk,          # ICMP packet checksum
193         $from_pid,          # ICMP packet id
194         $from_seq,          # ICMP packet sequence
195         $from_msg           # ICMP message
196         );
197
198     $self->{"seq"} = ($self->{"seq"} + 1) % 65536; # Increment sequence
199     $checksum = 0;                          # No checksum for starters
200     $msg = pack($icmp_struct . $self->{"data_size"}, $ICMP_ECHO, $subcode,
201                 $checksum, $self->{"pid"}, $self->{"seq"}, $self->{"data"});
202     $checksum = Net::Ping->checksum($msg);
203     $msg = pack($icmp_struct . $self->{"data_size"}, $ICMP_ECHO, $subcode,
204                 $checksum, $self->{"pid"}, $self->{"seq"}, $self->{"data"});
205     $len_msg = length($msg);
206     $saddr = sockaddr_in($port, $ip);
207     send($self->{"fh"}, $msg, $flags, $saddr); # Send the message
208
209     $rbits = "";
210     vec($rbits, $self->{"fh"}->fileno(), 1) = 1;
211     $ret = 0;
212     $done = 0;
213     $finish_time = time() + $timeout;       # Must be done by this time
214     while (!$done && $timeout > 0)          # Keep trying if we have time
215     {
216         $nfound = select($rbits, undef, undef, $timeout); # Wait for packet
217         $timeout = $finish_time - time();   # Get remaining time
218         if (!defined($nfound))              # Hmm, a strange error
219         {
220             $ret = undef;
221             $done = 1;
222         }
223         elsif ($nfound)                     # Got a packet from somewhere
224         {
225             $recv_msg = "";
226             $from_saddr = recv($self->{"fh"}, $recv_msg, 1500, $flags);
227             ($from_port, $from_ip) = sockaddr_in($from_saddr);
228             ($from_type, $from_subcode, $from_chk,
229              $from_pid, $from_seq, $from_msg) =
230                 unpack($icmp_struct . $self->{"data_size"},
231                        substr($recv_msg, length($recv_msg) - $len_msg,
232                               $len_msg));
233             if (($from_type == $ICMP_ECHOREPLY) &&
234                 ($from_ip eq $ip) &&
235                 ($from_pid == $self->{"pid"}) && # Does the packet check out?
236                 ($from_seq == $self->{"seq"}))
237             {
238                 $ret = 1;                   # It's a winner
239                 $done = 1;
240             }
241         }
242         else                                # Oops, timed out
243         {
244             $done = 1;
245         }
246     }
247     return($ret)
248 }
249
250 # Description:  Do a checksum on the message.  Basically sum all of
251 # the short words and fold the high order bits into the low order bits.
252
253 sub checksum
254 {
255     my ($class,
256         $msg            # The message to checksum
257         ) = @_;
258     my ($len_msg,       # Length of the message
259         $num_short,     # The number of short words in the message
260         $short,         # One short word
261         $chk            # The checksum
262         );
263
264     $len_msg = length($msg);
265     $num_short = $len_msg / 2;
266     $chk = 0;
267     foreach $short (unpack("S$num_short", $msg))
268     {
269         $chk += $short;
270     }                                           # Add the odd byte in
271     $chk += unpack("C", substr($msg, $len_msg - 1, 1)) if $len_msg % 2;
272     $chk = ($chk >> 16) + ($chk & 0xffff);      # Fold high into low
273     return(~(($chk >> 16) + $chk) & 0xffff);    # Again and complement
274 }
275
276 # Description:  Perform a tcp echo ping.  Since a tcp connection is
277 # host specific, we have to open and close each connection here.  We
278 # can't just leave a socket open.  Because of the robust nature of
279 # tcp, it will take a while before it gives up trying to establish a
280 # connection.  Therefore, we have to set the alarm to break out of the
281 # connection sooner if the timeout expires.  No data bytes are actually
282 # sent since the successful establishment of a connection is proof
283 # enough of the reachability of the remote host.  Also, tcp is
284 # expensive and doesn't need our help to add to the overhead.
285
286 sub ping_tcp
287 {
288     my ($self,
289         $ip,                # Packed IP number of the host
290         $timeout            # Seconds after which ping times out
291         ) = @_;
292     my ($saddr,             # sockaddr_in with port and ip
293         $ret                # The return value
294         );
295
296     socket($self->{"fh"}, &PF_INET(), &SOCK_STREAM(), $self->{"proto_num"}) ||
297         croak("tcp socket error - $!");
298     $saddr = sockaddr_in($self->{"port_num"}, $ip);
299
300     $SIG{'ALRM'} = sub { die };
301     alarm($timeout);        # Interrupt connect() if we have to
302
303     $ret = 0;               # Default to unreachable
304     eval {
305         die $! unless connect($self->{"fh"}, $saddr);
306         $ret = 1;
307     };
308     alarm(0);
309     $ret = 1 if $@ =~ /connection refused/i;
310     $self->{"fh"}->close();
311     return($ret);
312 }
313
314 # Description:  Perform a udp echo ping.  Construct a message of
315 # at least the one-byte sequence number and any additional data bytes.
316 # Send the message out and wait for a message to come back.  If we
317 # get a message, make sure all of its parts match.  If they do, we are
318 # done.  Otherwise go back and wait for the message until we run out
319 # of time.  Return the result of our efforts.
320
321 sub ping_udp
322 {
323     my ($self,
324         $ip,                # Packed IP number of the host
325         $timeout            # Seconds after which ping times out
326         ) = @_;
327
328     my $flags = 0;          # Nothing special on open
329
330     my ($saddr,             # sockaddr_in with port and ip
331         $ret,               # The return value
332         $msg,               # Message to be echoed
333         $finish_time,       # Time ping should be finished
334         $done,              # Set to 1 when we are done pinging
335         $rbits,             # Read bits, filehandles for reading
336         $nfound,            # Number of ready filehandles found
337         $from_saddr,        # sockaddr_in of sender
338         $from_msg,          # Characters echoed by $host
339         $from_port,         # Port message was echoed from
340         $from_ip            # Packed IP number of sender
341         );
342
343     $saddr = sockaddr_in($self->{"port_num"}, $ip);
344     $self->{"seq"} = ($self->{"seq"} + 1) % 256;    # Increment sequence
345     $msg = chr($self->{"seq"}) . $self->{"data"};   # Add data if any
346     send($self->{"fh"}, $msg, $flags, $saddr);      # Send it
347
348     $rbits = "";
349     vec($rbits, $self->{"fh"}->fileno(), 1) = 1;
350     $ret = 0;                   # Default to unreachable
351     $done = 0;
352     $finish_time = time() + $timeout;       # Ping needs to be done by then
353     while (!$done && $timeout > 0)
354     {
355         $nfound = select($rbits, undef, undef, $timeout); # Wait for response
356         $timeout = $finish_time - time();   # Get remaining time
357
358         if (!defined($nfound))  # Hmm, a strange error
359         {
360             $ret = undef;
361             $done = 1;
362         }
363         elsif ($nfound)         # A packet is waiting
364         {
365             $from_msg = "";
366             $from_saddr = recv($self->{"fh"}, $from_msg, 1500, $flags);
367             ($from_port, $from_ip) = sockaddr_in($from_saddr);
368             if (($from_ip eq $ip) &&        # Does the packet check out?
369                 ($from_port == $self->{"port_num"}) &&
370                 ($from_msg eq $msg))
371             {
372                 $ret = 1;       # It's a winner
373                 $done = 1;
374             }
375         }
376         else                    # Oops, timed out
377         {
378             $done = 1;
379         }
380     }
381     return($ret);
382 }
383
384 # Description:  Close the connection unless we are using the tcp
385 # protocol, since it will already be closed.
386
387 sub close
388 {
389     my ($self) = @_;
390
391     $self->{"fh"}->close() unless $self->{"proto"} eq "tcp";
392 }
393
394
395 1;
396 __END__
397
398 =head1 NAME
399
400 Net::Ping - check a remote host for reachability
401
402 $Id: Ping.pm,v 1.5 2001/11/19 09:44:18 rob Exp $
403
404 =head1 SYNOPSIS
405
406     use Net::Ping;
407
408     $p = Net::Ping->new();
409     print "$host is alive.\n" if $p->ping($host);
410     $p->close();
411
412     $p = Net::Ping->new("icmp");
413     foreach $host (@host_array)
414     {
415         print "$host is ";
416         print "NOT " unless $p->ping($host, 2);
417         print "reachable.\n";
418         sleep(1);
419     }
420     $p->close();
421
422     $p = Net::Ping->new("tcp", 2);
423     # Try connecting to the www port instead of the echo port
424     $p->{port_num} = getservbyname("http", "tcp");
425     while ($stop_time > time())
426     {
427         print "$host not reachable ", scalar(localtime()), "\n"
428             unless $p->ping($host);
429         sleep(300);
430     }
431     undef($p);
432
433     # For backward compatibility
434     print "$host is alive.\n" if pingecho($host);
435
436 =head1 DESCRIPTION
437
438 This module contains methods to test the reachability of remote
439 hosts on a network.  A ping object is first created with optional
440 parameters, a variable number of hosts may be pinged multiple
441 times and then the connection is closed.
442
443 You may choose one of three different protocols to use for the ping.
444 With the "tcp" protocol the ping() method attempts to establish a
445 connection to the remote host's echo port.  If the connection is
446 successfully established, the remote host is considered reachable.  No
447 data is actually echoed.  This protocol does not require any special
448 privileges but has higher overhead than the other two protocols.
449
450 Specifying the "udp" protocol causes the ping() method to send a udp
451 packet to the remote host's echo port.  If the echoed packet is
452 received from the remote host and the received packet contains the
453 same data as the packet that was sent, the remote host is considered
454 reachable.  This protocol does not require any special privileges.
455
456 If the "icmp" protocol is specified, the ping() method sends an icmp
457 echo message to the remote host, which is what the UNIX ping program
458 does.  If the echoed message is received from the remote host and
459 the echoed information is correct, the remote host is considered
460 reachable.  Specifying the "icmp" protocol requires that the program
461 be run as root or that the program be setuid to root.
462
463 =head2 Functions
464
465 =over 4
466
467 =item Net::Ping->new([$proto [, $def_timeout [, $bytes]]]);
468
469 Create a new ping object.  All of the parameters are optional.  $proto
470 specifies the protocol to use when doing a ping.  The current choices
471 are "tcp", "udp" or "icmp".  The default is "udp".
472
473 If a default timeout ($def_timeout) in seconds is provided, it is used
474 when a timeout is not given to the ping() method (below).  The timeout
475 must be greater than 0 and the default, if not specified, is 5 seconds.
476
477 If the number of data bytes ($bytes) is given, that many data bytes
478 are included in the ping packet sent to the remote host. The number of
479 data bytes is ignored if the protocol is "tcp".  The minimum (and
480 default) number of data bytes is 1 if the protocol is "udp" and 0
481 otherwise.  The maximum number of data bytes that can be specified is
482 1024.
483
484 =item $p->ping($host [, $timeout]);
485
486 Ping the remote host and wait for a response.  $host can be either the
487 hostname or the IP number of the remote host.  The optional timeout
488 must be greater than 0 seconds and defaults to whatever was specified
489 when the ping object was created.  If the hostname cannot be found or
490 there is a problem with the IP number, undef is returned.  Otherwise,
491 1 is returned if the host is reachable and 0 if it is not.  For all
492 practical purposes, undef and 0 and can be treated as the same case.
493
494 =item $p->close();
495
496 Close the network connection for this ping object.  The network
497 connection is also closed by "undef $p".  The network connection is
498 automatically closed if the ping object goes out of scope (e.g. $p is
499 local to a subroutine and you leave the subroutine).
500
501 =item pingecho($host [, $timeout]);
502
503 To provide backward compatibility with the previous version of
504 Net::Ping, a pingecho() subroutine is available with the same
505 functionality as before.  pingecho() uses the tcp protocol.  The
506 return values and parameters are the same as described for the ping()
507 method.  This subroutine is obsolete and may be removed in a future
508 version of Net::Ping.
509
510 =back
511
512 =head1 WARNING
513
514 pingecho() or a ping object with the tcp protocol use alarm() to
515 implement the timeout.  So, don't use alarm() in your program while
516 you are using pingecho() or a ping object with the tcp protocol.  The
517 udp and icmp protocols do not use alarm() to implement the timeout.
518
519 =head1 NOTES
520
521 There will be less network overhead (and some efficiency in your
522 program) if you specify either the udp or the icmp protocol.  The tcp
523 protocol will generate 2.5 times or more traffic for each ping than
524 either udp or icmp.  If many hosts are pinged frequently, you may wish
525 to implement a small wait (e.g. 25ms or more) between each ping to
526 avoid flooding your network with packets.
527
528 The icmp protocol requires that the program be run as root or that it
529 be setuid to root.  The tcp and udp protocols do not require special
530 privileges, but not all network devices implement the echo protocol
531 for tcp or udp.
532
533 Local hosts should normally respond to pings within milliseconds.
534 However, on a very congested network it may take up to 3 seconds or
535 longer to receive an echo packet from the remote host.  If the timeout
536 is set too low under these conditions, it will appear that the remote
537 host is not reachable (which is almost the truth).
538
539 Reachability doesn't necessarily mean that the remote host is actually
540 functioning beyond its ability to echo packets.
541
542 Because of a lack of anything better, this module uses its own
543 routines to pack and unpack ICMP packets.  It would be better for a
544 separate module to be written which understands all of the different
545 kinds of ICMP packets.
546
547 =head1 AUTHOR(S)
548
549   Original pingecho():
550     Andreas Karrer (karrer@bernina.ethz.ch)
551     Paul Marquess (pmarquess@bfsec.bt.co.uk)
552
553   Net::Ping base code:
554     Russell Mosemann (mose@ns.ccsn.edu)
555
556   Compatibility porting so ping_tcp()
557   can work with most remote systems:
558     Rob Brown (rob@roobik.com)
559
560 =head1 COPYRIGHT
561
562 Copyright (c) 2001 Rob Brown. All rights reserved.
563
564 Copyright (c) 1996 Russell Mosemann.  All rights reserved.
565
566 This program is free software; you may redistribute it and/or
567 modify it under the same terms as Perl itself.
568
569 =cut