From: Daniel Berger Date: 2006-12-02T04:54:49+09:00 Subject: Re: cidr.rb: port of Perl's Net::CIDR v0.11 available Jos Backus wrote: > On Fri, Dec 01, 2006 at 04:47:05AM +0900, Daniel Berger wrote: >> 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::PF_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::Ping::ICMP.new > p p.ping(ARGV.shift, 5) Seems to work. Mind if I include with the next release of net-ping (with acknowledgements, of course). The only tweak I would make are to have it return true or false, instead of 1 or nil. Regards, Dan