From: Jos Backus Date: 2006-12-02T04:40:55+09:00 Subject: Re: cidr.rb: port of Perl's Net::CIDR v0.11 available --sm4nu43k4a2Rpi4c Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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. -- Jos Backus jos at catnook.com --sm4nu43k4a2Rpi4c Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ping_icmp.rb" #!/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) --sm4nu43k4a2Rpi4c--