net-ping 2.1.0-universal-linux

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,189 @@
1
+ require File.join(File.dirname(__FILE__), 'ping')
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'open-uri'
6
+
7
+ # Force non-blocking Socket.getaddrinfo on Unix systems. Do not use on
8
+ # Windows because it (ironically) causes blocking problems.
9
+ unless File::ALT_SEPARATOR or RUBY_VERSION >= "1.9.3"
10
+ require 'resolv-replace'
11
+ end
12
+
13
+ # The Net module serves as a namespace only.
14
+ module Net
15
+
16
+ # The Ping::HTTP class encapsulates methods for HTTP pings.
17
+ class Ping::HTTP < Ping
18
+
19
+ # By default an http ping will follow a redirect and give you the result
20
+ # of the final URI. If this value is set to false, then it will not
21
+ # follow a redirect and will return false immediately on a redirect.
22
+ #
23
+ attr_accessor :follow_redirect
24
+
25
+ # The maximum number of redirects allowed. The default is 5.
26
+ attr_accessor :redirect_limit
27
+
28
+ # The user agent used for the HTTP request. The default is nil.
29
+ attr_accessor :user_agent
30
+
31
+ # OpenSSL certificate verification mode. The default is VERIFY_NONE.
32
+ attr_accessor :ssl_verify_mode
33
+
34
+ # Use GET request instead HEAD. The default is false.
35
+ attr_accessor :get_request
36
+
37
+ # was this ping proxied?
38
+ attr_accessor :proxied
39
+
40
+ # For unsuccessful requests that return a server error, it is
41
+ # useful to know the HTTP status code of the response.
42
+ attr_reader :code
43
+
44
+ # Creates and returns a new Ping::HTTP object. The default port is the
45
+ # port associated with the URI or 80. The default timeout is 5 seconds.
46
+ #
47
+ def initialize(uri=nil, port=nil, timeout=5)
48
+ @follow_redirect = true
49
+ @redirect_limit = 5
50
+ @ssl_verify_mode = OpenSSL::SSL::VERIFY_NONE
51
+ @get_request = false
52
+ @code = nil
53
+
54
+ port ||= URI.parse(uri).port if uri
55
+ port ||= 80
56
+
57
+ @port = port
58
+
59
+ super(uri, port, timeout)
60
+ end
61
+
62
+ # Looks for an HTTP response from the URI passed to the constructor.
63
+ # If the result is a kind of Net::HTTPSuccess then the ping was
64
+ # successful and true is returned. Otherwise, false is returned
65
+ # and the Ping::HTTP#exception method should contain a string
66
+ # indicating what went wrong.
67
+ #
68
+ # If the HTTP#follow_redirect accessor is set to true (which it is
69
+ # by default) and a redirect occurs during the ping, then the
70
+ # HTTP#warning attribute is set to the redirect message, but the
71
+ # return result is still true. If it's set to false then a redirect
72
+ # response is considered a failed ping.
73
+ #
74
+ # If no file or path is specified in the URI, then '/' is assumed.
75
+ # If no scheme is present in the URI, then 'http' is assumed.
76
+ #
77
+ def ping(host = @host)
78
+ super(host)
79
+ bool = false
80
+
81
+ # See https://bugs.ruby-lang.org/issues/8645
82
+ host = "http://#{host}" unless /\A(http(s)?:\/\/)/.match(host)
83
+
84
+ uri = URI.parse(host)
85
+
86
+ # A port provided here via the host argument overrides anything
87
+ # provided in constructor.
88
+ #
89
+ port = URI.split(host)[3] || URI.parse(host).port || @port
90
+ port = port.to_i
91
+
92
+ start_time = Time.now
93
+
94
+ response = do_ping(uri, port)
95
+
96
+ if response.is_a?(Net::HTTPSuccess)
97
+ bool = true
98
+ elsif redirect?(response) # Check code, HTTPRedirection does not always work
99
+ if @follow_redirect
100
+ @warning = response.message
101
+ rlimit = 0
102
+
103
+ while redirect?(response)
104
+ if rlimit >= redirect_limit
105
+ @exception = "Redirect limit exceeded"
106
+ break
107
+ end
108
+ redirect = URI.parse(response['location'])
109
+ port = redirect.port
110
+ redirect = uri + redirect if redirect.relative?
111
+
112
+ start_time = Time.now
113
+ response = do_ping(redirect, port)
114
+ rlimit += 1
115
+ end
116
+
117
+ if response.is_a?(Net::HTTPSuccess)
118
+ bool = true
119
+ else
120
+ @warning = nil
121
+ @exception ||= response.message
122
+ end
123
+
124
+ else
125
+ @exception = response.message
126
+ end
127
+ else
128
+ @exception ||= response.message
129
+ end
130
+
131
+ # There is no duration if the ping failed
132
+ @duration = Time.now - start_time if bool
133
+
134
+ bool
135
+ end
136
+
137
+ alias follow_redirect? follow_redirect
138
+ alias uri host
139
+ alias uri= host=
140
+
141
+ private
142
+
143
+ def redirect?(response)
144
+ response && response.code.to_i >= 300 && response.code.to_i < 400
145
+ end
146
+
147
+ def do_ping(uri, port)
148
+ response = nil
149
+ proxy = uri.find_proxy || URI.parse("")
150
+
151
+ begin
152
+ uri_path = uri.path.empty? ? '/' : uri.path
153
+
154
+ headers = {}
155
+ headers["User-Agent"] = user_agent if user_agent
156
+
157
+ http = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password).new(uri.host, port)
158
+
159
+ http.open_timeout = timeout
160
+ http.read_timeout = timeout
161
+
162
+ @proxied = http.proxy?
163
+
164
+ if @get_request == true
165
+ request = Net::HTTP::Get.new(uri_path, headers)
166
+ else
167
+ request = Net::HTTP::Head.new(uri_path, headers)
168
+ end
169
+
170
+ if uri.scheme == 'https'
171
+ http.use_ssl = true
172
+ http.verify_mode = @ssl_verify_mode
173
+ end
174
+
175
+ response = http.start{ |h|
176
+ h.open_timeout = timeout
177
+ h.read_timeout = timeout
178
+ h.request(request)
179
+ }
180
+ rescue Exception => err
181
+ @exception = err.message
182
+ end
183
+
184
+ @code = response.code if response
185
+
186
+ response
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,188 @@
1
+ require File.join(File.dirname(__FILE__), 'ping')
2
+
3
+ if File::ALT_SEPARATOR
4
+ require 'win32/security'
5
+ end
6
+
7
+ # The Net module serves as a namespace only.
8
+ module Net
9
+
10
+ # The Net::Ping::ICMP class encapsulates an icmp ping.
11
+ class Ping::ICMP < Ping
12
+ ICMP_ECHOREPLY = 0 # Echo reply
13
+ ICMP_ECHO = 8 # Echo request
14
+ ICMP_SUBCODE = 0
15
+
16
+ # You cannot set or change the port value. A value of 0 is always
17
+ # used internally for ICMP pings.
18
+ #
19
+ undef_method :port=
20
+
21
+ # Returns the data size, i.e. number of bytes sent on the ping. The
22
+ # default size is 56.
23
+ #
24
+ attr_reader :data_size
25
+
26
+ # Creates and returns a new Ping::ICMP object. This is similar to its
27
+ # superclass constructor, but must be created with root privileges (on
28
+ # UNIX systems), and the port value is ignored.
29
+ #
30
+ def initialize(host=nil, port=nil, timeout=5)
31
+ begin
32
+ # If we have cap2, but not are root, or have net_raw, raise an error
33
+ require 'cap2'
34
+ current_process = Cap2.process
35
+ unless Process.euid == 0 \
36
+ || current_process.permitted?(:net_raw) \
37
+ && current_process.enabled?(:net_raw)
38
+ raise StandardError, 'requires root privileges or setcap net_raw'
39
+ end
40
+ rescue LoadError
41
+ # Without cap2, raise error if we are not root
42
+ unless Process.euid == 0
43
+ raise StandardError, 'requires root privileges or setcap net_raw'
44
+ end
45
+ end
46
+
47
+ if File::ALT_SEPARATOR
48
+ unless Win32::Security.elevated_security?
49
+ raise 'requires elevated security'
50
+ end
51
+ end
52
+
53
+ @seq = 0
54
+ @bind_port = 0
55
+ @bind_host = nil
56
+ @data_size = 56
57
+ @data = ''
58
+
59
+ 0.upto(@data_size){ |n| @data << (n % 256).chr }
60
+
61
+ @ping_id = (Thread.current.object_id ^ Process.pid) & 0xffff
62
+
63
+ super(host, port, timeout)
64
+ @port = nil # This value is not used in ICMP pings.
65
+ end
66
+
67
+ # Sets the number of bytes sent in the ping method.
68
+ #
69
+ def data_size=(size)
70
+ @data_size = size
71
+ @data = ''
72
+ 0.upto(size){ |n| @data << (n % 256).chr }
73
+ end
74
+
75
+ # Associates the local end of the socket connection with the given
76
+ # +host+ and +port+. The default port is 0.
77
+ #
78
+ def bind(host, port = 0)
79
+ @bind_host = host
80
+ @bind_port = port
81
+ end
82
+
83
+ # Pings the +host+ specified in this method or in the constructor. If a
84
+ # host was not specified either here or in the constructor, an
85
+ # ArgumentError is raised.
86
+ #
87
+ def ping(host = @host)
88
+ super(host)
89
+ bool = false
90
+
91
+ socket = Socket.new(
92
+ Socket::PF_INET,
93
+ Socket::SOCK_RAW,
94
+ Socket::IPPROTO_ICMP
95
+ )
96
+
97
+ if @bind_host
98
+ saddr = Socket.pack_sockaddr_in(@bind_port, @bind_host)
99
+ socket.bind(saddr)
100
+ end
101
+
102
+ @seq = (@seq + 1) % 65536
103
+ pstring = 'C2 n3 A' << @data_size.to_s
104
+ timeout = @timeout
105
+
106
+ checksum = 0
107
+ msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @ping_id, @seq, @data].pack(pstring)
108
+
109
+ checksum = checksum(msg)
110
+ msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @ping_id, @seq, @data].pack(pstring)
111
+
112
+ begin
113
+ saddr = Socket.pack_sockaddr_in(0, host)
114
+ rescue Exception
115
+ socket.close unless socket.closed?
116
+ return bool
117
+ end
118
+
119
+ start_time = Time.now
120
+
121
+ socket.send(msg, 0, saddr) # Send the message
122
+
123
+ begin
124
+ Timeout.timeout(@timeout){
125
+ while true
126
+ io_array = select([socket], nil, nil, timeout)
127
+
128
+ if io_array.nil? || io_array[0].empty?
129
+ raise Timeout::Error if io_array.nil?
130
+ return false
131
+ end
132
+
133
+ ping_id = nil
134
+ seq = nil
135
+
136
+ data = socket.recvfrom(1500).first
137
+ type = data[20, 2].unpack('C2').first
138
+
139
+ case type
140
+ when ICMP_ECHOREPLY
141
+ if data.length >= 28
142
+ ping_id, seq = data[24, 4].unpack('n3')
143
+ end
144
+ else
145
+ if data.length > 56
146
+ ping_id, seq = data[52, 4].unpack('n3')
147
+ end
148
+ end
149
+
150
+ if ping_id == @ping_id && seq == @seq && type == ICMP_ECHOREPLY
151
+ bool = true
152
+ break
153
+ end
154
+ end
155
+ }
156
+ rescue Exception => err
157
+ @exception = err
158
+ ensure
159
+ socket.close if socket
160
+ end
161
+
162
+ # There is no duration if the ping failed
163
+ @duration = Time.now - start_time if bool
164
+ end
165
+
166
+ private
167
+
168
+ # Perform a checksum on the message. This is the sum of all the short
169
+ # words and it folds the high order bits into the low order bits.
170
+ #
171
+ def checksum(msg)
172
+ length = msg.length
173
+ num_short = length / 2
174
+ check = 0
175
+
176
+ msg.unpack("n#{num_short}").each do |short|
177
+ check += short
178
+ end
179
+
180
+ if length % 2 > 0
181
+ check += msg[length-1, 1].unpack('C').first << 8
182
+ end
183
+
184
+ check = (check >> 16) + (check & 0xffff)
185
+ return (~((check >> 16) + check) & 0xffff)
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,99 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ require_relative 'version'
5
+
6
+ # The Net module serves as a namespace only.
7
+ #
8
+ module Net
9
+
10
+ # The Ping class serves as an abstract base class for all other Ping class
11
+ # types. You should not instantiate this class directly.
12
+ #
13
+ class Ping
14
+ # The host to ping. In the case of Ping::HTTP, this is the URI.
15
+ attr_accessor :host
16
+
17
+ # The port to ping. This is set to the echo port (7) by default. The
18
+ # Ping::HTTP class defaults to port 80.
19
+ #
20
+ attr_accessor :port
21
+
22
+ # The maximum time a ping attempt is made.
23
+ attr_accessor :timeout
24
+
25
+ # If a ping fails, this value is set to the error that occurred which
26
+ # caused it to fail.
27
+ #
28
+ attr_reader :exception
29
+
30
+ # This value is set if a ping succeeds, but some other condition arose
31
+ # during the ping attempt which merits warning, e.g a redirect in the
32
+ # case of Ping::HTTP#ping.
33
+ #
34
+ attr_reader :warning
35
+
36
+ # The number of seconds (returned as a Float) that it took to ping
37
+ # the host. This is not a precise value, but rather a good estimate
38
+ # since there is a small amount of internal calculation that is added
39
+ # to the overall time.
40
+ #
41
+ attr_reader :duration
42
+
43
+ # The default constructor for the Net::Ping class. Accepts an optional
44
+ # +host+, +port+ and +timeout+. The port defaults to your echo port, or
45
+ # 7 if that happens to be undefined. The default timeout is 5 seconds.
46
+ #
47
+ # The host, although optional in the constructor, must be specified at
48
+ # some point before the Net::Ping#ping method is called, or else an
49
+ # ArgumentError will be raised.
50
+ #
51
+ # Yields +self+ in block context.
52
+ #
53
+ # This class is not meant to be instantiated directly. It is strictly
54
+ # meant as an interface for subclasses.
55
+ #
56
+ def initialize(host=nil, port=nil, timeout=5)
57
+ @host = host
58
+ @port = port || Socket.getservbyname('echo') || 7
59
+ @timeout = timeout
60
+ @exception = nil
61
+ @warning = nil
62
+ @duration = nil
63
+
64
+ yield self if block_given?
65
+ end
66
+
67
+ # The default interface for the Net::Ping#ping method. Each subclass
68
+ # should call super() before continuing with their own implementation in
69
+ # order to ensure that the @exception and @warning instance variables
70
+ # are reset.
71
+ #
72
+ # If +host+ is nil here, then it will use the host specified in the
73
+ # constructor. If the +host+ is nil and there was no host specified
74
+ # in the constructor then an ArgumentError is raised.
75
+ #--
76
+ # The @duration should be set in the subclass' ping method.
77
+ #
78
+ def ping(host = @host)
79
+ raise ArgumentError, 'no host specified' unless host
80
+ @exception = nil
81
+ @warning = nil
82
+ @duration = nil
83
+ end
84
+
85
+ def ping6(host = @host)
86
+ raise ArgumentError, 'no host specified' unless host
87
+ @exception = nil
88
+ @warning = nil
89
+ @duration = nil
90
+ end
91
+
92
+ def ping?(host = @host)
93
+ !!ping(host)
94
+ end
95
+
96
+ alias pingecho ping
97
+
98
+ end
99
+ end
@@ -0,0 +1,110 @@
1
+ require File.join(File.dirname(__FILE__), 'ping')
2
+
3
+ # The Net module serves as a namespace only.
4
+ module Net
5
+
6
+ # With a TCP ping simply try to open a connection. If we are successful,
7
+ # assume success. In either case close the connection to be polite.
8
+ #
9
+ class Ping::TCP < Ping
10
+ @@service_check = false
11
+
12
+ # Returns whether or not Errno::ECONNREFUSED is considered a successful
13
+ # ping. The default is false.
14
+ #
15
+ def self.service_check
16
+ @@service_check
17
+ end
18
+
19
+ # Sets whether or not an Errno::ECONNREFUSED should be considered a
20
+ # successful ping.
21
+ #
22
+ def self.service_check=(bool)
23
+ unless bool.kind_of?(TrueClass) || bool.kind_of?(FalseClass)
24
+ raise ArgumentError, 'argument must be true or false'
25
+ end
26
+ @@service_check = 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 successful, 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.service_check= method if you wish to change this behavior.
36
+ #
37
+ def ping(host=@host)
38
+ super(host)
39
+
40
+ bool = false
41
+
42
+ # Failure here most likely means bad host, so just bail.
43
+ begin
44
+ addr = Socket.getaddrinfo(host, port)
45
+ rescue SocketError => err
46
+ @exception = err
47
+ return false
48
+ end
49
+
50
+ begin
51
+ # Where addr[0][0] is likely AF_INET.
52
+ sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
53
+
54
+ # This may not be entirely necessary
55
+ sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
56
+
57
+ start_time = Time.now
58
+
59
+ begin
60
+ # Where addr[0][3] is an IP address
61
+ sock.connect_nonblock(Socket.pack_sockaddr_in(port, addr[0][3]))
62
+ rescue Errno::EINPROGRESS
63
+ # No-op, continue below
64
+ rescue Exception => err
65
+ # Something has gone horribly wrong
66
+ @exception = err
67
+ return false
68
+ end
69
+
70
+ resp = IO.select(nil, [sock], nil, timeout)
71
+
72
+ if resp.nil? # Assume ECONNREFUSED if nil
73
+ if @@service_check
74
+ bool = true
75
+ else
76
+ bool = false
77
+ @exception = Errno::ECONNREFUSED
78
+ end
79
+ else
80
+ sockopt = sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR)
81
+
82
+ if sockopt.int != 0
83
+ if @@service_check && sockopt.int == Errno::ECONNREFUSED::Errno
84
+ bool = true
85
+ else
86
+ bool = false
87
+ @exception = SystemCallError.new(sockopt.int)
88
+ end
89
+ else
90
+ bool = true
91
+ end
92
+ end
93
+ ensure
94
+ sock.close if sock
95
+ end
96
+
97
+ # There is no duration if the ping failed
98
+ @duration = Time.now - start_time if bool
99
+ end
100
+
101
+
102
+ # Class method aliases. DEPRECATED.
103
+ class << self
104
+ alias econnrefused service_check
105
+ alias econnrefused= service_check=
106
+ alias ecr service_check
107
+ alias ecr= service_check=
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,117 @@
1
+ require File.join(File.dirname(__FILE__), 'ping')
2
+
3
+ # The Net module serves as a namespace only.
4
+ module Net
5
+
6
+ # The Ping::UDP class encapsulates methods for UDP pings.
7
+ class Ping::UDP < Ping
8
+ @@service_check = true
9
+
10
+ # Returns whether or not the connect behavior should enforce remote
11
+ # service availability as well as reachability. The default is true.
12
+ #
13
+ def self.service_check
14
+ @@service_check
15
+ end
16
+
17
+ # Set whether or not the connect behavior should enforce remote
18
+ # service availability as well as reachability. If set to false
19
+ # then Errno::ECONNREFUSED or Errno::ECONNRESET will be considered
20
+ # a successful ping, meaning no actual data handshaking is required.
21
+ # By default, if either of those errors occurs it is considered a failed
22
+ # ping.
23
+ #
24
+ def self.service_check=(bool)
25
+ unless bool.kind_of?(TrueClass) || bool.kind_of?(FalseClass)
26
+ raise ArgumentError, 'argument must be true or false'
27
+ end
28
+ @@service_check = bool
29
+ end
30
+
31
+ # The maximum data size that can be sent in a UDP ping.
32
+ MAX_DATA = 64
33
+
34
+ # The data to send to the remote host. By default this is 'ping'.
35
+ # This should be MAX_DATA size characters or less.
36
+ #
37
+ attr_reader :data
38
+
39
+ # Creates and returns a new Ping::UDP object. This is effectively
40
+ # identical to its superclass constructor.
41
+ #
42
+ def initialize(host=nil, port=nil, timeout=5)
43
+ @data = 'ping'
44
+
45
+ super(host, port, timeout)
46
+
47
+ @bind_host = nil
48
+ @bind_port = nil
49
+ end
50
+
51
+ # Sets the data string sent to the remote host. This value cannot have
52
+ # a size greater than MAX_DATA.
53
+ #
54
+ def data=(string)
55
+ if string.size > MAX_DATA
56
+ err = "cannot set data string larger than #{MAX_DATA} characters"
57
+ raise ArgumentError, err
58
+ end
59
+
60
+ @data = string
61
+ end
62
+
63
+ # Associates the local end of the UDP connection with the given +host+
64
+ # and +port+. This is essentially a wrapper for UDPSocket#bind.
65
+ #
66
+ def bind(host, port)
67
+ @bind_host = host
68
+ @bind_port = port
69
+ end
70
+
71
+ # Sends a simple text string to the host and checks the return string. If
72
+ # the string sent and the string returned are a match then the ping was
73
+ # successful and true is returned. Otherwise, false is returned.
74
+ #
75
+ def ping(host = @host)
76
+ super(host)
77
+
78
+ bool = false
79
+ udp = UDPSocket.open
80
+ array = []
81
+
82
+ if @bind_host
83
+ udp.bind(@bind_host, @bind_port)
84
+ end
85
+
86
+ start_time = Time.now
87
+
88
+ begin
89
+ Timeout.timeout(@timeout){
90
+ udp.connect(host, @port)
91
+ udp.send(@data, 0)
92
+ array = udp.recvfrom(MAX_DATA)
93
+ }
94
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET => err
95
+ if @@service_check
96
+ @exception = err
97
+ else
98
+ bool = true
99
+ end
100
+ rescue Exception => err
101
+ @exception = err
102
+ else
103
+ if array[0] == @data
104
+ bool = true
105
+ end
106
+ ensure
107
+ udp.close if udp
108
+ end
109
+
110
+ # There is no duration if the ping failed
111
+ @duration = Time.now - start_time if bool
112
+
113
+ bool
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,6 @@
1
+ module Net
2
+ class Ping
3
+ # The version of the net-ping library.
4
+ VERSION = '2.1.0'
5
+ end
6
+ end