Class: Net::Ping::ICMP

Inherits:
Net::Ping show all
Defined in:
lib/net/ping/icmp.rb

Overview

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

Constant Summary

ICMP_ECHOREPLY =
0
ICMP_ECHO =

Echo reply Echo request

8
ICMP_SUBCODE =
0

Constants inherited from Net::Ping

VERSION

Instance Attribute Summary (collapse)

Attributes inherited from Net::Ping

#duration, #exception, #host, #port, #timeout, #warning

Instance Method Summary (collapse)

Constructor Details

- (ICMP) initialize(host = nil, port = nil, timeout = 5)

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.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/net/ping/icmp.rb', line 32

def initialize(host=nil, port=nil, timeout=5)
  raise 'requires root privileges' if Process.euid > 0

  if File::ALT_SEPARATOR && windows_version >= 6
    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 }

  @pid  = Process.pid & 0xffff

  super(host, port, timeout)
  @port = nil # This value is not used in ICMP pings.
end

Instance Attribute Details

- (Object) data_size

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



26
27
28
# File 'lib/net/ping/icmp.rb', line 26

def data_size
  @data_size
end

Instance Method Details

- (Object) bind(host, port = 0)

Associates the local end of the socket connection with the given host and port. The default port is 0.



66
67
68
69
# File 'lib/net/ping/icmp.rb', line 66

def bind(host, port = 0)
  @bind_host = host
  @bind_port = port
end

- (Object) ping(host = @host) Also known as: ping?, pingecho

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.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/net/ping/icmp.rb', line 75

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, @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(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?
          return false
        end

        pid = nil
        seq = nil

        data = socket.recvfrom(1500).first
        type = data[20, 2].unpack('C2').first

        case type
          when ICMP_ECHOREPLY
            if data.length >= 28
              pid, seq = data[24, 4].unpack('n3')
            end
          else
            if data.length > 56
              pid, seq = data[52, 4].unpack('n3')
            end
        end

        if pid == @pid && 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