DIY-pcap 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ DIY-pcap
2
+ =============
3
+
4
+ ## 这是什么?
5
+
6
+ 自定义发包工具,可以轻易实现包交互, 实现是来自 [ffi-pcap](https://github.com/sophsec/ffi-pcap/) 的二次封装.
7
+
8
+ ## 使用方法,完全自定义发包 ( 一 )
9
+
10
+ 1. 安装很简单
11
+
12
+ ```
13
+ gem install DIY-pcap
14
+ ```
15
+
16
+ 服务端与本机需要同时安装.
17
+
18
+ 2. 准备好要发送和接收的数据放在 `pcaps` 目录下, 创建文件 spec.rb:
19
+
20
+ ```ruby
21
+ pcap do |s|
22
+ s.dir = "pcaps"
23
+ s.send "r1.dat"
24
+ s.recv "s1.dat"
25
+ s.recv "s2.dat"
26
+ s.send "r2.dat"
27
+ end
28
+ ```
29
+
30
+ 上面的意思是, 从本机发送 `r1.dat` 到 服务端, 并等待接收 `s1.dat`, `s2.dat` 数据包, 之后再发送 `r2.dat`, 最后结束.
31
+ 更多内容请参考: simple/ 里面的内容.
32
+
33
+ 3. 开始发送与接收数据
34
+
35
+ * 服务端,执行 `rpcap spec.rb`
36
+
37
+ * 本机, 执行 `pcap spec.rb`
38
+
39
+ ## 使用方法, 回放pcap报文 ( 二 )
40
+
41
+ 1. 安装同上
42
+
43
+ 2. 准备好 pcap 文件放在 `pcaps/simple.pcap` 目录下, 创建文件 spec.rb:
44
+
45
+ ```ruby
46
+ DIY::Builder.new do
47
+ pcapfile "pcaps/simple.pcap"
48
+ use SimpleStrategy.new
49
+ end
50
+ ```
51
+ 3. 同上
52
+
53
+ 4. (其他说明) 扩展策略, 自定义日志, 修改报文内容.
54
+
55
+ OK, 祝你好运.
data/bin/pcap CHANGED
@@ -1,5 +1,5 @@
1
1
  $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib' )
2
- require 'diy/pcap'
2
+ require 'diy-pcap'
3
3
  require 'diy/task'
4
4
 
5
5
  if ARGV[0].nil?
data/bin/rpcap CHANGED
@@ -1,5 +1,5 @@
1
1
  $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib' )
2
- require 'diy/pcap'
2
+ require 'diy-pcap'
3
3
  require 'diy/task'
4
4
 
5
5
  $SERVER = true
data/lib/DIY-pcap.rb CHANGED
@@ -1,2 +1,3 @@
1
1
  require "diy/version"
2
2
  require 'diy/pcap'
3
+ require 'diy/dig'
data/lib/diy/dig.rb ADDED
@@ -0,0 +1,337 @@
1
+ # encoding : utf-8
2
+ module DIY
3
+
4
+ class Error < RuntimeError; end
5
+ # 数据包读取完毕
6
+ class EOFError < Error; end
7
+
8
+ class Recver
9
+ def initialize(live)
10
+ @live = live
11
+ @watchers = []
12
+ end
13
+
14
+ def run
15
+ @live.loop do |this, pkt|
16
+ notify_recv_pkt(pkt)
17
+ end
18
+ end
19
+
20
+ def stop
21
+ @live.stop
22
+ end
23
+
24
+ def notify_recv_pkt(pkt)
25
+ @watchers.each do |watcher|
26
+ watcher.recv_pkt(pkt.body)
27
+ end
28
+ end
29
+
30
+ def add_watcher(watcher)
31
+ @watchers = [] unless @watchers
32
+ @watchers << watcher
33
+ end
34
+
35
+ def del_watcher(watcher)
36
+ @watchers.delete(watcher)
37
+ end
38
+ end
39
+
40
+ class Sender
41
+ def initialize(live)
42
+ @live = live
43
+ end
44
+
45
+ def inject(pkt)
46
+ puts "send: #{Time.now}"
47
+ @live.inject(pkt)
48
+ end
49
+ end
50
+
51
+ require 'thread'
52
+ require 'timeout'
53
+ class Queue
54
+
55
+ def initialize(offline)
56
+ @expect_recv_queue = []
57
+ @offline = offline
58
+ @m = Mutex.new
59
+ # 暂存 next_send_pkt 数据
60
+ @tmp_send_pkt = nil
61
+ end
62
+
63
+ def expect_recv_queue
64
+ @expect_recv_queue
65
+ end
66
+
67
+ def pop
68
+ return nil if @expect_recv_queue.empty?
69
+ @m.synchronize {
70
+ return @expect_recv_queue.shift
71
+ }
72
+ end
73
+
74
+ def delete(what)
75
+ if @expect_recv_queue.include?(what)
76
+ @m.synchronize {
77
+ if @expect_recv_queue.include?(what)
78
+ return @expect_recv_queue.delete(what)
79
+ end
80
+ }
81
+ end
82
+ return nil
83
+ end
84
+
85
+ def delete_at(index)
86
+ @m.synchronize {
87
+ return @expect_recv_queue.delete_at(index)
88
+ }
89
+ end
90
+
91
+ def peek
92
+ return nil if @expect_recv_queue.empty?
93
+ @expect_recv_queue[0]
94
+ end
95
+
96
+ # 处理发送报文
97
+ #
98
+ # 等待接受报文完成后, 返回发送报文, 并重新填充接受报文
99
+ # TODO: 支持多个pcap文件
100
+ def next_send_pkt(&block)
101
+ wait_until { @expect_recv_queue.empty? }
102
+ if @tmp_send_pkt
103
+ pkt = @tmp_send_pkt
104
+ @tmp_send_pkt = nil
105
+ else
106
+ pkt = write_recv_pkt
107
+ wait_until { @expect_recv_queue.empty? }
108
+ end
109
+ raise EOFError, " no pkt to send" unless pkt
110
+ pkt = pkt.copy
111
+
112
+ recv_pkt = write_recv_pkt
113
+
114
+ yield(pkt.body) if block_given?
115
+
116
+ @tmp_send_pkt = recv_pkt.copy if recv_pkt
117
+ pkt.body
118
+ end
119
+ alias_method :next, :next_send_pkt
120
+
121
+ def write_recv_pkt
122
+ while ( (recv_pkt = @offline.next) && ( set_first_gout(recv_pkt.body); comein?(recv_pkt.body) ) )
123
+ @m.synchronize {
124
+ @expect_recv_queue << recv_pkt.copy.body
125
+ }
126
+ end
127
+ recv_pkt
128
+ end
129
+
130
+ def do_loop(&block)
131
+ raise "Must give me block" unless block_given?
132
+ while(true) do
133
+ next_send_pkt(&block)
134
+ end
135
+ end
136
+
137
+ def set_first_gout(pkt)
138
+ return @src_mac if @src_mac
139
+ if pkt.size < 12
140
+ raise PktError,"can't find src mac: error format packet"
141
+ end
142
+ @src_mac = pkt[6..11]
143
+ end
144
+
145
+ def comein?(pkt)
146
+ ret = judge_direct(pkt) do | pkt_mac, src_mac|
147
+ (pkt_mac != src_mac) ^ server?
148
+ end
149
+ ret
150
+ end
151
+
152
+ def gout?(pkt)
153
+ judge_direct(pkt) do | pkt_mac, src_mac|
154
+ (pkt_mac == src_mac) ^ server?
155
+ end
156
+ end
157
+
158
+ def server?
159
+ $SERVER
160
+ end
161
+
162
+ def judge_direct(pkt,&block)
163
+ if pkt.size < 12
164
+ raise PktError,"can't find src mac: error format packet"
165
+ end
166
+ raise "src_mac not set" unless @src_mac
167
+ yield( pkt[6..11], @src_mac )
168
+ end
169
+
170
+ def wait_until( timeout = 20, &block )
171
+ timeout(timeout) do
172
+ loop do
173
+ break if block.call
174
+ sleep 0.01
175
+ end
176
+ end
177
+ end
178
+
179
+ end
180
+
181
+ # 这个策略是一个最基本的:
182
+ # 具体返回值含义见 @BasicStrategy
183
+ class Strategy
184
+ OK = true
185
+ OK_NO_POP = 1
186
+ FAIL = false
187
+ NONE = nil
188
+ end
189
+
190
+ class BasicStrategy < Strategy
191
+
192
+ # @argument:
193
+ # hope_pkt: 期望的报文
194
+ # recv_pkt: 接收的报文
195
+ # queue: 期望接收队列, 如果期望乱序时,你可以使用这个参数
196
+ #
197
+ # @return:
198
+ # OK : 匹配, 可以进行下一个报文的处理
199
+ # OK_NO_POP: 匹配了接收队列中的报文, 但是不需要框架自动pop掉期望报文( 注意, 你需要自行处于报文 )
200
+ # FAIL: 肯定失败时使用
201
+ # NONE: 不匹配, 让框架进行下一个报文匹配
202
+ def call(hope_pkt, recv_pkt, queue)
203
+ raise "write code here"
204
+ end
205
+ end
206
+
207
+ class SimpleStrategy < BasicStrategy
208
+ def call(hope_pkt, recv_pkt, queue)
209
+ if hope_pkt == recv_pkt
210
+ return OK
211
+ else
212
+ return NONE
213
+ end
214
+ end
215
+ end
216
+
217
+ require 'logger'
218
+ class StrategyBuilder
219
+ def initialize(queue)
220
+ @ins = []
221
+ @logger = Logger.new(STDOUT)
222
+ @queue = queue
223
+ end
224
+ attr_reader :queue
225
+
226
+ def add(strategy)
227
+ @ins << strategy
228
+ end
229
+ alias << add
230
+
231
+ def logger=(logger)
232
+ @logger = logger
233
+ end
234
+
235
+ def logger
236
+ @logger
237
+ end
238
+
239
+ def recv_pkt(pkt)
240
+ recv_pkt_queue(queue,pkt)
241
+ end
242
+
243
+ def recv_pkt_queue(queue, recv_pkt)
244
+ hope_pkt = queue.peek
245
+ logger.debug("recv_pkt, I hope: #{ hope_pkt[0..10].dump rescue nil }...")
246
+ return if hope_pkt.nil?
247
+ @ins.each do |strategy|
248
+ begin
249
+ ret = strategy.call(hope_pkt, recv_pkt, queue)
250
+ rescue Exception => e
251
+ logger.error("strategy call exception: #{e.class} -> #{e.message}")
252
+ raise
253
+ #仅仅忽略
254
+ else
255
+ if ret == Strategy::OK
256
+ logger.info("pkt same:")
257
+ queue.pop
258
+ return
259
+ elsif ret == Strategy::OK_NO_POP
260
+ logger.info("pkt same but no pop:")
261
+ return
262
+ elsif ret == Strategy::FAIL
263
+ logger.warn("pkt fail:")
264
+ elsif ret == Strategy::NONE
265
+ logger.debug("pkt jumpped:")
266
+ next
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+
274
+ class Controller
275
+ def initialize(live, offline, strategy)
276
+ @live = live
277
+ @recver = Recver.new(@live)
278
+ @recver.add_watcher(strategy)
279
+ @recver_t = nil
280
+ @sender = Sender.new(@live)
281
+ @queue = strategy.queue
282
+ @logger = Logger.new(STDOUT)
283
+ end
284
+ attr_accessor :logger
285
+
286
+ def run
287
+ @recver_t = Thread.new do
288
+ @recver.run
289
+ end
290
+
291
+ begin
292
+ @queue.do_loop do |pkt|
293
+ @sender.inject(pkt)
294
+ end
295
+ @recver_t.join
296
+ rescue EOFError
297
+ @recver.stop
298
+ end
299
+ end
300
+
301
+ end
302
+
303
+ class Builder
304
+ def initialize(&block)
305
+ @strategies = []
306
+ instance_eval(&block)
307
+ end
308
+
309
+ def find_device
310
+ @device_name ||= FFI::PCap.dump_devices[0][0]
311
+ @live = FFI::PCap::Live.new(:dev=>@device_name, :handler => FFI::PCap::Handler, :promisc => true)
312
+ end
313
+
314
+ def device(name)
315
+ @device_name = name
316
+ end
317
+
318
+ def use(what)
319
+ @strategies.unshift(what)
320
+ end
321
+
322
+ def pcapfile(pcaps)
323
+ @offline = FFI::PCap::Offline.new(pcaps)
324
+ end
325
+
326
+ def run
327
+ @offline ||= FFI::PCap::Offline.new('pcaps/example.pcap')
328
+ @queue = Queue.new(@offline)
329
+ @strategy_builder = DIY::StrategyBuilder.new(@queue)
330
+ @strategies.each { |builder| @strategy_builder.add(builder) }
331
+ find_device
332
+ controller = Controller.new( @live, @offline, @strategy_builder )
333
+ controller.run
334
+ end
335
+
336
+ end
337
+ end
data/lib/diy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module DIY
2
2
  class PCAP
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
data/simple/cmd-pcap.rb CHANGED
@@ -1,5 +1,11 @@
1
+ # encoding : utf-8
2
+ # 这是一个单个包交互发送的例子
3
+ # 安装 DIY-pcap 后, 先启动服务端 `rpcap cmd-pcap`, 服务端会开始等待接收包文.
4
+ # 启动客户端 `pcap cmd-pcap`
5
+ # 等待收包完成, 如果出现超时, 说明中间数据不一致, 被修改了.
6
+
1
7
  pcap do |s|
2
- s.dir = File.join( File.dirname(__FILE__), 'pcaps')
8
+ s.dir = 'pcaps'
3
9
  s.send("r1.dat")
4
10
  s.recv("s1.dat")
5
11
  s.recv("s2.dat")
data/simple/pcap.rb ADDED
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib')
2
+ require 'rubygems'
3
+ require 'diy-pcap'
4
+ require 'diy/dig'
5
+
6
+ ss = DIY::SimpleStrategy.new
7
+
8
+ a = DIY::Builder.new do
9
+ use ss
10
+ pcapfile "pcaps/gre.pcap"
11
+ end
12
+
13
+ a.run
Binary file
data/spec/dig_spec.rb ADDED
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+ describe DIY::Queue do
3
+
4
+ before(:each) do
5
+ @device_name = FFI::PCap.dump_devices[0][0]
6
+ @live = FFI::PCap::Live.new(:dev=>@device_name, :handler => FFI::PCap::Handler, :promisc => true)
7
+ @offline = FFI::PCap::Offline.new('../simple/pcaps/gre.pcap')
8
+ end
9
+
10
+ it "#next_send_pkt" do
11
+ $SERVER = nil
12
+ q = DIY::Queue.new(@offline)
13
+ q.stub(:wait_until).and_return(true)
14
+ q.next_send_pkt.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt1' ) )
15
+ pkt1 = q.instance_variable_get("@expect_recv_queue")[0]
16
+ pkt2 = q.instance_variable_get("@expect_recv_queue")[1]
17
+ q.instance_variable_get("@expect_recv_queue").size.should == 2
18
+ pkt1.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )
19
+ pkt2.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt3' ) )
20
+ q.instance_variable_set("@expect_recv_queue", [])
21
+ q.next_send_pkt.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt4' ) )
22
+ q.instance_variable_get("@expect_recv_queue").should == []
23
+ lambda { loop { q.next_send_pkt } }.should raise_error
24
+ end
25
+
26
+ it "#next_send_pkt server" do
27
+ $SERVER = true
28
+ q = DIY::Queue.new(@offline)
29
+ q.stub(:wait_until).and_return(true)
30
+ q.next_send_pkt.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )
31
+ q.instance_variable_get("@expect_recv_queue").should == [ File.read( File.join( File.dirname(__FILE__), 'helper/pkt1' ) ) ]
32
+ q.instance_variable_set("@expect_recv_queue", [])
33
+ q.next_send_pkt.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt3' ) )
34
+ q.instance_variable_get("@expect_recv_queue").should == [ File.read( File.join( File.dirname(__FILE__), 'helper/pkt4' )), File.read( File.join( File.dirname(__FILE__), 'helper/pkt5' )) ]
35
+ $SERVER = nil
36
+ end
37
+
38
+ it "#comein? server" do
39
+ $SERVER = true
40
+ q = DIY::Queue.new(@offline)
41
+ pkt = File.read( File.join( File.dirname(__FILE__), 'helper/pkt1' ) )
42
+ pkt2 = File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )
43
+ q.set_first_gout(pkt)
44
+ q.set_first_gout(pkt2).should == pkt[6..11]
45
+ q.comein?(pkt).should == true
46
+ q.comein?(pkt2).should == false
47
+ $SERVER = nil
48
+ end
49
+
50
+ it "#peek #pop" do
51
+ q = DIY::Queue.new(@offline)
52
+ q.stub(:wait_until).and_return(true)
53
+ q.next_send_pkt
54
+ q.peek.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )
55
+ q.pop.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )
56
+ q.peek.should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt3' ) )
57
+ q.pop
58
+ q.peek.should == nil
59
+ end
60
+
61
+ it "#delete" do
62
+ q = DIY::Queue.new(@offline)
63
+ q.stub(:wait_until).and_return(true)
64
+ q.next_send_pkt
65
+ q.delete(File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )).should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )
66
+ q.pop
67
+ q.peek.should == nil
68
+ end
69
+
70
+ it "#delete_at" do
71
+ q = DIY::Queue.new(@offline)
72
+ q.stub(:wait_until).and_return(true)
73
+ q.next_send_pkt
74
+ q.delete_at(0).should == File.read( File.join( File.dirname(__FILE__), 'helper/pkt2' ) )
75
+ end
76
+
77
+ end
78
+
79
+ describe DIY::Controller do
80
+ before(:each) do
81
+ @device_name = FFI::PCap.dump_devices[0][0]
82
+ @live = FFI::PCap::Live.new(:dev=>@device_name, :handler => FFI::PCap::Handler, :promisc => true)
83
+ @offline = FFI::PCap::Offline.new('../simple/pcaps/gre.pcap')
84
+ end
85
+
86
+ it "#run" do
87
+ q = nil
88
+ server = Thread.new do
89
+ q = DIY::Queue.new(@offline)
90
+ q.stub(:server?).and_return(true)
91
+ DIY::Queue.stub(:new).and_return(q)
92
+ s = DIY::Controller.new(@live, @offline)
93
+ s.run
94
+ end
95
+ device_name = FFI::PCap.dump_devices[0][0]
96
+ live = FFI::PCap::Live.new(:dev=>@device_name, :handler => FFI::PCap::Handler, :promisc => true)
97
+ offline = FFI::PCap::Offline.new('../simple/pcaps/gre.pcap')
98
+ c = DIY::Controller.new(live, offline)
99
+ q.wait_until { q.peek != nil }
100
+ lambda { c.run ; server.join }.should_not raise_error
101
+ end
102
+
103
+ end
data/spec/helper/pkt1 ADDED
Binary file
data/spec/helper/pkt2 ADDED
Binary file
data/spec/helper/pkt3 ADDED
Binary file
data/spec/helper/pkt4 ADDED
Binary file
data/spec/helper/pkt5 ADDED
Binary file
data/spec/spec_helper.rb CHANGED
@@ -1,17 +1,2 @@
1
- # This file was generated by the `rspec --init` command. Conventionally, all
2
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # Require this file using `require "spec_helper"` to ensure that it is only
4
- # loaded once.
5
- #
6
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
- RSpec.configure do |config|
8
- config.treat_symbols_as_metadata_keys_with_true_values = true
9
- config.run_all_when_everything_filtered = true
10
- config.filter_run :focus
11
-
12
- # Run specs in random order to surface order dependencies. If you find an
13
- # order dependency and want to debug it, you can fix the order by providing
14
- # the seed, which is printed after each run.
15
- # --seed 1234
16
- config.order = 'random'
17
- end
1
+ $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib' )
2
+ require 'DIY-pcap'
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - yafei Lee
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-08-30 00:00:00 +08:00
17
+ date: 2012-09-10 00:00:00 +08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -47,16 +47,18 @@ files:
47
47
  - .rspec
