DIY-pcap 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/bin/pcap +2 -62
  2. data/bin/rpcap +2 -63
  3. data/lib/diy/command.rb +80 -0
  4. data/lib/diy/device_finder.rb +1 -1
  5. data/lib/diy/dig.rb +3 -1
  6. data/lib/diy/live.rb +5 -0
  7. data/lib/diy/parser/mu/fixnum_ext.rb +7 -0
  8. data/lib/diy/parser/mu/pcap/ethernet.rb +148 -0
  9. data/lib/diy/parser/mu/pcap/header.rb +75 -0
  10. data/lib/diy/parser/mu/pcap/io_pair.rb +67 -0
  11. data/lib/diy/parser/mu/pcap/io_wrapper.rb +76 -0
  12. data/lib/diy/parser/mu/pcap/ip.rb +61 -0
  13. data/lib/diy/parser/mu/pcap/ipv4.rb +257 -0
  14. data/lib/diy/parser/mu/pcap/ipv6.rb +148 -0
  15. data/lib/diy/parser/mu/pcap/packet.rb +104 -0
  16. data/lib/diy/parser/mu/pcap/pkthdr.rb +155 -0
  17. data/lib/diy/parser/mu/pcap/reader.rb +61 -0
  18. data/lib/diy/parser/mu/pcap/reader/http_family.rb +170 -0
  19. data/lib/diy/parser/mu/pcap/sctp.rb +367 -0
  20. data/lib/diy/parser/mu/pcap/sctp/chunk.rb +123 -0
  21. data/lib/diy/parser/mu/pcap/sctp/chunk/data.rb +134 -0
  22. data/lib/diy/parser/mu/pcap/sctp/chunk/init.rb +100 -0
  23. data/lib/diy/parser/mu/pcap/sctp/chunk/init_ack.rb +68 -0
  24. data/lib/diy/parser/mu/pcap/sctp/parameter.rb +110 -0
  25. data/lib/diy/parser/mu/pcap/sctp/parameter/ip_address.rb +48 -0
  26. data/lib/diy/parser/mu/pcap/stream_packetizer.rb +72 -0
  27. data/lib/diy/parser/mu/pcap/tcp.rb +505 -0
  28. data/lib/diy/parser/mu/pcap/udp.rb +69 -0
  29. data/lib/diy/parser/mu/scenario/pcap.rb +172 -0
  30. data/lib/diy/parser/mu/scenario/pcap/fields.rb +50 -0
  31. data/lib/diy/parser/mu/scenario/pcap/rtp.rb +71 -0
  32. data/lib/diy/parser/pcap.rb +113 -0
  33. data/lib/diy/parser/readme.md +72 -0
  34. data/lib/diy/utils.rb +9 -1
  35. data/lib/diy/version.rb +1 -1
  36. data/lib/diy/worker.rb +3 -2
  37. data/lib/diy/worker_keeper.rb +6 -0
  38. data/spec/helper/tcp.dat +0 -0
  39. data/spec/live_spec.rb +9 -0
  40. data/spec/mu_parser_spec.rb +12 -0
  41. data/spec/utils_spec.rb +1 -1
  42. metadata +34 -3
data/bin/pcap CHANGED
@@ -1,67 +1,7 @@
1
1
  $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib' )
2
2
  require 'rubygems'
3
3
  require 'diy-pcap'
4
- require 'diy/task'
5
4
 
6
- require 'optparse'
5
+ $_PORT = "7878"
7
6
 
