DIY-pcap 0.0.1 → 0.0.2

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/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