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