8
- options = {
9
- :ip => "0.0.0.0:7878",
10
- }
11
-
12
- OptionParser.new do |opts|
13
-
14
- opts.on("-f file", "File will be parsed") do |v|
15
- options[:file] = v
16
- end
17
-
18
- opts.on("-i ip", "--ip ip", "client or server : 0.0.0.0 or 0.0.0.0:7878", "default is 0.0.0.0:7878") do |v|
19
- options[:ip] = v
20
- end
21
-
22
- opts.on("-n device_name", "--name device_name", "Send or Recv device name") do |v|
23
- options[:device_name] = v
24
- end
25
-
26
- opts.on_tail("--show", "Show all devices name and exit") do
27
- require 'diy/device_finder'
28
- DIY::DeviceFinder.pp_devices
29
- exit 0
30
- end
31
-
32
- opts.on_tail('-v','--version', 'Show version') do
33
- puts DIY::PCAP::VERSION
34
- exit 0
35
- end
36
-
37
- opts.on_tail('-V', 'detail mode') do
38
- DIY::Logger.level = ::Logger::DEBUG
39
- end
40
-
41
- opts.on_tail('-h','--help', 'Show this help') do
42
- puts opts
43
- exit 0
44
- end
45
- end.parse!
46
-
47
- if options[:file]
48
- require File.join( Dir.pwd, options[:file] )
49
- else
50
- ip = options[:ip]
51
- if ip.include?(':')
52
- uri = "druby://#{ip}"
53
- else
54
- uri = "druby://#{ip}:7878"
55
- end
56
-
57
- if options[:device_name]
58
- device_name = options[:device_name]
59
- else
60
- require 'diy/device_finder'
61
- device_name = DIY::DeviceFinder.smart_select
62
- end
63
- DIY::Logger.info( "Initialize Live: #{device_name}" )
64
- device = DIY::Live.new(device_name) #FFI::PCap::Live.new(:dev=>device_name, :handler => FFI::PCap::Handler, :promisc => true)
65
- worker = DIY::Worker.new(device)
66
- DIY::WorkerKeeper.new(worker, uri).run
67
- end
7
+ require 'diy/command'
data/bin/rpcap CHANGED
@@ -1,68 +1,7 @@
1
1
  $LOAD_PATH.unshift File.join( File.dirname(__FILE__), '..', 'lib' )
2
2
  require 'rubygems'
3
3
  require 'diy-pcap'
4
- require 'diy/task'
5
4
 
6
- require 'optparse'
5
+ $_PORT = "7879"
7
6
 
8
- options = {
9
- :ip => "0.0.0.0:7879",
10
- }
11
-
12
- OptionParser.new do |opts|
13
-
14
- opts.on("-f file", "File will be parsed") do |v|
15
- options[:file] = v
16
- end
17
-
18
- opts.on("-i ip", "--ip ip", "client or server : 0.0.0.0 or 0.0.0.0:7879", "default is 0.0.0.0:7879") do |v|
19
- options[:ip] = v
20
- end
21
-
22
- opts.on("-n device_name", "--name device_name", "Send or Recv device name") do |v|
23
- options[:device_name] = v
24
- end
25
-
26
- opts.on_tail("--show", "Show all devices name and exit") do
27
- require 'diy/device_finder'
28
- DIY::DeviceFinder.pp_devices
29
- exit 0
30
- end
31
-
32
- opts.on_tail('-v','--version', 'Show version') do
33
- puts DIY::PCAP::VERSION
34
- exit 0
35
- end
36
-
37
- opts.on_tail('-V', 'detail mode') do
38
- DIY::Logger.level = ::Logger::DEBUG
39
- end
40
-
41
- opts.on_tail('-h','--help', 'Show this help') do
42
- puts opts
43
- exit 0
44
- end
45
- end.parse!
46
-
47
- if options[:file]
48
- $SERVER = true
49
- require File.join( Dir.pwd, options[:file])
50
- else
51
- ip = options[:ip]
52
- if ip.include?(':')
53
- uri = "druby://#{ip}"
54
- else
55
- uri = "druby://#{ip}:7879"
56
- end
57
-
58
- if options[:device_name]
59
- device_name = options[:device_name]
60
- else
61
- require 'diy/device_finder'
62
- device_name = DIY::DeviceFinder.smart_select
63
- end
64
- DIY::Logger.info( "Initialize Live: #{device_name}" )
65
- device = DIY::Live.new(device_name) #FFI::PCap::Live.new(:dev=>device_name, :handler => FFI::PCap::Handler, :promisc => true)
66
- worker = DIY::Worker.new(device)
67
- DIY::WorkerKeeper.new(worker, uri).run
68
- end
7
+ require 'diy/command'
@@ -0,0 +1,80 @@
1
+
2
+ require 'diy/task'
3
+ require 'optparse'
4
+
5
+ pcap_port = "7878"
6
+ rpcap_port = "7879"
7
+
8
+ if ! $_PORT
9
+ puts "Must set $_PORT value before call me"
10
+ exit 1
11
+ end
12
+
13
+ options = {
14
+ :ip => "0.0.0.0:#{$_PORT}",
15
+ }
16
+
17
+ OptionParser.new do |opts|
18
+
19
+ opts.on("-f file", "File will be parsed") do |v|
20
+ options[:file] = v
21
+ end
22
+
23
+ opts.on("-i ip", "--ip ip", "client or server : 0.0.0.0 or 0.0.0.0:#{$_PORT}", "default is 0.0.0.0:#{$_PORT}") do |v|
24
+ options[:ip] = v
25
+ end
26
+
27
+ opts.on("-n device_name", "--name device_name", "Send or Recv device name") do |v|
28
+ options[:device_name] = v
29
+ end
30
+
31
+ opts.on_tail("--show", "Show all devices name and exit") do
32
+ require 'diy/device_finder'
33
+ DIY::DeviceFinder.pp_devices
34
+ exit 0
35
+ end
36
+
37
+ opts.on_tail("--timer","Use TimerIdConv module instead of DRb's default idconv") do
38
+ options[:timer] = true
39
+ end
40
+
41
+ opts.on_tail('-v','--version', 'Show version') do
42
+ puts DIY::PCAP::VERSION
43
+ exit 0
44
+ end
45
+
46
+ opts.on_tail('-V', 'detail mode') do
47
+ DIY::Logger.level = ::Logger::DEBUG
48
+ DIY::Logger.debug "Enable debug mode"
49
+ end
50
+
51
+ opts.on_tail('-h','--help', 'Show this help and exit') do
52
+ puts opts
53
+ exit 0
54
+ end
55
+ end.parse!
56
+
57
+ if options[:file]
58
+ require File.join( Dir.pwd, options[:file] )
59
+ else
60
+ ip = options[:ip]
61
+ if ip.include?(':')
62
+ uri = "druby://#{ip}"
63
+ else
64
+ uri = "druby://#{ip}:#{$_PORT}"
65
+ end
66
+
67
+ if options[:device_name]
68
+ device_name = options[:device_name]
69
+ else
70
+ require 'diy/device_finder'
71
+ device_name = DIY::DeviceFinder.smart_select
72
+ end
73
+ device = DIY::Live.new(device_name)
74
+ worker = DIY::Worker.new(device)
75
+ worker_keeper = DIY::WorkerKeeper.new(worker, uri)
76
+ if options[:timer]
77
+ worker_keeper.use_timeridconv
78
+ end
79
+ worker_keeper.run
80
+ end
@@ -3,7 +3,7 @@ module DIY
3
3
  class <<self
