DIY-pcap 0.4.1 → 0.4.3

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 (35) hide show
  1. data/.gitignore +4 -4
  2. data/DIY-pcap.gemspec +17 -17
  3. data/Gemfile +3 -3
  4. data/Rakefile +1 -1
  5. data/lib/DIY-pcap.rb +2 -2
  6. data/lib/diy/command.rb +7 -1
  7. data/lib/diy/controller.rb +10 -15
  8. data/lib/diy/live.rb +9 -2
  9. data/lib/diy/parser/mu/pcap/ethernet.rb +148 -148
  10. data/lib/diy/parser/mu/pcap/header.rb +75 -75
  11. data/lib/diy/parser/mu/pcap/io_pair.rb +67 -67
  12. data/lib/diy/parser/mu/pcap/io_wrapper.rb +76 -76
  13. data/lib/diy/parser/mu/pcap/ip.rb +61 -61
  14. data/lib/diy/parser/mu/pcap/ipv4.rb +257 -257
  15. data/lib/diy/parser/mu/pcap/ipv6.rb +148 -148
  16. data/lib/diy/parser/mu/pcap/packet.rb +104 -104
  17. data/lib/diy/parser/mu/pcap/pkthdr.rb +155 -155
  18. data/lib/diy/parser/mu/pcap/reader.rb +61 -61
  19. data/lib/diy/parser/mu/pcap/reader/http_family.rb +170 -170
  20. data/lib/diy/parser/mu/pcap/sctp.rb +367 -367
  21. data/lib/diy/parser/mu/pcap/sctp/chunk.rb +123 -123
  22. data/lib/diy/parser/mu/pcap/sctp/chunk/data.rb +134 -134
  23. data/lib/diy/parser/mu/pcap/sctp/chunk/init.rb +100 -100
  24. data/lib/diy/parser/mu/pcap/sctp/chunk/init_ack.rb +68 -68
  25. data/lib/diy/parser/mu/pcap/sctp/parameter.rb +110 -110
  26. data/lib/diy/parser/mu/pcap/sctp/parameter/ip_address.rb +48 -48
  27. data/lib/diy/parser/mu/pcap/stream_packetizer.rb +72 -72
  28. data/lib/diy/parser/mu/pcap/tcp.rb +505 -505
  29. data/lib/diy/parser/mu/pcap/udp.rb +69 -69
  30. data/lib/diy/parser/mu/scenario/pcap.rb +172 -172
  31. data/lib/diy/parser/mu/scenario/pcap/fields.rb +50 -50
  32. data/lib/diy/parser/mu/scenario/pcap/rtp.rb +71 -71
  33. data/lib/diy/parser/pcap.rb +109 -109
  34. data/lib/diy/version.rb +1 -1
  35. metadata +7 -9
