mirror of
https://github.com/webmin/webmin.git
synced 2025-07-21 23:40:34 +00:00
225 lines
6.8 KiB
Perl
Executable File
225 lines
6.8 KiB
Perl
Executable File
# ping-monitor.pl
|
|
# Ping some host
|
|
# Contains code ripped from Net::Ping by Russell Mosemann
|
|
|
|
use Socket;
|
|
|
|
sub get_ping_status
|
|
{
|
|
my ($m) = @_;
|
|
my $wait = defined($m->{'wait'}) ? $m->{'wait'} : 5;
|
|
my $ip = &to_ipaddress($m->{'host'}) ||
|
|
&to_ip6address($m->{'host'});
|
|
return { 'up' => 0 } if (!$ip);
|
|
my $ipv6 = &check_ip6address($m->{'host'}) ||
|
|
&to_ip6address($m->{'host'}) &&
|
|
!&to_ipaddress($m->{'host'});
|
|
if ($config{'pinger'} || $ipv6) {
|
|
# Call a ping command if configured, or if using IPv6 since the built-
|
|
# in code doesn't support it yet
|
|
my $cmd;
|
|
my $auto_pinger = $config{'pinger'} eq "linux" || !$config{'pinger'};
|
|
if ($auto_pinger && $gconfig{'os_type'} =~ /-linux$/) {
|
|
# Use linux command
|
|
$cmd = ($ipv6 ? "ping6" : "ping")." -c 1 -w $wait";
|
|
}
|
|
elsif ($auto_pinger && $gconfig{'os_type'} eq 'freebsd') {
|
|
# Use FreeBSD command
|
|
$cmd = ($ipv6 ? "ping6" : "ping")." -c 1 -W ".($wait * 1000);
|
|
}
|
|
elsif ($auto_pinger) {
|
|
# Don't know command for this OS
|
|
return { 'up' => - 1 };
|
|
}
|
|
else {
|
|
$cmd = $config{'pinger'};
|
|
}
|
|
my $rv;
|
|
my $out;
|
|
eval {
|
|
local $sig{'ALRM'} = sub { die "timeout" };
|
|
alarm($wait + 1);
|
|
$out = &backquote_command("$cmd ".quotemeta($m->{'host'}).
|
|
" 2>&1 </dev/null");
|
|
$rv = $?;
|
|
alarm(0);
|
|
};
|
|
if ($@ =~ /timeout/) {
|
|
return { 'up' => 0, 'desc' => &text('ping_timeout', $wait) };
|
|
}
|
|
elsif ($@) {
|
|
return { 'up' => 0, 'desc' => "$@" };
|
|
}
|
|
elsif ($rv) {
|
|
my @l = split(/\r?\n/, $out);
|
|
return { 'up' => 0, 'desc' => &html_escape($l[$#l]) };
|
|
}
|
|
else {
|
|
return { 'up' => 1 };
|
|
}
|
|
}
|
|
else {
|
|
# Use builtin code
|
|
if (!&to_ipaddress($ip)) {
|
|
return { 'up' => 0, 'desc' => $text{'ping_resolv'} };
|
|
}
|
|
my $rv = &ping_icmp(inet_aton($ip), $wait);
|
|
return { 'up' => $rv ? 1 : 0 };
|
|
}
|
|
}
|
|
|
|
sub show_ping_dialog
|
|
{
|
|
my ($m) = @_;
|
|
print &ui_table_row($text{'ping_host'},
|
|
&ui_textbox("host", $m->{'host'}, 50), 3);
|
|
|
|
print &ui_table_row($text{'ping_wait'},
|
|
&ui_textbox("wait", defined($m->{'wait'}) ? $m->{'wait'} : 5, 6).
|
|
" ".$text{'oldfile_secs'});
|
|
}
|
|
|
|
sub parse_ping_dialog
|
|
{
|
|
my ($m) = @_;
|
|
#$config{'ping_cmd'} || &error($text{'ping_econfig'});
|
|
&to_ipaddress($in{'host'}) || &to_ip6address($in{'host'}) ||
|
|
&error($text{'ping_ehost'});
|
|
$in{'wait'} =~ /^(\d*\.)?\d+$/ || &error($text{'ping_ewait'});
|
|
$m->{'host'} = $in{'host'};
|
|
$m->{'wait'} = $in{'wait'};
|
|
}
|
|
|
|
sub ping_icmp
|
|
{
|
|
my ($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
|
|
$data,
|
|
$cnt,
|
|
$data_size
|
|
);
|
|
|
|
# Construct packet data string
|
|
$data_size = 0;
|
|
for ($cnt = 0; $cnt < $data_size; $cnt++)
|
|
{
|
|
$data .= chr($cnt % 256);
|
|
}
|
|
|
|
my $proto_num = (getprotobyname('icmp'))[2];
|
|
socket(PSOCK, PF_INET, SOCK_RAW, $proto_num);
|
|
|
|
$ping_seq = ($ping_seq + 1) % 65536; # Increment sequence
|
|
$checksum = 0; # No checksum for starters
|
|
$msg = pack($icmp_struct . $data_size, $ICMP_ECHO, $subcode,
|
|
$checksum, $$, $ping_seq, $data);
|
|
$checksum = checksum($msg);
|
|
$msg = pack($icmp_struct . $data_size, $ICMP_ECHO, $subcode,
|
|
$checksum, $$, $ping_seq, $data);
|
|
$len_msg = length($msg);
|
|
$saddr = pack_sockaddr_in($port, $ip);
|
|
send(PSOCK, $msg, $flags, $saddr); # Send the message
|
|
|
|
$rbits = "";
|
|
vec($rbits, fileno(PSOCK), 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
|
|
{
|
|
# Probably an interrupted system call, so try again
|
|
$ret = undef;
|
|
#$done = 1;
|
|
}
|
|
elsif ($nfound) # Got a packet from somewhere
|
|
{
|
|
$recv_msg = "";
|
|
$from_saddr = recv(PSOCK, $recv_msg, 1500, $flags);
|
|
if ($from_saddr) {
|
|
($from_port, $from_ip) = unpack_sockaddr_in($from_saddr);
|
|
($from_type, $from_subcode, $from_chk,
|
|
$from_pid, $from_seq, $from_msg) =
|
|
unpack($icmp_struct . $data_size,
|
|
substr($recv_msg, length($recv_msg) - $len_msg,
|
|
$len_msg));
|
|
if (($from_type == $ICMP_ECHOREPLY) &&
|
|
($from_ip eq $ip) &&
|
|
($from_pid == $$) && # Does the packet check out?
|
|
($from_seq == $ping_seq))
|
|
{
|
|
$ret = 1; # It's a winner
|
|
$done = 1;
|
|
}
|
|
} else {
|
|
# Packet not actually received
|
|
$ret = undef;
|
|
}
|
|
}
|
|
else # Oops, timed out
|
|
{
|
|
$done = 1;
|
|
}
|
|
}
|
|
close(PSOCK);
|
|
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 ($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 = $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)) if $len_msg % 2;
|
|
$chk = ($chk >> 16) + ($chk & 0xffff); # Fold high into low
|
|
return(~(($chk >> 16) + $chk) & 0xffff); # Again and complement
|
|
}
|
|
|
|
|