4
4
  def smart_select
5
5
  ret = devices.find do |device, net|
6
- !device.match(/dialup/) && net != nil
6
+ !device.match(/dialup/) && net != nil && net != "0.0.0.0/0.0.0.0"
7
7
  end
8
8
  if ret
9
9
  ret[0]
@@ -10,4 +10,6 @@ require 'diy/strategy_builder'
10
10
  require 'diy/strategy'
11
11
 
12
12
  require 'diy/worker'
13
- require 'diy/worker_keeper'
13
+ require 'diy/worker_keeper'
14
+
15
+ autoload :Mu, "diy/parser/pcap"
@@ -7,6 +7,7 @@ module DIY
7
7
  def initialize(device_name)
8
8
  DIY::Logger.info( "Initialize Live: #{device_name}" )
9
9
  @live = FFI::PCap::Live.new(:dev=>device_name, :handler => FFI::PCap::CopyHandler, :promisc => true)
10
+ DIY::Logger.info( "Listen on: #{net} " )
10
11
  #~ @live.non_blocking= true
11
12
  end
12
13
  attr_reader :live
@@ -27,6 +28,10 @@ module DIY
27
28
  end
28
29
  end
29
30
 
31
+ def net
32
+ @live.network + " / " + @live.netmask
33
+ end
34
+
30
35
  end # end of class Live
31
36
 
32
37
  end
