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.
Net::Ping::new
# File lib/net/ping/icmp.rb, line 30 def initialize(host=nil, port=nil, timeout=5) begin # If we have cap2, but not are root, or have net_raw, raise an error require 'cap2' current_process = Cap2.process unless Process.euid == 0 \ || current_process.permitted?(:net_raw) \ && current_process.enabled?(:net_raw) raise StandardError, 'requires root privileges or setcap net_raw' end rescue LoadError # Without cap2, raise error if we are not root unless Process.euid == 0 raise StandardError, 'requires root privileges or setcap net_raw' end end 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 78 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 69 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.
Net::Ping#ping
# File lib/net/ping/icmp.rb, line 87 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? raise Timeout::Error 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 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 171 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