48
48
  - DIY-pcap.gemspec
49
49
  - Gemfile
50
+ - README.md
50
51
  - Rakefile
51
52
  - bin/pcap
52
53
  - bin/rpcap
53
54
  - lib/DIY-pcap.rb
55
+ - lib/diy/dig.rb
54
56
  - lib/diy/pcap.rb
55
- - lib/diy/sender.rb
56
57
  - lib/diy/task.rb
57
58
  - lib/diy/version.rb
58
59
  - simple/cmd-pcap.rb
59
- - simple/diy-pcap.rb
60
+ - simple/pcap.rb
61
+ - simple/pcaps/gre.pcap
60
62
  - simple/pcaps/r1.dat
61
63
  - simple/pcaps/r3-2.dat
62
64
  - simple/pcaps/r3.dat
@@ -64,6 +66,12 @@ files:
64
66
  - simple/pcaps/s2.dat
65
67
  - simple/pcaps/s3.dat
66
68
  - simple/pcaps/s4.dat
69
+ - spec/dig_spec.rb
70
+ - spec/helper/pkt1
71
+ - spec/helper/pkt2
72
+ - spec/helper/pkt3
73
+ - spec/helper/pkt4
74
+ - spec/helper/pkt5
67
75
  - spec/spec_helper.rb
68
76
  has_rdoc: true
69
77
  homepage: ""
data/lib/diy/sender.rb DELETED
@@ -1,10 +0,0 @@
1
- require 'eventmachine'
2
-
3
-
4
- module DIY
5
- class Sender < EM::Connection
6
- def notify_readable(*arg)
7
- end
8
-
9
- end
10
- end
data/simple/diy-pcap.rb DELETED
@@ -1,13 +0,0 @@
1
- $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib' )
2
- require 'rubygems'
3
- require 'diy/pcap'
4
-
5
- # client and server
6
- DIY::PCAP.new do |s|
7
- s.dir = File.join( File.dirname(__FILE__), 'pcaps')
8
- s.send("r1.dat")
9
- s.recv("s1.dat")
10
- s.recv("s2.dat")
11
- s.recv("s3.dat")
12
- s.send("r3.dat")
13
- end