📄 ping.pm
字号:
package Net::Ping;# Author: mose@ccsn.edu (Russell Mosemann)## Authors of the original pingecho():# karrer@bernina.ethz.ch (Andreas Karrer)# Paul.Marquess@btinternet.com (Paul Marquess)## Copyright (c) 1996 Russell Mosemann. All rights reserved. This# program is free software; you may redistribute it and/or modify it# under the same terms as Perl itself.use 5.005_64;require Exporter;use strict;our(@ISA, @EXPORT, $VERSION, $def_timeout, $def_proto, $max_datasize);use FileHandle;use Socket qw( SOCK_DGRAM SOCK_STREAM SOCK_RAW PF_INET inet_aton sockaddr_in );use Carp;@ISA = qw(Exporter);@EXPORT = qw(pingecho);$VERSION = 2.02;# Constants$def_timeout = 5; # Default timeout to wait for a reply$def_proto = "udp"; # Default protocol to use for pinging$max_datasize = 1024; # Maximum data bytes in a packet# Description: The pingecho() subroutine is provided for backward# compatibility with the original Net::Ping. It accepts a host# name/IP and an optional timeout in seconds. Create a tcp ping# object and try pinging the host. The result of the ping is returned.sub pingecho{ my ($host, # Name or IP number of host to ping $timeout # Optional timeout in seconds ) = @_; my ($p); # A ping object $p = Net::Ping->new("tcp", $timeout); $p->ping($host); # Going out of scope closes the connection}# Description: The new() method creates a new ping object. Optional# parameters may be specified for the protocol to use, the timeout in# seconds and the size in bytes of additional data which should be# included in the packet.# After the optional parameters are checked, the data is constructed# and a socket is opened if appropriate. The object is returned.sub new{ my ($this, $proto, # Optional protocol to use for pinging $timeout, # Optional timeout in seconds $data_size # Optional additional bytes of data ) = @_; my $class = ref($this) || $this; my $self = {}; my ($cnt, # Count through data bytes $min_datasize # Minimum data bytes required ); bless($self, $class); $proto = $def_proto unless $proto; # Determine the protocol croak("Protocol for ping must be \"tcp\", \"udp\" or \"icmp\"") unless $proto =~ m/^(tcp|udp|icmp)$/; $self->{"proto"} = $proto; $timeout = $def_timeout unless $timeout; # Determine the timeout croak("Default timeout for ping must be greater than 0 seconds") if $timeout <= 0; $self->{"timeout"} = $timeout; $min_datasize = ($proto eq "udp") ? 1 : 0; # Determine data size $data_size = $min_datasize unless defined($data_size) && $proto ne "tcp"; croak("Data for ping must be from $min_datasize to $max_datasize bytes") if ($data_size < $min_datasize) || ($data_size > $max_datasize); $data_size-- if $self->{"proto"} eq "udp"; # We provide the first byte $self->{"data_size"} = $data_size; $self->{"data"} = ""; # Construct data bytes for ($cnt = 0; $cnt < $self->{"data_size"}; $cnt++) { $self->{"data"} .= chr($cnt % 256); } $self->{"seq"} = 0; # For counting packets if ($self->{"proto"} eq "udp") # Open a socket { $self->{"proto_num"} = (getprotobyname('udp'))[2] || croak("Can't udp protocol by name"); $self->{"port_num"} = (getservbyname('echo', 'udp'))[2] || croak("Can't get udp echo port by name"); $self->{"fh"} = FileHandle->new(); socket($self->{"fh"}, &PF_INET(), &SOCK_DGRAM(), $self->{"proto_num"}) || croak("udp socket error - $!"); } elsif ($self->{"proto"} eq "icmp") { croak("icmp ping requires root privilege") if ($> and $^O ne 'VMS'); $self->{"proto_num"} = (getprotobyname('icmp'))[2] || croak("Can't get icmp protocol by name"); $self->{"pid"} = $$ & 0xffff; # Save lower 16 bits of pid $self->{"fh"} = FileHandle->new(); socket($self->{"fh"}, &PF_INET(), &SOCK_RAW(), $self->{"proto_num"}) || croak("icmp socket error - $!"); } elsif ($self->{"proto"} eq "tcp") # Just a file handle for now { $self->{"proto_num"} = (getprotobyname('tcp'))[2] || croak("Can't get tcp protocol by name"); $self->{"port_num"} = (getservbyname('echo', 'tcp'))[2] || croak("Can't get tcp echo port by name"); $self->{"fh"} = FileHandle->new(); } return($self);}# Description: Ping a host name or IP number with an optional timeout.# First lookup the host, and return undef if it is not found. Otherwise# perform the specific ping method based on the protocol. Return the # result of the ping.sub ping{ my ($self, $host, # Name or IP number of host to ping $timeout # Seconds after which ping times out ) = @_; my ($ip, # Packed IP number of $host $ret # The return value ); croak("Usage: \$p->ping(\$host [, \$timeout])") unless @_ == 2 || @_ == 3; $timeout = $self->{"timeout"} unless $timeout; croak("Timeout must be greater than 0 seconds") if $timeout <= 0; $ip = inet_aton($host); return(undef) unless defined($ip); # Does host exist? if ($self->{"proto"} eq "udp") { $ret = $self->ping_udp($ip, $timeout); } elsif ($self->{"proto"} eq "icmp") { $ret = $self->ping_icmp($ip, $timeout); } elsif ($self->{"proto"} eq "tcp") { $ret = $self->ping_tcp($ip, $timeout); } else { croak("Unknown protocol \"$self->{proto}\" in ping()"); } return($ret);}sub ping_icmp{ my ($self, $ip, # Packed IP number of the host $timeout # Seconds after which ping times out ) = @_; my $ICMP_ECHOREPLY = 0; # ICMP packet types my $ICMP_ECHO = 8; my $icmp_struct = "C2 S3 A"; # Structure of a minimal ICMP packet my $subcode = 0; # No ICMP subcode for ECHO and ECHOREPLY my $flags = 0; # No special flags when opening a socket my $port = 0; # No port with ICMP my ($saddr, # sockaddr_in with port and ip $checksum, # Checksum of ICMP packet $msg, # ICMP packet to send $len_msg, # Length of $msg $rbits, # Read bits, filehandles for reading $nfound, # Number of ready filehandles found $finish_time, # Time ping should be finished $done, # set to 1 when we are done $ret, # Return value $recv_msg, # Received message including IP header $from_saddr, # sockaddr_in of sender $from_port, # Port packet was sent from $from_ip, # Packed IP of sender $from_type, # ICMP type $from_subcode, # ICMP subcode $from_chk, # ICMP packet checksum $from_pid, # ICMP packet id $from_seq, # ICMP packet sequence $from_msg # ICMP message ); $self->{"seq"} = ($self->{"seq"} + 1) % 65536; # Increment sequence $checksum = 0; # No checksum for starters $msg = pack($icmp_struct . $self->{"data_size"}, $ICMP_ECHO, $subcode, $checksum, $self->{"pid"}, $self->{"seq"}, $self->{"data"}); $checksum = Net::Ping->checksum($msg); $msg = pack($icmp_struct . $self->{"data_size"}, $ICMP_ECHO, $subcode, $checksum, $self->{"pid"}, $self->{"seq"}, $self->{"data"}); $len_msg = length($msg); $saddr = sockaddr_in($port, $ip); send($self->{"fh"}, $msg, $flags, $saddr); # Send the message $rbits = ""; vec($rbits, $self->{"fh"}->fileno(), 1) = 1; $ret = 0; $done = 0; $finish_time = time() + $timeout; # Must be done by this time while (!$done && $timeout > 0) # Keep trying if we have time { $nfound = select($rbits, undef, undef, $timeout); # Wait for packet $timeout = $finish_time - time(); # Get remaining time if (!defined($nfound)) # Hmm, a strange error { $ret = undef; $done = 1; } elsif ($nfound) # Got a packet from somewhere { $recv_msg = ""; $from_saddr = recv($self->{"fh"}, $recv_msg, 1500, $flags); ($from_port, $from_ip) = sockaddr_in($from_saddr); ($from_type, $from_subcode, $from_chk, $from_pid, $from_seq, $from_msg) = unpack($icmp_struct . $self->{"data_size"}, substr($recv_msg, length($recv_msg) - $len_msg, $len_msg)); if (($from_type == $ICMP_ECHOREPLY) && ($from_ip eq $ip) && ($from_pid == $self->{"pid"}) && # Does the packet check out? ($from_seq == $self->{"seq"})) { $ret = 1; # It's a winner $done = 1; } } else # Oops, timed out { $done = 1; } } return($ret)}# Description: Do a checksum on the message. Basically sum all of# the short words and fold the high order bits into the low order bits.sub checksum{ my ($class, $msg # The message to checksum ) = @_; my ($len_msg, # Length of the message $num_short, # The number of short words in the message $short, # One short word $chk # The checksum ); $len_msg = length($msg); $num_short = int($len_msg / 2); $chk = 0; foreach $short (unpack("S$num_short", $msg)) { $chk += $short; } # Add the odd byte in $chk += (unpack("C", substr($msg, $len_msg - 1, 1)) << 8) if $len_msg % 2; $chk = ($chk >> 16) + ($chk & 0xffff); # Fold high into low return(~(($chk >> 16) + $chk) & 0xffff); # Again and complement
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -