DIY-pcap 0.2.5 → 0.2.6

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