rping 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +38 -0
- data/bin/rping +37 -0
- data/lib/rping.rb +154 -0
- metadata +67 -0
data/README
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= rping
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
rping is a ruby implementation of ping.
|
6
|
+
|
7
|
+
== Source Code
|
8
|
+
|
9
|
+
https://bitbucket.org/winebarrel/rping
|
10
|
+
|
11
|
+
|
12
|
+
== Example
|
13
|
+
=== Command
|
14
|
+
|
15
|
+
shell> rping
|
16
|
+
Usage: rping [options]
|
17
|
+
-c COUNT
|
18
|
+
-i INTERVAL
|
19
|
+
-w TIMEOUT
|
20
|
+
-d DESTINATION
|
21
|
+
shell> rping -c 3 -d localhost
|
22
|
+
PING localhost
|
23
|
+
36 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=128 time=1.0 ms
|
24
|
+
36 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=128 time=0.0 ms
|
25
|
+
36 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=128 time=0.0 ms
|
26
|
+
|
27
|
+
== Library
|
28
|
+
|
29
|
+
require 'lib/rping'
|
30
|
+
|
31
|
+
p RPing.ping('localhost')
|
32
|
+
#=> [{:time=>0.0, :dest=>"127.0.0.1", :src=>"127.0.0.1", :ttl=>128, :size=>36, :seq=>1}]
|
33
|
+
|
34
|
+
RPing.ping('127.0.0.1', :count => 3) do |reply|
|
35
|
+
p reply
|
36
|
+
#=> {:time=>0.0, :src=>"127.0.0.1", :dest=>"127.0.0.1", :ttl=>128, :size=>36, :seq=>1}
|
37
|
+
end
|
38
|
+
|
data/bin/rping
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'lib/rping'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
$stdout.sync = true
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
dest = nil
|
12
|
+
|
13
|
+
ARGV.options do |opt|
|
14
|
+
opt.on('-c COUNT') {|v| options[:count] = v.to_i }
|
15
|
+
opt.on('-i INTERVAL') {|v| options[:interval] = v.to_i }
|
16
|
+
opt.on('-w TIMEOUT') {|v| options[:timeout] = v.to_i }
|
17
|
+
opt.on('-d DESTINATION') {|v| dest = v }
|
18
|
+
|
19
|
+
opt.parse!
|
20
|
+
|
21
|
+
unless dest
|
22
|
+
puts opt.help
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
puts "PING #{dest}"
|
28
|
+
|
29
|
+
RPing.ping(dest, options) do |r|
|
30
|
+
if r
|
31
|
+
puts <<-EOS
|
32
|
+
#{r[:size]} bytes from #{dest} (#{r[:dest]}): icmp_seq=#{r[:seq]} ttl=#{r[:ttl]} time=#{'%.1f' % r[:time]} ms
|
33
|
+
EOS
|
34
|
+
else
|
35
|
+
puts 'packet loss'
|
36
|
+
end
|
37
|
+
end
|
data/lib/rping.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
class RPing
|
5
|
+
MAX_LEN = 64 * 1024
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@count = options.fetch(:count, 1)
|
9
|
+
@interval = options.fetch(:interval, 1)
|
10
|
+
@timeout = options.fetch(:timeout, 4)
|
11
|
+
@icmp_id = Process.pid & 0xffff
|
12
|
+
@seq_num = 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.ping(dest, options = {}, &block)
|
16
|
+
self.new(options).ping(dest, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ping(addr, &block)
|
20
|
+
unless addr =~ /\A\d{3}\.\d{3}\.\d{3}\.\d{3}\Z/
|
21
|
+
addr = IPSocket.getaddress(addr)
|
22
|
+
end
|
23
|
+
|
24
|
+
sock = nil
|
25
|
+
buf = []
|
26
|
+
|
27
|
+
begin
|
28
|
+
sock = make_socket
|
29
|
+
th = ping_recv(sock, buf, &block)
|
30
|
+
ping_send(sock, addr)
|
31
|
+
th.join
|
32
|
+
ensure
|
33
|
+
sock.close if sock
|
34
|
+
end
|
35
|
+
|
36
|
+
return buf
|
37
|
+
end
|
38
|
+
|
39
|
+
def make_socket
|
40
|
+
Socket.new(Socket::AF_INET, Socket::SOCK_RAW, Socket::IPPROTO_ICMP)
|
41
|
+
end
|
42
|
+
|
43
|
+
def ping_send(sock, addr)
|
44
|
+
sockaddr = Socket.pack_sockaddr_in(0, addr)
|
45
|
+
|
46
|
+
@count.times do |i|
|
47
|
+
sleep @interval unless i.zero?
|
48
|
+
req = make_echo_request
|
49
|
+
sock.send(req, 0, sockaddr)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def ping_recv(sock, buf = nil, &block)
|
54
|
+
Thread.start do
|
55
|
+
@count.times do
|
56
|
+
reply = ping_recv0(sock)
|
57
|
+
yield(reply) if block
|
58
|
+
buf << reply if buf
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def ping_recv0(sock)
|
65
|
+
reply = nil
|
66
|
+
|
67
|
+
begin
|
68
|
+
timeout(@timeout) do
|
69
|
+
if select([sock], nil, nil, @timeout)
|
70
|
+
msg = sock.recv(MAX_LEN)
|
71
|
+
recv_time = Time.now.to_f
|
72
|
+
ip, icmp = unpack_echo_reply(msg)
|
73
|
+
|
74
|
+
# icmp[0] == 0: Type == Echo Reply
|
75
|
+
if icmp[0] == 0 and icmp[3] == @icmp_id
|
76
|
+
reply = {
|
77
|
+
:dest => ip[8].bytes.to_a.join('.'),
|
78
|
+
:src => ip[9].bytes.to_a.join('.'),
|
79
|
+
:size => msg.length,
|
80
|
+
:ttl => ip[5],
|
81
|
+
:seq => icmp[4],
|
82
|
+
:time => (recv_time - icmp[5]) * 1000,
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
rescue Timeout::Error
|
88
|
+
end
|
89
|
+
|
90
|
+
return reply
|
91
|
+
end
|
92
|
+
|
93
|
+
def make_echo_request
|
94
|
+
data = Time.now.to_f
|
95
|
+
|
96
|
+
req = [
|
97
|
+
[ 8, :C], # Type = Echo
|
98
|
+
[ 0, :C], # Code = 0
|
99
|
+
[ 0, :n], # Checksum
|
100
|
+
[@icmp_id, :n], # Identification
|
101
|
+
[@seq_num, :n], # Sequence Number
|
102
|
+
[ data, :d], # Data
|
103
|
+
]
|
104
|
+
|
105
|
+
@seq_num += 1
|
106
|
+
req = req.map {|i| i[0] }.pack(req.map {|i| i[1] }.join)
|
107
|
+
update_checksum(req)
|
108
|
+
|
109
|
+
return req
|
110
|
+
end
|
111
|
+
|
112
|
+
def update_checksum(buf)
|
113
|
+
sum = 0
|
114
|
+
buf.unpack('n*').each {|i| sum += i }
|
115
|
+
|
116
|
+
unless (buf.length % 2).zero?
|
117
|
+
sum += buf[-1].unpack('n')[0]
|
118
|
+
end
|
119
|
+
|
120
|
+
sum = (sum & 0xffff) + (sum >> 16);
|
121
|
+
sum = (sum & 0xffff) + (sum >> 16);
|
122
|
+
|
123
|
+
buf[2, 2] = [~sum & 0xffff].pack('n')
|
124
|
+
end
|
125
|
+
|
126
|
+
def unpack_echo_reply(buf)
|
127
|
+
tmpl = [
|
128
|
+
# IP
|
129
|
+
:C, # Version, IHL
|
130
|
+
:C, # Type of Service
|
131
|
+
:n, # Total Length
|
132
|
+
:n, # Identification
|
133
|
+
:n, # Flags, Fragment Offset
|
134
|
+
:C, # Time to Live,
|
135
|
+
:C, # Protocol
|
136
|
+
:n, # Header Checksum
|
137
|
+
:a4, # Source Address
|
138
|
+
:a4, # Destination Address
|
139
|
+
# ICMP
|
140
|
+
:C, # Type
|
141
|
+
:C, # Code
|
142
|
+
:n, # Checksum
|
143
|
+
:n, # Identification
|
144
|
+
:n, # Sequence Number
|
145
|
+
:d, # Data
|
146
|
+
]
|
147
|
+
|
148
|
+
reply = buf.unpack(tmpl.join)
|
149
|
+
ip = reply.slice(0, 19)
|
150
|
+
icmp = reply.slice(10, 6)
|
151
|
+
|
152
|
+
return [ip, icmp]
|
153
|
+
end
|
154
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rping
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- winebarrel
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-01-21 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email: sgwr_dts@yahoo.co.jp
|
23
|
+
executables:
|
24
|
+
- rping
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- README
|
31
|
+
- bin/rping
|
32
|
+
- lib/rping.rb
|
33
|
+
homepage: https://bitbucket.org/winebarrel/rping
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 3
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
version: "0"
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.11
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: rping is a ruby implementation of ping.
|
66
|
+
test_files: []
|
67
|
+
|