net-ping 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,92 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ require 'ping'
3
+
4
+ module Net
5
+ class Ping::External < Ping
6
+
7
+ # Pings the host using your system's ping utility and checks for any
8
+ # errors or warnings. Returns true if boolful, or false if not.
9
+ #
10
+ # If false, then the Ping::External#exception method should contain a
11
+ # string indicating what went wrong. If true, the Ping::External#warning
12
+ # method may or may not contain a value.
13
+ #
14
+ def ping(host = @host)
15
+ super(host)
16
+ input, output, error = ""
17
+ pstring = "ping "
18
+ bool = false
19
+
20
+ case RUBY_PLATFORM
21
+ when /linux|bsd/i
22
+ pstring += "-c 1 #{host}"
23
+ when /solaris|sunos/i
24
+ pstring += "#{host} 1"
25
+ when /hpux/i
26
+ pstring += "#{host} -n 1"
27
+ when /win32|windows|mswin/i
28
+ pstring += "-n 1 #{host}"
29
+ else
30
+ pstring += "#{host}"
31
+ end
32
+
33
+ if RUBY_PLATFORM.match('mswin')
34
+ require 'win32/open3'
35
+ else
36
+ require 'open3'
37
+ end
38
+
39
+ start_time = Time.now
40
+
41
+ Timeout.timeout(@timeout){
42
+ input, output, error = Open3.popen3(pstring)
43
+ }
44
+
45
+ e = error.gets # Can't chomp yet, might be nil
46
+
47
+ input.close
48
+ error.close
49
+
50
+ unless e.nil?
51
+ if e =~ /warning/i
52
+ @warning = e.chomp
53
+ bool = true
54
+ else
55
+ @exception = e.chomp
56
+ end
57
+ # The "no answer" response goes to stdout, not stderr, so check it
58
+ else
59
+ lines = output.readlines
60
+ output.close
61
+ if lines.nil? || lines.empty?
62
+ bool = true
63
+ else
64
+ regexp = /
65
+ no\ answer|
66
+ host\ unreachable|
67
+ could\ not\ find\ host|
68
+ request\ timed\ out
69
+ /ix
70
+ lines.each{ |e|
71
+ if regexp.match(e)
72
+ @exception = e.chomp
73
+ break
74
+ end
75
+ }
76
+ bool = true unless @exception
77
+ end
78
+ end
79
+
80
+ # There is no duration if the ping failed
81
+ @duration = Time.now - start_time if bool
82
+
83
+ bool
84
+ end
85
+
86
+ alias ping? ping
87
+ alias pingecho ping
88
+ end
89
+
90
+ # Class alias for backwards compatibility.
91
+ PingExternal = Ping::External
92
+ end
@@ -0,0 +1,83 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ require 'ping'
3
+ require 'net/http'
4
+ require 'resolv-replace' # Force non-blocking Socket.getaddrinfo
5
+ require 'uri'
6
+
7
+ module Net
8
+ class Ping::HTTP < Ping
9
+
10
+ # By default an http ping will follow a redirect and give you the result
11
+ # of the final URI. If this value is set to false, then it will not
12
+ # follow a redirect and will return false immediately on a redirect.
13
+ #
14
+ attr_accessor :follow_redirect
15
+
16
+ # Creates and returns a new Ping::HTTP object. Note that the default
17
+ # port for Ping::HTTP is 80.
18
+ #
19
+ def initialize(uri=nil, port=80, timeout=5)
20
+ @follow_redirect = true
21
+ super(uri, port, timeout)
22
+ end
23
+
24
+ # Looks for an HTTP response from the URI passed to the constructor.
25
+ # If the result is a kind of Net::HTTPSuccess then the ping was
26
+ # boolful and true is returned. Otherwise, false is returned
27
+ # and the Ping::HTTP#exception method should contain a string
28
+ # indicating what went wrong.
29
+ #
30
+ # If the HTTP#follow_redirect accessor is set to true (which it is
31
+ # by default) and a redirect occurs during the ping, then the
32
+ # HTTP#warning attribute is set to the redirect message, but the
33
+ # return result is still true. If it's set to false then a false
34
+ # value is returned if a redirect occurs.
35
+ #
36
+ def ping(host = @host)
37
+ super(host)
38
+ bool = false
39
+ uri = URI.parse(host)
40
+
41
+ start_time = Time.now
42
+
43
+ begin
44
+ response = nil
45
+ Timeout.timeout(@timeout){
46
+ response = Net::HTTP.get_response(uri.host, uri.path, @port)
47
+ }
48
+ rescue Exception => err
49
+ @exception = err.message
50
+ else
51
+ if response.is_a?(Net::HTTPSuccess)
52
+ bool = true
53
+ else
54
+ if @follow_redirect
55
+ @warning = response.message
56
+ while response.is_a?(Net::HTTPRedirection)
57
+ redirect = URI.parse(response['location'])
58
+ redirect = uri + redirect if redirect.relative?
59
+ response = Net::HTTP.get_response(redirect.host, redirect.path, @port)
60
+ end
61
+ bool = true if response.is_a?(Net::HTTPSuccess)
62
+ else
63
+ @exception = response.message
64
+ end
65
+ end
66
+ end
67
+
68
+ # There is no duration if the ping failed
69
+ @duration = Time.now - start_time if bool
70
+
71
+ bool
72
+ end
73
+
74
+ alias ping? ping
75
+ alias pingecho ping
76
+ alias follow_redirect? follow_redirect
77
+ alias uri host
78
+ alias uri= host=
79
+ end
80
+
81
+ # Class alias for backwards compatibility
82
+ PingHTTP = Ping::HTTP
83
+ end
@@ -0,0 +1,168 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ require 'ping'
3
+
4
+ module Net
5
+ class Ping::ICMP < Ping
6
+ ICMP_ECHOREPLY = 0
7
+ ICMP_ECHO = 8
8
+ ICMP_SUBCODE = 0
9
+
10
+ # You cannot set or change the port value. A value of 0 is always
11
+ # used internally for ICMP pings.
12
+ #
13
+ undef_method :port=
14
+
15
+ # Returns the data size, i.e. number of bytes sent on the ping. The
16
+ # default size is 56.
17
+ #
18
+ attr_reader :data_size
19
+
20
+ # Creates and returns a new Ping::ICMP object. This is similar to its
21
+ # superclass constructor, but must be created with root privileges (on
22
+ # UNIX systems), and the port value is ignored.
23
+ #
24
+ def initialize(host=nil, port=nil, timeout=5)
25
+ raise 'requires root privileges' if Process.euid > 0
26
+
27
+ @seq = 0
28
+ @bind_port = 0
29
+ @bind_host = nil
30
+ @data_size = 56
31
+ @data = ''
32
+
33
+ 0.upto(@data_size){ |n| @data << (n % 256).chr }
34
+
35
+ @pid = Process.pid & 0xffff
36
+
37
+ super(host, port, timeout)
38
+ @port = nil # This value is not used in ICMP pings.
39
+ end
40
+
41
+ # Sets the number of bytes sent in the ping method.
42
+ #
43
+ def data_size=(size)
44
+ @data_size = size
45
+ @data = ''
46
+ 0.upto(size){ |n| @data << (n % 256).chr }
47
+ end
48
+
49
+ # Associates the local end of the socket connection with the given
50
+ # +host+ and +port+. The default port is 0.
51
+ #
52
+ def bind(host, port = 0)
53
+ @bind_host = host
54
+ @bind_port = port
55
+ end
56
+
57
+ # Pings the +host+ specified in this method or in the constructor. If a
58
+ # host was not specified either here or in the constructor, an
59
+ # ArgumentError is raised.
60
+ #
61
+ def ping(host = @host)
62
+ super(host)
63
+ bool = false
64
+
65
+ socket = Socket.new(
66
+ Socket::PF_INET,
67
+ Socket::SOCK_RAW,
68
+ Socket::IPPROTO_ICMP
69
+ )
70
+
71
+ if @bind_host
72
+ saddr = Socket.pack_sockaddr_in(@bind_port, @bind_host)
73
+ socket.bind(saddr)
74
+ end
75
+
76
+ @seq = (@seq + 1) % 65536
77
+ pstring = 'C2 n3 A' << @data_size.to_s
78
+ timeout = @timeout
79
+
80
+ checksum = 0
81
+ msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)
82
+ checksum = checksum(msg)
83
+ msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)
84
+
85
+ start_time = Time.now
86
+
87
+ begin
88
+ saddr = Socket.pack_sockaddr_in(0, host)
89
+ rescue Exception
90
+ return bool
91
+ end
92
+
93
+ socket.send(msg, 0, saddr) # Send the message
94
+
95
+ begin
96
+ Timeout.timeout(@timeout){
97
+ io_array = select([socket], nil, nil, timeout)
98
+
99
+ if io_array.nil? || io_array[0].empty?
100
+ return false
101
+ end
102
+
103
+ pid = nil
104
+ seq = nil
105
+
106
+ data, sender = socket.recvfrom(1500)
107
+ port, host = Socket.unpack_sockaddr_in(sender)
108
+ type, subcode = data[20, 2].unpack('C2')
109
+
110
+ case type
111
+ when ICMP_ECHOREPLY
112
+ if data.length >= 28
113
+ pid, seq = data[24, 4].unpack('n3')
114
+ end
115
+ else
116
+ if data.length > 56
117
+ pid, seq = data[52, 4].unpack('n3')
118
+ end
119
+ end
120
+
121
+ if pid == @pid && seq == @seq && type == ICMP_ECHOREPLY
122
+ bool = true
123
+ end
124
+ }
125
+ rescue Exception => err
126
+ @exception = err
127
+ ensure
128
+ socket.close if socket
129
+ end
130
+
131
+ # There is no duration if the ping failed
132
+ @duration = Time.now - start_time if bool
133
+
134
+ return bool
135
+ end
136
+
137
+ alias ping? ping
138
+ alias pingecho ping
139
+
140
+ private
141
+
142
+ # Perform a checksum on the message. This is the sum of all the short
143
+ # words and it folds the high order bits into the low order bits.
144
+ #
145
+ def checksum(msg)
146
+ length = msg.length
147
+ num_short = length / 2
148
+ check = 0
149
+
150
+ msg.unpack("n#{num_short}").each do |short|
151
+ check += short
152
+ end
153
+
154
+ if length % 2 > 0
155
+ check += msg[length-1, 1].unpack('C') << 8
156
+ end
157
+
158
+ check = (check >> 16) + (check & 0xffff)
159
+ return (~((check >> 16) + check) & 0xffff)
160
+ end
161
+
162
+ alias ping? ping
163
+ alias pingecho ping
164
+ end
165
+
166
+ # Alias for consistency with other ping related classes
167
+ PingICMP = Ping::ICMP
168
+ end
@@ -0,0 +1,81 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ module Net
5
+ class Ping
6
+ VERSION = '1.2.0'
7
+
8
+ # The host to ping. In the case of Ping::HTTP, this is the URI.
9
+ attr_accessor :host
10
+
11
+ # The port to ping. This is set to the echo port (7) by default. The
12
+ # Ping::HTTP class defaults to port 80.
13
+ #
14
+ attr_accessor :port
15
+
16
+ # The maximum time a ping attempt is made.
17
+ attr_accessor :timeout
18
+
19
+ # If a ping fails, this value is set to the error that occurred which
20
+ # caused it to fail.
21
+ #
22
+ attr_reader :exception
23
+
24
+ # This value is set if a ping succeeds, but some other condition arose
25
+ # during the ping attempt which merits warning, e.g a redirect in the
26
+ # case of Ping::HTTP#ping.
27
+ #
28
+ attr_reader :warning
29
+
30
+ # The number of seconds (returned as a Float) that it took to ping
31
+ # the host. This is not a precise value, but rather a good estimate
32
+ # since there is a small amount of internal calculation that is added
33
+ # to the overall time.
34
+ #
35
+ attr_reader :duration
36
+
37
+ # The default constructor for the Net::Ping class. Accepts an optional
38
+ # +host+, +port+ and +timeout+. The port defaults to your echo port, or
39
+ # 7 if that happens to be undefined. The default timeout is 5 seconds.
40
+ #
41
+ # The host, although optional in the constructor, must be specified at
42
+ # some point before the Net::Ping#ping method is called, or else an
43
+ # ArgumentError will be raised.
44
+ #
45
+ # Yields +self+ in block context.
46
+ #
47
+ # This class is not meant to be instantiated directly. It is strictly
48
+ # meant as an interface for subclasses.
49
+ #
50
+ def initialize(host=nil, port=nil, timeout=5)
51
+ @host = host
52
+ @port = port || Socket.getservbyname('echo') || 7
53
+ @timeout = timeout
54
+ @exception = nil
55
+ @warning = nil
56
+ @duration = nil
57
+
58
+ yield self if block_given?
59
+ end
60
+
61
+ # The default interface for the Net::Ping#ping method. Each subclass
62
+ # should call super() before continuing with their own implementation in
63
+ # order to ensure that the @exception and @warning instance variables
64
+ # are reset.
65
+ #
66
+ # If +host+ is nil here, then it will use the host specified in the
67
+ # constructor. If the +host+ is nil and there was no host specified
68
+ # in the constructor then an ArgumentError is raised.
69
+ #--
70
+ # The @duration should be set in the subclass' ping method.
71
+ #
72
+ def ping(host = @host)
73
+ raise ArgumentError, 'no host specified' unless host
74
+ @exception = nil
75
+ @warning = nil
76
+ end
77
+
78
+ alias ping? ping
79
+ alias pingecho ping
80
+ end
81
+ end
@@ -0,0 +1,84 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ require 'ping'
3
+
4
+ module Net
5
+
6
+ # With a TCP ping, simply try to open a connection. If we are boolful,
7
+ # assume bool. In either case, close the connection to be polite.
8
+ #
9
+ class Ping::TCP < Ping
10
+ @@econnrefused = false
11
+
12
+ # Returns whether or not Errno::ECONNREFUSED is considered a boolful
13
+ # ping. The default is false.
14
+ #
15
+ def self.econnrefused
16
+ @@econnrefused
17
+ end
18
+
19
+ # Sets whether or not an Errno::ECONNREFUSED should be considered a
20
+ # boolful ping.
21
+ #
22
+ def self.econnrefused=(bool)
23
+ unless bool.kind_of?(TrueClass) || bool.kind_of?(FalseClass)
24
+ raise ArgumentError, 'argument must be true or false'
25
+ end
26
+ @@econnrefused = bool
27
+ end
28
+
29
+ # This method attempts to ping a host and port using a TCPSocket with
30
+ # the host, port and timeout values passed in the constructor. Returns
31
+ # true if boolful, or false otherwise.
32
+ #
33
+ # Note that, by default, an Errno::ECONNREFUSED return result will be
34
+ # considered a failed ping. See the documentation for the
35
+ # Ping::TCP.econnrefused= method if you wish to change this behavior.
36
+ #
37
+ def ping(host=@host)
38
+ super(host)
39
+
40
+ bool = false
41
+ tcp = nil
42
+ start_time = Time.now
43
+
44
+ begin
45
+ Timeout.timeout(@timeout){
46
+ begin
47
+ tcp = TCPSocket.new(host, @port)
48
+ rescue Errno::ECONNREFUSED => err
49
+ if @@econnrefused
50
+ bool = true
51
+ else
52
+ @exception = err
53
+ end
54
+ rescue Exception => err
55
+ @exception = err
56
+ else
57
+ bool = true
58
+ end
59
+ }
60
+ rescue Timeout::Error => err
61
+ @exception = err
62
+ ensure
63
+ tcp.close if tcp
64
+ end
65
+
66
+ # There is no duration if the ping failed
67
+ @duration = Time.now - start_time if bool
68
+
69
+ bool
70
+ end
71
+
72
+ alias ping? ping
73
+ alias pingecho ping
74
+
75
+ # Class method aliases
76
+ class << self
77
+ alias ecr econnrefused
78
+ alias ecr= econnrefused=
79
+ end
80
+ end
81
+
82
+ # Class alias for backwards compatibility
83
+ PingTCP = Ping::TCP
84
+ end