rping 0.1.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.
Files changed (4) hide show
  1. data/README +38 -0
  2. data/bin/rping +37 -0
  3. data/lib/rping.rb +154 -0
  4. 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
+
@@ -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
@@ -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
+