net-ping 1.3.3-x86-mingw32
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.
- data/CHANGES +189 -0
- data/MANIFEST +23 -0
- data/README +48 -0
- data/Rakefile +84 -0
- data/doc/ping.txt +249 -0
- data/examples/example_pingexternal.rb +16 -0
- data/examples/example_pinghttp.rb +22 -0
- data/examples/example_pingtcp.rb +16 -0
- data/examples/example_pingudp.rb +12 -0
- data/lib/net/ping.rb +17 -0
- data/lib/net/ping/external.rb +126 -0
- data/lib/net/ping/http.rb +98 -0
- data/lib/net/ping/icmp.rb +165 -0
- data/lib/net/ping/ping.rb +88 -0
- data/lib/net/ping/tcp.rb +83 -0
- data/lib/net/ping/udp.rb +119 -0
- data/lib/net/ping/wmi.rb +118 -0
- data/net-ping.gemspec +38 -0
- data/test/test_net_ping.rb +26 -0
- data/test/test_net_ping_external.rb +87 -0
- data/test/test_net_ping_http.rb +115 -0
- data/test/test_net_ping_icmp.rb +126 -0
- data/test/test_net_ping_tcp.rb +108 -0
- data/test/test_net_ping_udp.rb +122 -0
- data/test/test_net_ping_wmi.rb +84 -0
- metadata +129 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
########################################################################
|
2
|
+
# example_pingexternal.rb
|
3
|
+
#
|
4
|
+
# A short sample program demonstrating an external ping. You can run
|
5
|
+
# this program via the example:external task. Modify as you see fit.
|
6
|
+
########################################################################
|
7
|
+
require 'net/ping'
|
8
|
+
|
9
|
+
good = 'www.rubyforge.org'
|
10
|
+
bad = 'foo.bar.baz'
|
11
|
+
|
12
|
+
p1 = Net::Ping::External.new(good)
|
13
|
+
p p1.ping?
|
14
|
+
|
15
|
+
p2 = Net::Ping::External.new(bad)
|
16
|
+
p p2.ping?
|
@@ -0,0 +1,22 @@
|
|
1
|
+
########################################################################
|
2
|
+
# example_pinghttp.rb
|
3
|
+
#
|
4
|
+
# A short sample program demonstrating an http ping. You can run
|
5
|
+
# this program via the example:http task. Modify as you see fit.
|
6
|
+
########################################################################
|
7
|
+
require 'net/ping'
|
8
|
+
|
9
|
+
good = 'http://www.google.com/index.html'
|
10
|
+
bad = 'http://www.ruby-lang.org/index.html'
|
11
|
+
|
12
|
+
puts "== Good ping, no redirect"
|
13
|
+
|
14
|
+
p1 = Net::Ping::HTTP.new(good)
|
15
|
+
p p1.ping?
|
16
|
+
|
17
|
+
puts "== Bad ping"
|
18
|
+
|
19
|
+
p2 = Net::Ping::HTTP.new(bad)
|
20
|
+
p p2.ping?
|
21
|
+
p p2.warning
|
22
|
+
p p2.exception
|
@@ -0,0 +1,16 @@
|
|
1
|
+
########################################################################
|
2
|
+
# example_pingtcp.rb
|
3
|
+
#
|
4
|
+
# A short sample program demonstrating a tcp ping. You can run
|
5
|
+
# this program via the example:tcp task. Modify as you see fit.
|
6
|
+
########################################################################
|
7
|
+
require 'net/ping'
|
8
|
+
|
9
|
+
good = 'www.google.com'
|
10
|
+
bad = 'foo.bar.baz'
|
11
|
+
|
12
|
+
p1 = Net::Ping::TCP.new(good, 'http')
|
13
|
+
p p1.ping?
|
14
|
+
|
15
|
+
p2 = Net::Ping::TCP.new(bad)
|
16
|
+
p p2.ping?
|
@@ -0,0 +1,12 @@
|
|
1
|
+
########################################################################
|
2
|
+
# example_pingudp.rb
|
3
|
+
#
|
4
|
+
# A short sample program demonstrating a UDP ping. You can run
|
5
|
+
# this program via the example:udp task. Modify as you see fit.
|
6
|
+
########################################################################
|
7
|
+
require 'net/ping'
|
8
|
+
|
9
|
+
host = 'www.google.com'
|
10
|
+
|
11
|
+
u = Net::Ping::UDP.new(host)
|
12
|
+
p u.ping?
|
data/lib/net/ping.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# By doing a "require 'net/ping'" you are requiring every subclass. If you
|
2
|
+
# want to require a specific ping type only, do "require 'net/ping/tcp'",
|
3
|
+
# for example.
|
4
|
+
#
|
5
|
+
require 'rbconfig'
|
6
|
+
|
7
|
+
require File.join(File.dirname(__FILE__), 'ping/tcp')
|
8
|
+
require File.join(File.dirname(__FILE__), 'ping/udp')
|
9
|
+
require File.join(File.dirname(__FILE__), 'ping/icmp')
|
10
|
+
require File.join(File.dirname(__FILE__), 'ping/external')
|
11
|
+
require File.join(File.dirname(__FILE__), 'ping/http')
|
12
|
+
|
13
|
+
if Config::CONFIG['host_os'] =~ /msdos|mswin|cygwin|mingw|win32/i &&
|
14
|
+
JAVA_PLATFORM != 'java'
|
15
|
+
then
|
16
|
+
require File.join(File.dirname(__FILE__), 'ping/wmi')
|
17
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require File.join(File.dirname(__FILE__), 'ping')
|
3
|
+
|
4
|
+
if Config::CONFIG['host_os'] =~ /mswin|win32|msdos|cygwin|mingw/i &&
|
5
|
+
RUBY_PLATFORM != 'java'
|
6
|
+
then
|
7
|
+
if RUBY_VERSION.to_f < 1.9
|
8
|
+
require 'win32/open3'
|
9
|
+
end
|
10
|
+
require 'windows/console'
|
11
|
+
else
|
12
|
+
require 'open3'
|
13
|
+
end
|
14
|
+
|
15
|
+
# The Net module serves as a namespace only.
|
16
|
+
module Net
|
17
|
+
|
18
|
+
# The Ping::External class encapsulates methods for external (system) pings.
|
19
|
+
class Ping::External < Ping
|
20
|
+
|
21
|
+
CWINDOWS = Config::CONFIG['host_os'] =~ /mswin|win32|msdos|cygwin|mingw/i &&
|
22
|
+
RUBY_PLATFORM != 'java'
|
23
|
+
|
24
|
+
if CWINDOWS
|
25
|
+
include Windows::Console
|
26
|
+
end
|
27
|
+
|
28
|
+
# Pings the host using your system's ping utility and checks for any
|
29
|
+
# errors or warnings. Returns true if successful, or false if not.
|
30
|
+
#
|
31
|
+
# If the ping failed then the Ping::External#exception method should
|
32
|
+
# contain a string indicating what went wrong. If the ping succeeded then
|
33
|
+
# the Ping::External#warning method may or may not contain a value.
|
34
|
+
#
|
35
|
+
def ping(host = @host)
|
36
|
+
super(host)
|
37
|
+
|
38
|
+
input, output, error = ""
|
39
|
+
pstring = "ping "
|
40
|
+
bool = false
|
41
|
+
orig_cp = nil
|
42
|
+
|
43
|
+
case Config::CONFIG['host_os']
|
44
|
+
when /linux|bsd|osx|mach|darwin/i
|
45
|
+
pstring += "-c 1 #{host}"
|
46
|
+
when /solaris|sunos/i
|
47
|
+
pstring += "#{host} 1"
|
48
|
+
when /hpux/i
|
49
|
+
pstring += "#{host} -n 1"
|
50
|
+
when /win32|windows|msdos|mswin|cygwin|mingw/i
|
51
|
+
if RUBY_PLATFORM != 'java'
|
52
|
+
orig_cp = GetConsoleCP()
|
53
|
+
SetConsoleCP(437) if orig_cp != 437 # United States
|
54
|
+
end
|
55
|
+
pstring += "-n 1 #{host}"
|
56
|
+
else
|
57
|
+
pstring += "#{host}"
|
58
|
+
end
|
59
|
+
|
60
|
+
start_time = Time.now
|
61
|
+
|
62
|
+
begin
|
63
|
+
err = nil
|
64
|
+
|
65
|
+
Timeout.timeout(@timeout){
|
66
|
+
input, output, error = Open3.popen3(pstring)
|
67
|
+
err = error.gets # Can't chomp yet, might be nil
|
68
|
+
}
|
69
|
+
|
70
|
+
input.close
|
71
|
+
error.close
|
72
|
+
|
73
|
+
if CWINDOWS && GetConsoleCP() != orig_cp
|
74
|
+
SetConsoleCP(orig_cp)
|
75
|
+
end
|
76
|
+
|
77
|
+
unless err.nil?
|
78
|
+
if err =~ /warning/i
|
79
|
+
@warning = err.chomp
|
80
|
+
bool = true
|
81
|
+
else
|
82
|
+
@exception = err.chomp
|
83
|
+
end
|
84
|
+
# The "no answer" response goes to stdout, not stderr, so check it
|
85
|
+
else
|
86
|
+
lines = output.readlines
|
87
|
+
output.close
|
88
|
+
if lines.nil? || lines.empty?
|
89
|
+
bool = true
|
90
|
+
else
|
91
|
+
regexp = /
|
92
|
+
no\ answer|
|
93
|
+
host\ unreachable|
|
94
|
+
could\ not\ find\ host|
|
95
|
+
request\ timed\ out|
|
96
|
+
100%\ packet\ loss
|
97
|
+
/ix
|
98
|
+
|
99
|
+
lines.each{ |line|
|
100
|
+
if regexp.match(line)
|
101
|
+
@exception = line.chomp
|
102
|
+
break
|
103
|
+
end
|
104
|
+
}
|
105
|
+
|
106
|
+
bool = true unless @exception
|
107
|
+
end
|
108
|
+
end
|
109
|
+
rescue Exception => error
|
110
|
+
@exception = error.message
|
111
|
+
ensure
|
112
|
+
input.close if input && !input.closed?
|
113
|
+
error.close if error && !error.closed?
|
114
|
+
output.close if output && !output.closed?
|
115
|
+
end
|
116
|
+
|
117
|
+
# There is no duration if the ping failed
|
118
|
+
@duration = Time.now - start_time if bool
|
119
|
+
|
120
|
+
bool
|
121
|
+
end
|
122
|
+
|
123
|
+
alias ping? ping
|
124
|
+
alias pingecho ping
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'ping')
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'rbconfig'
|
5
|
+
|
6
|
+
# Force non-blocking Socket.getaddrinfo on Unix systems. Do not use on
|
7
|
+
# Windows because it (ironically) causes blocking problems.
|
8
|
+
unless Config::CONFIG['host_os'] =~ /mswin|win32|msdos|cygwin|mingw/i
|
9
|
+
require 'resolv-replace'
|
10
|
+
end
|
11
|
+
|
12
|
+
# The Net module serves as a namespace only.
|
13
|
+
module Net
|
14
|
+
|
15
|
+
# The Ping::HTTP class encapsulates methods for HTTP pings.
|
16
|
+
class Ping::HTTP < Ping
|
17
|
+
|
18
|
+
# By default an http ping will follow a redirect and give you the result
|
19
|
+
# of the final URI. If this value is set to false, then it will not
|
20
|
+
# follow a redirect and will return false immediately on a redirect.
|
21
|
+
#
|
22
|
+
attr_accessor :follow_redirect
|
23
|
+
|
24
|
+
# Creates and returns a new Ping::HTTP object. Note that the default
|
25
|
+
# port for Ping::HTTP is 80.
|
26
|
+
#
|
27
|
+
def initialize(uri=nil, port=80, timeout=5)
|
28
|
+
@follow_redirect = true
|
29
|
+
super(uri, port, timeout)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Looks for an HTTP response from the URI passed to the constructor.
|
33
|
+
# If the result is a kind of Net::HTTPSuccess then the ping was
|
34
|
+
# successful and true is returned. Otherwise, false is returned
|
35
|
+
# and the Ping::HTTP#exception method should contain a string
|
36
|
+
# indicating what went wrong.
|
37
|
+
#
|
38
|
+
# If the HTTP#follow_redirect accessor is set to true (which it is
|
39
|
+
# by default) and a redirect occurs during the ping, then the
|
40
|
+
# HTTP#warning attribute is set to the redirect message, but the
|
41
|
+
# return result is still true. If it's set to false then a redirect
|
42
|
+
# response is considered a failed ping.
|
43
|
+
#
|
44
|
+
# If no file or path is specified in the URI, then '/' is assumed.
|
45
|
+
#
|
46
|
+
def ping(host = @host)
|
47
|
+
super(host)
|
48
|
+
bool = false
|
49
|
+
uri = URI.parse(host)
|
50
|
+
|
51
|
+
start_time = Time.now
|
52
|
+
|
53
|
+
begin
|
54
|
+
response = nil
|
55
|
+
uri_path = uri.path.empty? ? '/' : uri.path
|
56
|
+
Timeout.timeout(@timeout){
|
57
|
+
response = Net::HTTP.get_response(uri.host, uri_path, @port)
|
58
|
+
}
|
59
|
+
rescue Exception => err
|
60
|
+
@exception = err.message
|
61
|
+
else
|
62
|
+
if response.is_a?(Net::HTTPSuccess)
|
63
|
+
bool = true
|
64
|
+
else
|
65
|
+
if @follow_redirect
|
66
|
+
@warning = response.message
|
67
|
+
|
68
|
+
while response.is_a?(Net::HTTPRedirection)
|
69
|
+
redirect = URI.parse(response['location'])
|
70
|
+
redirect = uri + redirect if redirect.relative?
|
71
|
+
response = Net::HTTP.get_response(redirect.host, redirect.path, @port)
|
72
|
+
end
|
73
|
+
|
74
|
+
if response.is_a?(Net::HTTPSuccess)
|
75
|
+
bool = true
|
76
|
+
else
|
77
|
+
@warning = nil
|
78
|
+
@exception = response.message
|
79
|
+
end
|
80
|
+
else
|
81
|
+
@exception = response.message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# There is no duration if the ping failed
|
87
|
+
@duration = Time.now - start_time if bool
|
88
|
+
|
89
|
+
bool
|
90
|
+
end
|
91
|
+
|
92
|
+
alias ping? ping
|
93
|
+
alias pingecho ping
|
94
|
+
alias follow_redirect? follow_redirect
|
95
|
+
alias uri host
|
96
|
+
alias uri= host=
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'ping')
|
2
|
+
|
3
|
+
# The Net module serves as a namespace only.
|
4
|
+
module Net
|
5
|
+
|
6
|
+
# The Net::Ping::ICMP class encapsulates an icmp ping.
|
7
|
+
class Ping::ICMP < Ping
|
8
|
+
ICMP_ECHOREPLY = 0 # Echo reply
|
9
|
+
ICMP_ECHO = 8 # Echo request
|
10
|
+
ICMP_SUBCODE = 0
|
11
|
+
|
12
|
+
# You cannot set or change the port value. A value of 0 is always
|
13
|
+
# used internally for ICMP pings.
|
14
|
+
#
|
15
|
+
undef_method :port=
|
16
|
+
|
17
|
+
# Returns the data size, i.e. number of bytes sent on the ping. The
|
18
|
+
# default size is 56.
|
19
|
+
#
|
20
|
+
attr_reader :data_size
|
21
|
+
|
22
|
+
# Creates and returns a new Ping::ICMP object. This is similar to its
|
23
|
+
# superclass constructor, but must be created with root privileges (on
|
24
|
+
# UNIX systems), and the port value is ignored.
|
25
|
+
#
|
26
|
+
def initialize(host=nil, port=nil, timeout=5)
|
27
|
+
raise 'requires root privileges' if Process.euid > 0
|
28
|
+
|
29
|
+
@seq = 0
|
30
|
+
@bind_port = 0
|
31
|
+
@bind_host = nil
|
32
|
+
@data_size = 56
|
33
|
+
@data = ''
|
34
|
+
|
35
|
+
0.upto(@data_size){ |n| @data << (n % 256).chr }
|
36
|
+
|
37
|
+
@pid = Process.pid & 0xffff
|
38
|
+
|
39
|
+
super(host, port, timeout)
|
40
|
+
@port = nil # This value is not used in ICMP pings.
|
41
|
+
end
|
42
|
+
|
43
|
+
# Sets the number of bytes sent in the ping method.
|
44
|
+
#
|
45
|
+
def data_size=(size)
|
46
|
+
@data_size = size
|
47
|
+
@data = ''
|
48
|
+
0.upto(size){ |n| @data << (n % 256).chr }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Associates the local end of the socket connection with the given
|
52
|
+
# +host+ and +port+. The default port is 0.
|
53
|
+
#
|
54
|
+
def bind(host, port = 0)
|
55
|
+
@bind_host = host
|
56
|
+
@bind_port = port
|
57
|
+
end
|
58
|
+
|
59
|
+
# Pings the +host+ specified in this method or in the constructor. If a
|
60
|
+
# host was not specified either here or in the constructor, an
|
61
|
+
# ArgumentError is raised.
|
62
|
+
#
|
63
|
+
def ping(host = @host)
|
64
|
+
super(host)
|
65
|
+
bool = false
|
66
|
+
|
67
|
+
socket = Socket.new(
|
68
|
+
Socket::PF_INET,
|
69
|
+
Socket::SOCK_RAW,
|
70
|
+
Socket::IPPROTO_ICMP
|
71
|
+
)
|
72
|
+
|
73
|
+
if @bind_host
|
74
|
+
saddr = Socket.pack_sockaddr_in(@bind_port, @bind_host)
|
75
|
+
socket.bind(saddr)
|
76
|
+
end
|
77
|
+
|
78
|
+
@seq = (@seq + 1) % 65536
|
79
|
+
pstring = 'C2 n3 A' << @data_size.to_s
|
80
|
+
timeout = @timeout
|
81
|
+
|
82
|
+
checksum = 0
|
83
|
+
msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)
|
84
|
+
checksum = checksum(msg)
|
85
|
+
msg = [ICMP_ECHO, ICMP_SUBCODE, checksum, @pid, @seq, @data].pack(pstring)
|
86
|
+
|
87
|
+
begin
|
88
|
+
saddr = Socket.pack_sockaddr_in(0, host)
|
89
|
+
rescue Exception
|
90
|
+
socket.close unless socket.closed?
|
91
|
+
return bool
|
92
|
+
end
|
93
|
+
|
94
|
+
start_time = Time.now
|
95
|
+
|
96
|
+
socket.send(msg, 0, saddr) # Send the message
|
97
|
+
|
98
|
+
begin
|
99
|
+
Timeout.timeout(@timeout){
|
100
|
+
io_array = select([socket], nil, nil, timeout)
|
101
|
+
|
102
|
+
if io_array.nil? || io_array[0].empty?
|
103
|
+
return false
|
104
|
+
end
|
105
|
+
|
106
|
+
pid = nil
|
107
|
+
seq = nil
|
108
|
+
|
109
|
+
data, sender = socket.recvfrom(1500)
|
110
|
+
port, host = Socket.unpack_sockaddr_in(sender)
|
111
|
+
type, subcode = data[20, 2].unpack('C2')
|
112
|
+
|
113
|
+
case type
|
114
|
+
when ICMP_ECHOREPLY
|
115
|
+
if data.length >= 28
|
116
|
+
pid, seq = data[24, 4].unpack('n3')
|
117
|
+
end
|
118
|
+
else
|
119
|
+
if data.length > 56
|
120
|
+
pid, seq = data[52, 4].unpack('n3')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if pid == @pid && seq == @seq && type == ICMP_ECHOREPLY
|
125
|
+
bool = true
|
126
|
+
end
|
127
|
+
}
|
128
|
+
rescue Exception => err
|
129
|
+
@exception = err
|
130
|
+
ensure
|
131
|
+
socket.close if socket
|
132
|
+
end
|
133
|
+
|
134
|
+
# There is no duration if the ping failed
|
135
|
+
@duration = Time.now - start_time if bool
|
136
|
+
|
137
|
+
return bool
|
138
|
+
end
|
139
|
+
|
140
|
+
alias ping? ping
|
141
|
+
alias pingecho ping
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# Perform a checksum on the message. This is the sum of all the short
|
146
|
+
# words and it folds the high order bits into the low order bits.
|
147
|
+
#
|
148
|
+
def checksum(msg)
|
149
|
+
length = msg.length
|
150
|
+
num_short = length / 2
|
151
|
+
check = 0
|
152
|
+
|
153
|
+
msg.unpack("n#{num_short}").each do |short|
|
154
|
+
check += short
|
155
|
+
end
|
156
|
+
|
157
|
+
if length % 2 > 0
|
158
|
+
check += msg[length-1, 1].unpack('C').first << 8
|
159
|
+
end
|
160
|
+
|
161
|
+
check = (check >> 16) + (check & 0xffff)
|
162
|
+
return (~((check >> 16) + check) & 0xffff)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|