Well, you could start by porting the ping_icmp() function from Ping.pm
to Ruby. I started working on it but I got stuck and I need to work on
other things. Below is what I've got so far:
I've made some progress; see attached. Let me know what you think.
------------------------------------------------------------------------
#!/usr/bin/env ruby
require 'rubygems'
require 'net/ping'
module Net
class Ping::ICMP < Ping
ICMP_ECHOREPLY = 0
ICMP_ECHO = 8
ICMP_STRUCT = 'C2 n3 A'
ICMP_SUBCODE = 0
ICMP_FLAGS = 0
ICMP_PORT = 0
def initialize(*args)
super(args)
raise 'requires root privileges' if Process.euid > 0 # unsure
@seq = 0
@data_size = 0
@data = ''
@pid = Process.pid & 0xffff
end
def checksum(msg)
length = msg.length
num_short = length / 2
check = 0
msg.unpack("n#{num_short}").each do |short|
check += short
end
if length % 2 > 0
check += msg[length-1, 1].unpack('C') << 8
end
check = (check >> 16) + (check & 0xffff)
return (~((check >> 16) + check) & 0xffff)
end
def ping(ip, timeout)
ret = nil
socket = Socket.new(
Socket:
F_INET,
Socket::SOCK_RAW,
Socket::IPPROTO_ICMP
)
@seq = (@seq + 1) % 65536
pstring = 'C2 n3 A' << @data_size.to_s
checksum = 0
msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)
checksum = checksum(msg)
msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)
begin
saddr = Socket.pack_sockaddr_in(ICMP_PORT, ip)
rescue => exc
return nil
end
@from_ip = @from_type = @from_subcode = nil
socket.send(msg, ICMP_FLAGS, saddr) # Send the message
done = false
finish_time = Time.now + timeout
while !done and timeout > 0
nfound = select([socket], nil, nil, timeout)
timeout = finish_time - Time.now
if nfound.nil? # timed out
ret = nil
done = true
elsif !nfound[0].empty?
recv_msg = ''
from_pid = -1
from_seq = -1
recv_msg, from_saddr = socket.recvfrom(1500, ICMP_FLAGS)
from_port, from_ip = Socket.unpack_sockaddr_in(from_saddr)
from_type, from_subcode = recv_msg[20, 2].unpack('C2')
case from_type
when ICMP_ECHOREPLY
if recv_msg.length >= 28
from_pid, from_seq = recv_msg[24, 4].unpack('n3')
end
else
if recv_msg.length >= 56
from_pid, from_seq = recv_msg[52, 4].unpack('n3')
end
end
@from_ip = from_ip
@from_type = from_type
@from_subcode = from_subcode
if from_pid == @pid and from_seq == @seq
if from_type == ICMP_ECHOREPLY
ret = 1
end
done = true
end
else
# Shouldn't happen
done = true
end
end
return ret
end
end
end
p = Net:
ing::ICMP.new
p p.ping(ARGV.shift, 5)