rping 0.1.0

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