class Net::Ping::ICMP

The Net::Ping::ICMP class encapsulates an icmp ping.

Constants

ICMP_ECHO
ICMP_ECHOREPLY
ICMP_SUBCODE

Attributes

data_size[R]

Returns the data size, i.e. number of bytes sent on the ping. The default size is 56.

Public Class Methods

new(host=nil, port=nil, timeout=5) click to toggle source

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.

Calls superclass method 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

bind(host, port = 0) click to toggle source

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
data_size=(size) click to toggle source

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
ping(host = @host) click to toggle source

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.

Calls superclass method 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

checksum(msg) click to toggle source

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