class Net::Ping::ICMP
The Net::Ping::ICMP class encapsulates an icmp ping.
Constants
- ICMP_ECHO
- ICMP_ECHOREPLY
- ICMP_SUBCODE
Attributes
Returns the data size, i.e. number of bytes sent on the ping. The default size is 56.
Public Class Methods
Creates and returns a new Ping::ICMP object. This is similar to its superclass constructor, but must be created with root privileges (on UNIX systems), and the port value is ignored.
# File lib/net/ping/icmp.rb, line 30 def initialize(host=nil, port=nil, timeout=5) raise 'requires root privileges' if Process.euid > 0 if File::ALT_SEPARATOR unless Win32::Security.elevated_security? raise 'requires elevated security' end end @seq = 0 @bind_port = 0 @bind_host = nil @data_size = 56 @data = '' 0.upto(@data_size){ |n| @data << (n % 256).chr } @ping_id = (Thread.current.object_id ^ Process.pid) & 0xffff super(host, port, timeout) @port = nil # This value is not used in ICMP pings. end
Public Instance Methods
Associates the local end of the socket connection with the given
host and port. The default port is 0.
# File lib/net/ping/icmp.rb, line 64 def bind(host, port = 0) @bind_host = host @bind_port = port end
Sets the number of bytes sent in the ping method.
# File lib/net/ping/icmp.rb, line 55 def data_size=(size) @data_size = size @data = '' 0.upto(size){ |n| @data << (n % 256).chr } end
Pings the host specified in this method or in the constructor.
If a host was not specified either here or in the constructor, an
ArgumentError is raised.
# File lib/net/ping/icmp.rb, line 73 def ping(host = @host) super(host) bool = false socket = Socket.new( Socket::PF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP ) if @bind_host saddr = Socket.pack_sockaddr_in(@bind_port, @bind_host) socket.bind(saddr) end @seq = (@seq + 1) % 65536 pstring = 'C2 n3 A' << @data_size.to_s timeout = @timeout checksum = 0 msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @ping_id, @seq, @data].pack(pstring) checksum = checksum(msg) msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @ping_id, @seq, @data].pack(pstring) begin saddr = Socket.pack_sockaddr_in(0, host) rescue Exception socket.close unless socket.closed? return bool end start_time = Time.now socket.send(msg, 0, saddr) # Send the message begin Timeout.timeout(@timeout){ while true io_array = select([socket], nil, nil, timeout) if io_array.nil? || io_array[0].empty? @exception = "timeout" if io_array.nil? return false end ping_id = nil seq = nil data = socket.recvfrom(1500).first type = data[20, 2].unpack('C2').first case type when ICMP_ECHOREPLY if data.length >= 28 ping_id, seq = data[24, 4].unpack('n3') end else if data.length > 56 ping_id, seq = data[52, 4].unpack('n3') end end if ping_id == @ping_id && seq == @seq && type == ICMP_ECHOREPLY bool = true break end end } rescue Exception => err @exception = err ensure socket.close if socket end # There is no duration if the ping failed @duration = Time.now - start_time if bool return bool end
Private Instance Methods
Perform a checksum on the message. This is the sum of all the short words and it folds the high order bits into the low order bits.
# File lib/net/ping/icmp.rb, line 162 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').first << 8 end check = (check >> 16) + (check & 0xffff) return (~((check >> 16) + check) & 0xffff) end