@@ -0,0 +1,7 @@
1
+ if RUBY_VERSION == '1.8.6'
2
+ class Fixnum
3
+ def ord
4
+ self
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,148 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ module Mu
6
+ class Pcap
7
+
8
+ class Ethernet < Packet
9
+ attr_accessor :src, :dst, :type
10
+
11
+ ETHERTYPE_IP = 0x0800
12
+ ETHERTYPE_IP6 = 0x86dd
13
+ ETHERTYPE_ARP = 0x0806
14
+ ETHERTYPE_PPPOE_SESSION = 0x8864
15
+ ETHERTYPE_802_1Q = 0X8100
16
+
17
+ PPP_IP = 0x0021
18
+ PPP_IPV6 = 0x0057
19
+
20
+ def initialize src=nil, dst=nil, type=0
21
+ super()
22
+ @src = src
23
+ @dst = dst
24
+ @type = type
25
+ end
26
+
27
+ def flow_id
28
+ if not @payload or @payload.is_a? String
29
+ return [:ethernet, @src, @dst, @type]
30
+ else
31
+ return @payload.flow_id
32
+ end
33
+ end
34
+
35
+ FMT_MAC = "C6"
36
+ FMT_n = 'n'
37
+ MAC_TEMPLATE = '%02x:%02x:%02x:%02x:%02x:%02x'
38
+ def self.from_bytes bytes
39
+ if bytes.length < 14
40
+ raise ParseError, "Truncated Ethernet header: expected 14 bytes, got #{bytes.length} bytes"
41
+ end
42
+
43
+ dst = bytes.slice!(0,6).unpack FMT_MAC
44
+ dst = MAC_TEMPLATE % dst
45
+ src = bytes.slice!(0,6).unpack FMT_MAC
46
+ src = MAC_TEMPLATE % src
47
+ type = bytes.slice!(0,2).unpack(FMT_n)[0]
48
+ while (type == ETHERTYPE_802_1Q)
49
+ # Skip 4 bytes for 802.1q vlan tag field
50
+ bytes.slice!(0,2)
51
+ type = bytes.slice!(0,2).unpack(FMT_n)[0]
52
+ end
53
+ ethernet = Ethernet.new src, dst, type
54
+ ethernet.payload = bytes
55
+ ethernet.payload_raw = bytes
56
+ begin
57
+ case type
58
+ when ETHERTYPE_IP
59
+ ethernet.payload = IPv4.from_bytes bytes
60
+ when ETHERTYPE_IP6
61
+ ethernet.payload = IPv6.from_bytes bytes
62
+ when ETHERTYPE_PPPOE_SESSION
63
+ # Remove PPPoE/PPP session layer
64
+ ethernet.payload = bytes
65
+ ethernet.remove_pppoe!
66
+ else
67
+ ethernet.payload = bytes
68
+ end
69
+ rescue ParseError => e
70
+ Pcap.warning e
71
+ end
72
+ return ethernet
73
+ end
74
+
75
+ def ip?
76
+ return payload.is_a?(IP)
77
+ end
78
+
79
+ ADDR_TO_BYTES = {}
80
+ FMT_HEADER = 'a6a6n'
81
+ def write io
82
+ dst_mac = ADDR_TO_BYTES[@dst] ||= @dst.split(':').inject('') {|m, b| m << b.to_i(16).chr}
83
+ src_mac = ADDR_TO_BYTES[@src] ||= @src.split(':').inject('') {|m, b| m << b.to_i(16).chr}
84
+ bytes = [dst_mac, src_mac, @type].pack(FMT_HEADER)
85
+ io.write bytes
86
+ if @payload.is_a? String
87
+ io.write @payload
88
+ else
89
+ @payload.write io
90
+ end
91
+ end
92
+
93
+ # Remove the PPPoE and PPP headers. PPPoE is documented in RFC 2516.
94
+ def remove_pppoe!
95
+ bytes = self.payload_raw
96
+
97
+ # Remove PPPoE header
98
+ Pcap.assert bytes.length >= 6, 'Truncated PPPoE header: ' +
99
+ "expected at least 6 bytes, got #{bytes.length} bytes"
100
+ version_type, code, session_id, length = bytes.unpack 'CCnn'
101
+ version = version_type >> 4 & 0b1111
102
+ type = version_type & 0b1111
103
+ Pcap.assert version == 1, "Unknown PPPoE version: #{version}"
104
+ Pcap.assert type == 1, "Unknown PPPoE type: #{type}"
105
+ Pcap.assert code == 0, "Unknown PPPoE code: #{code}"
106
+ bytes = bytes[6..-1]
107
+ Pcap.assert bytes.length >= length, 'Truncated PPoE packet: ' +
108
+ "expected #{length} bytes, got #{bytes.length} bytes"
109
+
110
+ # Remove PPP header
111
+ Pcap.assert bytes.length >= 2, 'Truncated PPP packet: ' +
112
+ "expected at least bytes, got #{bytes.length} bytes"
113
+ protocol_id, = bytes.unpack 'n'
114
+ bytes = bytes[2..-1]
115
+ case protocol_id
116
+ when PPP_IP
117
+ self.payload = IPv4.from_bytes bytes
118
+ self.payload_raw = bytes
119
+ self.type = ETHERTYPE_IP
120
+ when PPP_IPV6
121
+ self.payload = IPv6.from_bytes bytes
122
+ self.payload_raw = bytes
123
+ self.type = ETHERTYPE_IP6
124
+ else
125
+ # Failed. Don't update payload or type.
126
+ raise ParseError, "Unknown PPP protocol: 0x#{'%04x' % protocol_id}"
127
+ end
128
+ end
129
+
130
+ def to_s
131
+ if @payload.is_a? String
132
+ payload = @payload.inspect
133
+ else
134
+ payload = @payload.to_s
135
+ end
136
+ return "ethernet(%s, %s, %d, %s)" % [@src, @dst, @type, payload]
137
+ end
138
+
139
+ def == other
140
+ return super &&
141
+ self.src == other.src &&
142
+ self.dst == other.dst &&
143
+ self.type == other.type
144
+ end
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,75 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ module Mu
6
+ class Pcap
7
+
8
+ class Header
9
+ attr_accessor :magic, :version_major, :version_minor, :thiszone, :sigfigs,
10
+ :snaplen, :linktype
11
+
12
+ BIG_ENDIAN_FORMAT = 'nnNNNN'
13
+ LITTLE_ENDIAN_FORMAT = 'vvVVVV'
14
+
15
+ UNSUPPORTED_FORMATS = {
16
+ 0x474D4255 => "NetMon", # "GMBU"
17
+ 0x5452534E => "NA Sniffer (DOS)" # Starts with "TRSNIFF data"
18
+ }
19
+
20
+ def initialize
21
+ @magic = BIG_ENDIAN
22
+ @version_major = 2
23
+ @version_minor = 4
24
+ @thiszone = 0
25
+ @sigfigs = 0
26
+ @snaplen = 1500
27
+ @linktype = DLT_NULL
28
+ end
29
+
30
+ def self.read ios
31
+ header = Header.new
32
+ bytes = ios.read 24
33
+ Pcap.assert bytes, 'PCAP header missing'
34
+ Pcap.assert bytes.length == 24, 'Truncated PCAP header: ' +
35
+ "expected 24 bytes, got #{bytes.length} bytes"
36
+ header.magic, _ = bytes[0, 4].unpack 'N'
37
+ if header.magic == BIG_ENDIAN
38
+ format = BIG_ENDIAN_FORMAT
39
+ elsif header.magic == LITTLE_ENDIAN
40
+ format = LITTLE_ENDIAN_FORMAT
41
+ else
42
+ format = UNSUPPORTED_FORMATS[header.magic]
43
+ if format.nil?
44
+ err = "Unsupported packet capture format. "
45
+ else
46
+ err = "#{format} capture files are not supported. "
47
+ end
48
+ raise ParseError, err
49
+ end
50
+ header.version_major, header.version_minor, header.thiszone,
51
+ header.sigfigs, header.snaplen, header.linktype =
52
+ bytes[4..-1].unpack format
53
+ return header
54
+ end
55
+
56
+ def write io
57
+ bytes = [BIG_ENDIAN, @version_major, @version_minor, @thiszone,
58
+ @sigfigs, @snaplen, DLT_EN10MB].pack('N' + BIG_ENDIAN_FORMAT)
59
+ io.write bytes
60
+ end
61
+
62
+ def == other
63
+ return self.class == other.class &&
64
+ self.magic == other.magic &&
65
+ self.version_major == other.version_major &&
66
+ self.version_minor == other.version_minor &&
67
+ self.thiszone == other.thiszone &&
68
+ self.sigfigs == other.sigfigs &&
69
+ self.snaplen == other.snaplen &&
70
+ self.linktype == other.linktype
71
+ end
72
+ end
73
+
74
+ end
75
+ end