@@ -1,69 +1,69 @@
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 UDP < Packet
9
- attr_accessor :src_port, :dst_port
10
-
11
- def initialize src_port=0, dst_port=0
12
- super()
13
- @src_port = src_port
14
- @dst_port = dst_port
15
- end
16
-
17
- def flow_id
18
- return [:udp, @src_port, @dst_port]
19
- end
20
-
21
- FMT_nnnn = 'nnnn'
22
- def self.from_bytes bytes
23
- bytes_length = bytes.length
24
- bytes_length >= 8 or
25
- raise ParseError, "Truncated UDP header: expected 8 bytes, got #{bytes_length} bytes"
26
- sport, dport, length, checksum = bytes.unpack(FMT_nnnn)
27
- bytes_length >= length or
28
- raise ParseError, "Truncated UDP packet: expected #{length} bytes, got #{bytes_length} bytes"
29
- udp = UDP.new sport, dport
30
- udp.payload_raw = bytes[8..-1]
31
- udp.payload = bytes[8..length]
32
- return udp
33
- end
34
-
35
- def write io, ip
36
- length = @payload.length
37
- length_8 = length + 8
38
- if length_8 > 65535
39
- Pcap.warning "UDP payload is too large"
40
- end
41
- pseudo_header = ip.pseudo_header length_8
42
- header = [@src_port, @dst_port, length_8, 0] \
43
- .pack FMT_nnnn
44
- checksum = IP.checksum(pseudo_header + header + @payload)
45
- header = [@src_port, @dst_port, length_8, checksum] \
46
- .pack FMT_nnnn
47
- io.write header
48
- io.write @payload
49
- end
50
-
51
- def self.udp? packet
52
- return packet.is_a?(Ethernet) &&
53
- packet.payload.is_a?(IP) &&
54
- packet.payload.payload.is_a?(UDP)
55
- end
56
-
57
- def to_s
58
- return "udp(%d, %d, %s)" % [@src_port, @dst_port, @payload.inspect]
59
- end
60
-
61
- def == other
62
- return super &&
63
- self.src_port == other.src_port &&
64
- self.dst_port == other.dst_port
65
- end
66
- end
67
-
68
- end
69
- end
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 UDP < Packet
9
+ attr_accessor :src_port, :dst_port
10
+
11
+ def initialize src_port=0, dst_port=0
12
+ super()
13
+ @src_port = src_port
14
+ @dst_port = dst_port
15
+ end
16
+
17
+ def flow_id
18
+ return [:udp, @src_port, @dst_port]
19
+ end
20
+
21
+ FMT_nnnn = 'nnnn'
22
+ def self.from_bytes bytes
23
+ bytes_length = bytes.length
24
+ bytes_length >= 8 or
25
+ raise ParseError, "Truncated UDP header: expected 8 bytes, got #{bytes_length} bytes"
26
+ sport, dport, length, checksum = bytes.unpack(FMT_nnnn)
27
+ bytes_length >= length or
28
+ raise ParseError, "Truncated UDP packet: expected #{length} bytes, got #{bytes_length} bytes"
29
+ udp = UDP.new sport, dport
30
+ udp.payload_raw = bytes[8..-1]
31
+ udp.payload = bytes[8..length]
32
+ return udp
33
+ end
34
+
35
+ def write io, ip
36
+ length = @payload.length
37
+ length_8 = length + 8
38
+ if length_8 > 65535
39
+ Pcap.warning "UDP payload is too large"
40
+ end
41
+ pseudo_header = ip.pseudo_header length_8
42
+ header = [@src_port, @dst_port, length_8, 0] \
43
+ .pack FMT_nnnn
44
+ checksum = IP.checksum(pseudo_header + header + @payload)
45
+ header = [@src_port, @dst_port, length_8, checksum] \
46
+ .pack FMT_nnnn
47
+ io.write header
48
+ io.write @payload
49
+ end
50
+
51
+ def self.udp? packet
52
+ return packet.is_a?(Ethernet) &&
53
+ packet.payload.is_a?(IP) &&
54
+ packet.payload.payload.is_a?(UDP)
55
+ end
56
+
57
+ def to_s
58
+ return "udp(%d, %d, %s)" % [@src_port, @dst_port, @payload.inspect]
59
+ end
60
+
61
+ def == other
62
+ return super &&
63
+ self.src_port == other.src_port &&
64
+ self.dst_port == other.dst_port
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -1,172 +1,172 @@
1
- # http://www.mudynamics.com
2
- # http://labs.mudynamics.com
3
- # http://www.pcapr.net
4
-
5
- require 'tempfile'
6
- require 'fileutils'
7
- require 'mu/scenario/pcap/fields'
8
- require 'mu/pcap'
9
- require 'json'
10
-
11
- module Mu
12
- class Scenario
13
-
14
- module Pcap
15
- TSHARK_READ_TIMEOUT = 10.0 # seconds
16
- TSHARK_LINES_PER_PACKET = 16384
17
- TSHARK_OPTS = "-n -o tcp.desegment_tcp_streams:false"
18
- TSHARK_OPTS_SUFFIX = TSHARK_OPTS
19
- TSHARK_SIZE_OPTS = "-n -o 'column.format: cum_size, \"%B\"'"
20
- TSHARK_PSML_OPTS = %Q{#{TSHARK_OPTS} -o 'column.format: "Protocol", "%p", "Info", "%i"'}
21
-
22
- MAX_PCAP_SIZE = 102400 # 100KB
23
- MAX_RAW_PCAP_SIZE_MB = 25
24
- MAX_RAW_PCAP_SIZE = MAX_RAW_PCAP_SIZE_MB * 1024 * 1000
25
- EXCLUDE_FROM_SIZE_CHECK = ['rtp'].freeze
26
-
27
- class PcapTooLarge < StandardError; end
28
-
29
- def self.reset_options options
30
- return unless options
31
- tshark_opts = options << ' ' << TSHARK_OPTS_SUFFIX
32
- remove_const(:TSHARK_OPTS) if const_defined?(:TSHARK_OPTS)
33
- const_set(:TSHARK_OPTS, tshark_opts)
34
- end
35
-
36
- def self.validate_pcap_size(path)
37
- tshark_filter = EXCLUDE_FROM_SIZE_CHECK.map{ |proto| "not #{proto}" }.join " and "
38
- io = ::IO.popen "tshark #{TSHARK_SIZE_OPTS} -r #{path} -R '#{tshark_filter}' | tail -1"
39
- if ::IO.select [ io ], nil, nil, TSHARK_READ_TIMEOUT
40
- if io.eof?
41
- size = 0
42
- else
43
- last_line = io.readline
44
- size = last_line.to_i
45
- end
46
- end
47
-
48
- if size.nil? or size == 0
49
- size = File.size(path)
50
- end
51
-
52
- if size > MAX_PCAP_SIZE
53
- raise PcapTooLarge, "Selected packets have a size of #{size} bytes which " +
54
- "exceeds the #{MAX_PCAP_SIZE} byte maximum."
55
- end
56
-
57
- if size > MAX_RAW_PCAP_SIZE
58
- raise PcapTooLarge, "Selected packets have a raw size of #{size} bytes which " +
59
- "exceeds the #{MAX_RAW_PCAP_SIZE_MB}MB maximum."
60
- end
61
-
62
- return size
63
- end
64
-
65
- PAR_VERSION = 1
66
- def self.export_to_par pcap_path, opts=nil
67
- opts ||= {}
68
-
69
- # Open pcap file
70
- File.exist?(pcap_path) or raise "Cannot open file '#{pcap_path}'."
71
- validate_pcap_size pcap_path
72
- pcap = open pcap_path, 'rb'
73
-
74
- # Get Mu::Pcap::Packets
75
- packets = to_pcap_packets pcap, opts[:isolate_l7]
76
-
77
- # Write normalized packets to tempfile
78
- tmpdir = Dir.mktmpdir
79
- norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
80
- pcap = Mu::Pcap.from_packets packets
81
- pcap.write norm_pcap
82
- norm_pcap.close
83
-
84
- # Get wireshark dissected field values for all packets.
85
- `tshark -T fields #{TSHARK_OPTS} #{Fields::TSHARK_OPTS} -Eseparator='\xff' -r #{norm_pcap.path} > #{tmpdir}/fields`
86
- fields = open "#{tmpdir}/fields", 'rb'
87
-
88
- # Get wireshark dissected field values for all packets.
89
- fields_array = []
90
- if fields
91
- packets.each do |packet|
92
- fields_array << Fields.next_from_io(fields)
93
- end
94
- end
95
-
96
- # Protocol specific preprocessing, packets may be deleted.
97
- Rtp.preprocess packets, fields_array
98
-
99
- File.open "#{tmpdir}/packets.dump", 'wb' do |f|
100
- Marshal.dump packets, f
101
- end
102
-
103
- # Create a second pcap with packets removed.
104
- norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
105
- pcap = Mu::Pcap.from_packets packets
106
- pcap.write norm_pcap
107
- norm_pcap.close
108
-
109
- # Dump PSML to file.
110
- # (The no-op filter sometimes produces slighty more verbose descriptions.)
111
- `tshark -T psml #{TSHARK_PSML_OPTS} -r #{norm_pcap.path} -R 'rtp or not rtp' > #{tmpdir}/psml`
112
-
113
- # Dump PDML io file.
114
- `tshark -T pdml #{TSHARK_OPTS} -r #{norm_pcap.path} > #{tmpdir}/pdml`
115
- pdml = open "#{tmpdir}/pdml", 'rb'
116
-
117
- # Create about
118
- open "#{tmpdir}/about", 'w' do |about|
119
- about.puts({:par_version => PAR_VERSION}.to_json)
120
- end
121
-
122
- # Create zip
123
- Dir.chdir tmpdir do
124
- system "zip -q dissected.zip about pdml psml fields packets.dump normalized.pcap"
125
- return open("dissected.zip")
126
- end
127
- ensure
128
- if tmpdir
129
- FileUtils.rm_rf tmpdir
130
- end
131
- end
132
-
133
- def self.to_pcap_packets io, isolate_l7=true
134
- packets = []
135
-
136
- # Read Pcap packets from Pcap
137
- Mu::Pcap.each_pkthdr io do |pkthdr|
138
- if pkthdr.len != pkthdr.caplen
139
- raise Mu::Pcap::ParseError, "Error: Capture contains truncated packets. " +
140
- "Try recapturing with an increased snapshot length."
141
- end
142
- if not pkthdr.pkt.is_a? Mu::Pcap::Ethernet
143
- warning 'Unable to parse packet, skipping.'
144
- end
145
- packets << pkthdr.pkt
146
- end
147
-
148
- if (packets.length == 0)
149
- raise Mu::Pcap::ParseError, "No valid packets found!"
150
- end
151
-
152
- packets = Mu::Pcap::IPv4.reassemble packets
153
-
154
- if isolate_l7
155
- packets = Mu::Pcap::Packet.isolate_l7 packets
156
- end
157
-
158
- packets = Mu::Pcap::Packet.normalize packets
159
- packets = Mu::Pcap::TCP.split packets
160
-
161
- packets
162
- end
163
-
164
- def self.warning msg
165
- $stderr.puts "WARNING: #{msg}"#, caller, $!
166
- end
167
- end
168
-
169
- end
170
- end
171
-
172
- require 'mu/scenario/pcap/rtp'
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ require 'tempfile'
6
+ require 'fileutils'
7
+ require 'mu/scenario/pcap/fields'
8
+ require 'mu/pcap'
9
+ require 'json'
10
+
11
+ module Mu
12
+ class Scenario
13
+
14
+ module Pcap
15
+ TSHARK_READ_TIMEOUT = 10.0 # seconds
16
+ TSHARK_LINES_PER_PACKET = 16384
17
+ TSHARK_OPTS = "-n -o tcp.desegment_tcp_streams:false"
18
+ TSHARK_OPTS_SUFFIX = TSHARK_OPTS
19
+ TSHARK_SIZE_OPTS = "-n -o 'column.format: cum_size, \"%B\"'"
20
+ TSHARK_PSML_OPTS = %Q{#{TSHARK_OPTS} -o 'column.format: "Protocol", "%p", "Info", "%i"'}
21
+
22
+ MAX_PCAP_SIZE = 102400 # 100KB
23
+ MAX_RAW_PCAP_SIZE_MB = 25
24
+ MAX_RAW_PCAP_SIZE = MAX_RAW_PCAP_SIZE_MB * 1024 * 1000
25
+ EXCLUDE_FROM_SIZE_CHECK = ['rtp'].freeze
26
+
27
+ class PcapTooLarge < StandardError; end
28
+
29
+ def self.reset_options options
30
+ return unless options
31
+ tshark_opts = options << ' ' << TSHARK_OPTS_SUFFIX
32
+ remove_const(:TSHARK_OPTS) if const_defined?(:TSHARK_OPTS)
33
+ const_set(:TSHARK_OPTS, tshark_opts)
34
+ end
35
+
36
+ def self.validate_pcap_size(path)
37
+ tshark_filter = EXCLUDE_FROM_SIZE_CHECK.map{ |proto| "not #{proto}" }.join " and "
38
+ io = ::IO.popen "tshark #{TSHARK_SIZE_OPTS} -r #{path} -R '#{tshark_filter}' | tail -1"
39
+ if ::IO.select [ io ], nil, nil, TSHARK_READ_TIMEOUT
40
+ if io.eof?
41
+ size = 0
42
+ else
43
+ last_line = io.readline
44
+ size = last_line.to_i
45
+ end
46
+ end
47
+
48
+ if size.nil? or size == 0
49
+ size = File.size(path)
50
+ end
51
+
52
+ if size > MAX_PCAP_SIZE
53
+ raise PcapTooLarge, "Selected packets have a size of #{size} bytes which " +
54
+ "exceeds the #{MAX_PCAP_SIZE} byte maximum."
55
+ end
56
+
57
+ if size > MAX_RAW_PCAP_SIZE
58
+ raise PcapTooLarge, "Selected packets have a raw size of #{size} bytes which " +
59
+ "exceeds the #{MAX_RAW_PCAP_SIZE_MB}MB maximum."
60
+ end
61
+
62
+ return size
63
+ end
64
+
65
+ PAR_VERSION = 1
66
+ def self.export_to_par pcap_path, opts=nil
67
+ opts ||= {}
68
+
69
+ # Open pcap file
70
+ File.exist?(pcap_path) or raise "Cannot open file '#{pcap_path}'."
71
+ validate_pcap_size pcap_path
72
+ pcap = open pcap_path, 'rb'
73
+
74
+ # Get Mu::Pcap::Packets
75
+ packets = to_pcap_packets pcap, opts[:isolate_l7]
76
+
77
+ # Write normalized packets to tempfile
78
+ tmpdir = Dir.mktmpdir
79
+ norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
80
+ pcap = Mu::Pcap.from_packets packets
81
+ pcap.write norm_pcap
82
+ norm_pcap.close
83
+
84
+ # Get wireshark dissected field values for all packets.
85
+ `tshark -T fields #{TSHARK_OPTS} #{Fields::TSHARK_OPTS} -Eseparator='\xff' -r #{norm_pcap.path} > #{tmpdir}/fields`
86
+ fields = open "#{tmpdir}/fields", 'rb'
87
+
88
+ # Get wireshark dissected field values for all packets.
89
+ fields_array = []
90
+ if fields
91
+ packets.each do |packet|
92
+ fields_array << Fields.next_from_io(fields)
93
+ end
94
+ end
95
+
96
+ # Protocol specific preprocessing, packets may be deleted.
97
+ Rtp.preprocess packets, fields_array
98
+
99
+ File.open "#{tmpdir}/packets.dump", 'wb' do |f|
100
+ Marshal.dump packets, f
101
+ end
102
+
103
+ # Create a second pcap with packets removed.
104
+ norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
105
+ pcap = Mu::Pcap.from_packets packets
106
+ pcap.write norm_pcap
107
+ norm_pcap.close
108
+
109
+ # Dump PSML to file.
110
+ # (The no-op filter sometimes produces slighty more verbose descriptions.)
111
+ `tshark -T psml #{TSHARK_PSML_OPTS} -r #{norm_pcap.path} -R 'rtp or not rtp' > #{tmpdir}/psml`
112
+
113
+ # Dump PDML io file.
114
+ `tshark -T pdml #{TSHARK_OPTS} -r #{norm_pcap.path} > #{tmpdir}/pdml`
115
+ pdml = open "#{tmpdir}/pdml", 'rb'
116
+
117
+ # Create about
118
+ open "#{tmpdir}/about", 'w' do |about|
119
+ about.puts({:par_version => PAR_VERSION}.to_json)
120
+ end
121
+
122
+ # Create zip
123
+ Dir.chdir tmpdir do
124
+ system "zip -q dissected.zip about pdml psml fields packets.dump normalized.pcap"
125
+ return open("dissected.zip")
126
+ end
127
+ ensure
128
+ if tmpdir
129
+ FileUtils.rm_rf tmpdir
130
+ end
131
+ end
132
+
133
+ def self.to_pcap_packets io, isolate_l7=true
134
+ packets = []
135
+
136
+ # Read Pcap packets from Pcap
137
+ Mu::Pcap.each_pkthdr io do |pkthdr|
138
+ if pkthdr.len != pkthdr.caplen
139
+ raise Mu::Pcap::ParseError, "Error: Capture contains truncated packets. " +
140
+ "Try recapturing with an increased snapshot length."
141
+ end
142
+ if not pkthdr.pkt.is_a? Mu::Pcap::Ethernet
143
+ warning 'Unable to parse packet, skipping.'
144
+ end
145
+ packets << pkthdr.pkt
146
+ end
147
+
148
+ if (packets.length == 0)
149
+ raise Mu::Pcap::ParseError, "No valid packets found!"
150
+ end
151
+
152
+ packets = Mu::Pcap::IPv4.reassemble packets
153
+
154
+ if isolate_l7
155
+ packets = Mu::Pcap::Packet.isolate_l7 packets
156
+ end
157
+
158
+ packets = Mu::Pcap::Packet.normalize packets
159
+ packets = Mu::Pcap::TCP.split packets
160
+
161
+ packets
162
+ end
163
+
164
+ def self.warning msg
165
+ $stderr.puts "WARNING: #{msg}"#, caller, $!
166
+ end
167
+ end
168
+
169
+ end
170
+ end
171
+
172
+ require 'mu/scenario/pcap/rtp'