net-ping 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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