woolen_common 0.0.1
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.
- checksums.yaml +15 -0
- data/README.md +31 -0
- data/ext/woolen_common/extconf.rb +26 -0
- data/ext/woolen_common/linux.h +3 -0
- data/ext/woolen_common/win.c +18 -0
- data/ext/woolen_common/win.h +4 -0
- data/ext/woolen_common/win/puts_color.c +139 -0
- data/ext/woolen_common/win/puts_color.h +5 -0
- data/ext/woolen_common/woolen_common.c +20 -0
- data/ext/woolen_common/woolen_common.h +7 -0
- data/lib/woolen_common.rb +39 -0
- data/lib/woolen_common/abstract_middleware/builder.rb +138 -0
- data/lib/woolen_common/abstract_middleware/map_cfg_manager.rb +52 -0
- data/lib/woolen_common/abstract_middleware/runner.rb +72 -0
- data/lib/woolen_common/action_pool_proxy.rb +28 -0
- data/lib/woolen_common/actionpool.rb +10 -0
- data/lib/woolen_common/actionpool/pool.rb +295 -0
- data/lib/woolen_common/actionpool/queue.rb +41 -0
- data/lib/woolen_common/actionpool/thread.rb +181 -0
- data/lib/woolen_common/addr_helper.rb +93 -0
- data/lib/woolen_common/cache.rb +305 -0
- data/lib/woolen_common/common_helper.rb +42 -0
- data/lib/woolen_common/config_manager.rb +36 -0
- data/lib/woolen_common/drb_helper.rb +125 -0
- data/lib/woolen_common/ffi/win32_kernel32.rb +86 -0
- data/lib/woolen_common/logger.rb +419 -0
- data/lib/woolen_common/pcap/mu/fixnum_ext.rb +8 -0
- data/lib/woolen_common/pcap/mu/pcap/ethernet.rb +164 -0
- data/lib/woolen_common/pcap/mu/pcap/header.rb +76 -0
- data/lib/woolen_common/pcap/mu/pcap/io_pair.rb +68 -0
- data/lib/woolen_common/pcap/mu/pcap/io_wrapper.rb +77 -0
- data/lib/woolen_common/pcap/mu/pcap/ip.rb +62 -0
- data/lib/woolen_common/pcap/mu/pcap/ipv4.rb +274 -0
- data/lib/woolen_common/pcap/mu/pcap/ipv6.rb +149 -0
- data/lib/woolen_common/pcap/mu/pcap/packet.rb +106 -0
- data/lib/woolen_common/pcap/mu/pcap/pkthdr.rb +162 -0
- data/lib/woolen_common/pcap/mu/pcap/reader.rb +62 -0
- data/lib/woolen_common/pcap/mu/pcap/reader/http_family.rb +175 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp.rb +369 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk.rb +124 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/data.rb +135 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init.rb +101 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init_ack.rb +69 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/parameter.rb +111 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/parameter/ip_address.rb +49 -0
- data/lib/woolen_common/pcap/mu/pcap/stream_packetizer.rb +74 -0
- data/lib/woolen_common/pcap/mu/pcap/tcp.rb +522 -0
- data/lib/woolen_common/pcap/mu/pcap/udp.rb +81 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap.rb +175 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap/fields.rb +51 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap/rtp.rb +72 -0
- data/lib/woolen_common/pcap/pcap.rb +115 -0
- data/lib/woolen_common/pcap/readme.md +72 -0
- data/lib/woolen_common/ruby_ext/blank.rb +126 -0
- data/lib/woolen_common/ruby_ext/drb_ext.rb +7 -0
- data/lib/woolen_common/ruby_ext/string.rb +116 -0
- data/lib/woolen_common/ruby_ext/win32_ole.rb +4 -0
- data/lib/woolen_common/ruby_proxy.rb +5 -0
- data/lib/woolen_common/ruby_proxy/client.rb +305 -0
- data/lib/woolen_common/ruby_proxy/config.rb +44 -0
- data/lib/woolen_common/ruby_proxy/exceptions.rb +17 -0
- data/lib/woolen_common/ruby_proxy/proxy.rb +157 -0
- data/lib/woolen_common/ruby_proxy/proxy_global_set.rb +44 -0
- data/lib/woolen_common/ruby_proxy/proxy_load.rb +34 -0
- data/lib/woolen_common/ruby_proxy/server.rb +53 -0
- data/lib/woolen_common/splib.rb +36 -0
- data/lib/woolen_common/splib/Array.rb +33 -0
- data/lib/woolen_common/splib/CodeReloader.rb +59 -0
- data/lib/woolen_common/splib/Constants.rb +44 -0
- data/lib/woolen_common/splib/Conversions.rb +47 -0
- data/lib/woolen_common/splib/Exec.rb +132 -0
- data/lib/woolen_common/splib/Float.rb +13 -0
- data/lib/woolen_common/splib/HumanIdealRandomIterator.rb +40 -0
- data/lib/woolen_common/splib/Monitor.rb +214 -0
- data/lib/woolen_common/splib/PriorityQueue.rb +110 -0
- data/lib/woolen_common/splib/Sleep.rb +10 -0
- data/lib/woolen_common/splib/UrlShorteners.rb +48 -0
- data/lib/woolen_common/ssh_proxy.rb +146 -0
- data/lib/woolen_common/system_helper.rb +123 -0
- data/lib/woolen_common/system_monitor.rb +23 -0
- data/lib/woolen_common/system_monitor/linux_monitor.rb +250 -0
- data/lib/woolen_common/system_monitor/windows_monitor.rb +145 -0
- data/lib/woolen_common/type_helper.rb +42 -0
- data/lib/woolen_common/ver_ctrl_middle_ware.rb +92 -0
- data/lib/woolen_common/version.rb +3 -0
- metadata +210 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
module Mu
|
7
|
+
class Pcap
|
8
|
+
|
9
|
+
class UDP < Packet
|
10
|
+
class << self
|
11
|
+
def build_pkt(ip_pkt,src_port,dst_port)
|
12
|
+
if ip_pkt && ip_pkt.kind_of?(IP)
|
13
|
+
the_udp_pkt = self.new(src_port,dst_port)
|
14
|
+
ip_pkt.payload = the_udp_pkt
|
15
|
+
else
|
16
|
+
raise 'can not build udp pkt with not ip pkt'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
attr_accessor :src_port, :dst_port
|
21
|
+
|
22
|
+
def initialize(src_port=0, dst_port=0)
|
23
|
+
super()
|
24
|
+
@src_port = src_port
|
25
|
+
@dst_port = dst_port
|
26
|
+
end
|
27
|
+
|
28
|
+
def flow_id
|
29
|
+
return [:udp, @src_port, @dst_port]
|
30
|
+
end
|
31
|
+
|
32
|
+
FMT_nnnn = 'nnnn'
|
33
|
+
|
34
|
+
def self.from_bytes bytes
|
35
|
+
bytes_length = bytes.length
|
36
|
+
bytes_length >= 8 or
|
37
|
+
raise ParseError, "Truncated UDP header: expected 8 bytes, got #{bytes_length} bytes"
|
38
|
+
sport, dport, length, checksum = bytes.unpack(FMT_nnnn)
|
39
|
+
bytes_length >= length or
|
40
|
+
raise ParseError, "Truncated UDP packet: expected #{length} bytes, got #{bytes_length} bytes"
|
41
|
+
udp = UDP.new sport, dport
|
42
|
+
udp.payload_raw = bytes[8..-1]
|
43
|
+
udp.payload = bytes[8..length]
|
44
|
+
return udp
|
45
|
+
end
|
46
|
+
|
47
|
+
def write io, ip
|
48
|
+
length = @payload.bytesize
|
49
|
+
length_8 = length + 8
|
50
|
+
if length_8 > 65535
|
51
|
+
Pcap.warning "UDP payload is too large"
|
52
|
+
end
|
53
|
+
pseudo_header = ip.pseudo_header length_8
|
54
|
+
header = [@src_port, @dst_port, length_8, 0] \
|
55
|
+
.pack FMT_nnnn
|
56
|
+
checksum = IP.checksum(pseudo_header + header + @payload)
|
57
|
+
header = [@src_port, @dst_port, length_8, checksum] \
|
58
|
+
.pack FMT_nnnn
|
59
|
+
io.write header
|
60
|
+
io.write @payload
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.udp? packet
|
64
|
+
return packet.is_a?(Ethernet) &&
|
65
|
+
packet.payload.is_a?(IP) &&
|
66
|
+
packet.payload.payload.is_a?(UDP)
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
return "udp(%d, %d, %s)" % [@src_port, @dst_port, @payload.inspect]
|
71
|
+
end
|
72
|
+
|
73
|
+
def == other
|
74
|
+
return super &&
|
75
|
+
self.src_port == other.src_port &&
|
76
|
+
self.dst_port == other.dst_port
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
require 'tempfile'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'mu/scenario/pcap/fields'
|
9
|
+
require 'mu/pcap'
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
module Mu
|
13
|
+
class Scenario
|
14
|
+
|
15
|
+
module Pcap
|
16
|
+
TSHARK_READ_TIMEOUT = 10.0 # seconds
|
17
|
+
TSHARK_LINES_PER_PACKET = 16384
|
18
|
+
TSHARK_OPTS = "-n -o tcp.desegment_tcp_streams:false"
|
19
|
+
TSHARK_OPTS_SUFFIX = TSHARK_OPTS
|
20
|
+
TSHARK_SIZE_OPTS = "-n -o 'column.format: cum_size, \"%B\"'"
|
21
|
+
TSHARK_PSML_OPTS = %Q{#{TSHARK_OPTS} -o 'column.format: "Protocol", "%p", "Info", "%i"'}
|
22
|
+
|
23
|
+
MAX_PCAP_SIZE = 102400 # 100KB
|
24
|
+
MAX_RAW_PCAP_SIZE_MB = 25
|
25
|
+
MAX_RAW_PCAP_SIZE = MAX_RAW_PCAP_SIZE_MB * 1024 * 1000
|
26
|
+
EXCLUDE_FROM_SIZE_CHECK = ['rtp'].freeze
|
27
|
+
|
28
|
+
class PcapTooLarge < StandardError;
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.reset_options options
|
32
|
+
return unless options
|
33
|
+
tshark_opts = options << ' ' << TSHARK_OPTS_SUFFIX
|
34
|
+
remove_const(:TSHARK_OPTS) if const_defined?(:TSHARK_OPTS)
|
35
|
+
const_set(:TSHARK_OPTS, tshark_opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.validate_pcap_size(path)
|
39
|
+
tshark_filter = EXCLUDE_FROM_SIZE_CHECK.map { |proto| "not #{proto}" }.join " and "
|
40
|
+
io = ::IO.popen "tshark #{TSHARK_SIZE_OPTS} -r #{path} -R '#{tshark_filter}' | tail -1"
|
41
|
+
if ::IO.select [io], nil, nil, TSHARK_READ_TIMEOUT
|
42
|
+
if io.eof?
|
43
|
+
size = 0
|
44
|
+
else
|
45
|
+
last_line = io.readline
|
46
|
+
size = last_line.to_i
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if size.nil? or size == 0
|
51
|
+
size = File.size(path)
|
52
|
+
end
|
53
|
+
|
54
|
+
if size > MAX_PCAP_SIZE
|
55
|
+
raise PcapTooLarge, "Selected packets have a size of #{size} bytes which " +
|
56
|
+
"exceeds the #{MAX_PCAP_SIZE} byte maximum."
|
57
|
+
end
|
58
|
+
|
59
|
+
if size > MAX_RAW_PCAP_SIZE
|
60
|
+
raise PcapTooLarge, "Selected packets have a raw size of #{size} bytes which " +
|
61
|
+
"exceeds the #{MAX_RAW_PCAP_SIZE_MB}MB maximum."
|
62
|
+
end
|
63
|
+
|
64
|
+
return size
|
65
|
+
end
|
66
|
+
|
67
|
+
PAR_VERSION = 1
|
68
|
+
|
69
|
+
def self.export_to_par pcap_path, opts=nil
|
70
|
+
opts ||= {}
|
71
|
+
|
72
|
+
# Open pcap file
|
73
|
+
File.exist?(pcap_path) or raise "Cannot open file '#{pcap_path}'."
|
74
|
+
validate_pcap_size pcap_path
|
75
|
+
pcap = open pcap_path, 'rb'
|
76
|
+
|
77
|
+
# Get Mu::Pcap::Packets
|
78
|
+
packets = to_pcap_packets pcap, opts[:isolate_l7]
|
79
|
+
|
80
|
+
# Write normalized packets to tempfile
|
81
|
+
tmpdir = Dir.mktmpdir
|
82
|
+
norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
|
83
|
+
pcap = Mu::Pcap.from_packets packets
|
84
|
+
pcap.write norm_pcap
|
85
|
+
norm_pcap.close
|
86
|
+
|
87
|
+
# Get wireshark dissected field values for all packets.
|
88
|
+
`tshark -T fields #{TSHARK_OPTS} #{Fields::TSHARK_OPTS} -Eseparator='\xff' -r #{norm_pcap.path} > #{tmpdir}/fields`
|
89
|
+
fields = open "#{tmpdir}/fields", 'rb'
|
90
|
+
|
91
|
+
# Get wireshark dissected field values for all packets.
|
92
|
+
fields_array = []
|
93
|
+
if fields
|
94
|
+
packets.each do |packet|
|
95
|
+
fields_array << Fields.next_from_io(fields)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Protocol specific preprocessing, packets may be deleted.
|
100
|
+
Rtp.preprocess packets, fields_array
|
101
|
+
|
102
|
+
File.open "#{tmpdir}/packets.dump", 'wb' do |f|
|
103
|
+
Marshal.dump packets, f
|
104
|
+
end
|
105
|
+
|
106
|
+
# Create a second pcap with packets removed.
|
107
|
+
norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
|
108
|
+
pcap = Mu::Pcap.from_packets packets
|
109
|
+
pcap.write norm_pcap
|
110
|
+
norm_pcap.close
|
111
|
+
|
112
|
+
# Dump PSML to file.
|
113
|
+
# (The no-op filter sometimes produces slighty more verbose descriptions.)
|
114
|
+
`tshark -T psml #{TSHARK_PSML_OPTS} -r #{norm_pcap.path} -R 'rtp or not rtp' > #{tmpdir}/psml`
|
115
|
+
|
116
|
+
# Dump PDML io file.
|
117
|
+
`tshark -T pdml #{TSHARK_OPTS} -r #{norm_pcap.path} > #{tmpdir}/pdml`
|
118
|
+
pdml = open "#{tmpdir}/pdml", 'rb'
|
119
|
+
|
120
|
+
# Create about
|
121
|
+
open "#{tmpdir}/about", 'w' do |about|
|
122
|
+
about.puts({ :par_version => PAR_VERSION }.to_json)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Create zip
|
126
|
+
Dir.chdir tmpdir do
|
127
|
+
system "zip -q dissected.zip about pdml psml fields packets.dump normalized.pcap"
|
128
|
+
return open("dissected.zip")
|
129
|
+
end
|
130
|
+
ensure
|
131
|
+
if tmpdir
|
132
|
+
FileUtils.rm_rf tmpdir
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.to_pcap_packets io, isolate_l7=true
|
137
|
+
packets = []
|
138
|
+
|
139
|
+
# Read Pcap packets from Pcap
|
140
|
+
Mu::Pcap.each_pkthdr io do |pkthdr|
|
141
|
+
if pkthdr.len != pkthdr.caplen
|
142
|
+
raise Mu::Pcap::ParseError, "Error: Capture contains truncated packets. " +
|
143
|
+
"Try recapturing with an increased snapshot length."
|
144
|
+
end
|
145
|
+
if not pkthdr.pkt.is_a? Mu::Pcap::Ethernet
|
146
|
+
warning 'Unable to parse packet, skipping.'
|
147
|
+
end
|
148
|
+
packets << pkthdr.pkt
|
149
|
+
end
|
150
|
+
|
151
|
+
if (packets.length == 0)
|
152
|
+
raise Mu::Pcap::ParseError, "No valid packets found!"
|
153
|
+
end
|
154
|
+
|
155
|
+
packets = Mu::Pcap::IPv4.reassemble packets
|
156
|
+
|
157
|
+
if isolate_l7
|
158
|
+
packets = Mu::Pcap::Packet.isolate_l7 packets
|
159
|
+
end
|
160
|
+
|
161
|
+
packets = Mu::Pcap::Packet.normalize packets
|
162
|
+
packets = Mu::Pcap::TCP.split packets
|
163
|
+
|
164
|
+
packets
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.warning msg
|
168
|
+
$stderr.puts "WARNING: #{msg}" #, caller, $!
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
require 'mu/scenario/pcap/rtp'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
require 'mu/scenario/pcap'
|
7
|
+
|
8
|
+
module Mu
|
9
|
+
class Scenario
|
10
|
+
module Pcap
|
11
|
+
|
12
|
+
class Fields
|
13
|
+
FIELDS = [
|
14
|
+
:rtp,
|
15
|
+
:"rtp.setup-frame"
|
16
|
+
].freeze
|
17
|
+
FIELD_COUNT = FIELDS.length
|
18
|
+
SEPARATOR = "\xff".freeze
|
19
|
+
TSHARK_OPTS = "-Eseparator='#{SEPARATOR}'"
|
20
|
+
FIELDS.each do |field|
|
21
|
+
TSHARK_OPTS << " -e #{field}"
|
22
|
+
end
|
23
|
+
TSHARK_OPTS.freeze
|
24
|
+
|
25
|
+
def self.readline io
|
26
|
+
if ::IO.select [io], nil, nil, Pcap::TSHARK_READ_TIMEOUT
|
27
|
+
return io.readline.chomp
|
28
|
+
end
|
29
|
+
|
30
|
+
raise Errno::ETIMEDOUT, "read timed out"
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.next_from_io io
|
34
|
+
if line = readline(io)
|
35
|
+
fields = line.split SEPARATOR, FIELD_COUNT
|
36
|
+
hash = {}
|
37
|
+
FIELDS.each do |key|
|
38
|
+
val = fields.shift
|
39
|
+
hash[key] = val.empty? ? nil : val
|
40
|
+
end
|
41
|
+
return hash
|
42
|
+
end
|
43
|
+
rescue Exception => e
|
44
|
+
Pcap.warning e.message
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
module Mu
|
7
|
+
class Scenario
|
8
|
+
module Pcap
|
9
|
+
module Rtp
|
10
|
+
|
11
|
+
TRUNC_COUNT = 5
|
12
|
+
|
13
|
+
def self.preprocess packets, fields_per_packet
|
14
|
+
signaled_by = []
|
15
|
+
prev_signal_frame = {}
|
16
|
+
packets.each_with_index do |packet, idx|
|
17
|
+
fields = fields_per_packet[idx]
|
18
|
+
if fields and fields[:rtp]
|
19
|
+
flow_id = packet.flow_id
|
20
|
+
if frame = fields[:"rtp.setup-frame"]
|
21
|
+
prev_signal_frame[flow_id] = frame
|
22
|
+
else
|
23
|
+
if frame = prev_signal_frame[flow_id]
|
24
|
+
fields[:"rtp.setup-frame"] = frame
|
25
|
+
else
|
26
|
+
packets[idx] = nil
|
27
|
+
fields_per_packet[idx] = nil
|
28
|
+
next
|
29
|
+
end
|
30
|
+
end
|
31
|
+
sig_idx = frame.to_i
|
32
|
+
signaled_by[idx] = sig_idx
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
flow_to_count = Hash.new 0
|
37
|
+
prev_setup_frame = {}
|
38
|
+
keep_frames = []
|
39
|
+
packets.each_with_index do |packet, idx|
|
40
|
+
if setup_frame = signaled_by[idx]
|
41
|
+
flow = packet.flow_id
|
42
|
+
count = flow_to_count[flow]
|
43
|
+
if setup_frame != prev_setup_frame[flow]
|
44
|
+
prev_setup_frame[flow] = setup_frame
|
45
|
+
count = 1
|
46
|
+
else
|
47
|
+
count += 1
|
48
|
+
end
|
49
|
+
if count <= TRUNC_COUNT
|
50
|
+
keep_frames << idx + 1
|
51
|
+
else
|
52
|
+
packets[idx] = nil
|
53
|
+
fields_per_packet[idx] = nil
|
54
|
+
end
|
55
|
+
flow_to_count[flow] = count
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
packets.reject! { |p| not p }
|
60
|
+
fields_per_packet.reject! { |p| not p }
|
61
|
+
|
62
|
+
filter = "not rtp"
|
63
|
+
keep_frames.each do |frame|
|
64
|
+
filter << " or frame.number == #{frame}"
|
65
|
+
end
|
66
|
+
|
67
|
+
return filter
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# http://www.mudynamics.com
|
3
|
+
# http://labs.mudynamics.com
|
4
|
+
# http://www.pcapr.net
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
7
|
+
|
8
|
+
require 'socket'
|
9
|
+
require 'stringio'
|
10
|
+
require 'mu/fixnum_ext'
|
11
|
+
|
12
|
+
module Mu
|
13
|
+
|
14
|
+
class Pcap
|
15
|
+
class ParseError < StandardError;
|
16
|
+
end
|
17
|
+
|
18
|
+
LITTLE_ENDIAN = 0xd4c3b2a1
|
19
|
+
BIG_ENDIAN = 0xa1b2c3d4
|
20
|
+
|
21
|
+
DLT_NULL = 0
|
22
|
+
DLT_EN10MB = 1
|
23
|
+
DLT_RAW = 12 # DLT_LOOP in OpenBSD
|
24
|
+
DLT_LINUX_SLL = 113
|
25
|
+
|
26
|
+
attr_accessor :header, :pkthdrs
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@header = Header.new
|
30
|
+
@pkthdrs = []
|
31
|
+
end
|
32
|
+
|
33
|
+
# Read PCAP file from IO and return Mu::Pcap. If decode is true, also
|
34
|
+
# decode the Pkthdr packet contents to Mu::Pcap objects.
|
35
|
+
def self.read io, decode=true
|
36
|
+
pcap = Pcap.new
|
37
|
+
pcap.header = each_pkthdr(io, decode) do |pkthdr|
|
38
|
+
pcap.pkthdrs << pkthdr
|
39
|
+
end
|
40
|
+
return pcap
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create PCAP from list of packets.
|
44
|
+
def self.from_packets packets
|
45
|
+
pcap = Pcap.new
|
46
|
+
packets.each do |packet|
|
47
|
+
pkthdr = Mu::Pcap::Pkthdr.new
|
48
|
+
pkthdr.pkt = packet
|
49
|
+
pcap.pkthdrs << pkthdr
|
50
|
+
end
|
51
|
+
return pcap
|
52
|
+
end
|
53
|
+
|
54
|
+
# Write PCAP file to IO. Uses big-endian and linktype EN10MB.
|
55
|
+
def write io
|
56
|
+
@header.write io
|
57
|
+
@pkthdrs.each do |pkthdr|
|
58
|
+
pkthdr.write io
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Read PCAP packet headers from IO and return Mu::Pcap::Header. If decode
|
63
|
+
# is true, also decode the Pkthdr packet contents to Mu::Pcap objects. Use
|
64
|
+
# this for large files when each packet header can processed independently
|
65
|
+
# - it will perform better.
|
66
|
+
def self.each_pkthdr io, decode=true
|
67
|
+
header = Header.read io
|
68
|
+
while not io.eof?
|
69
|
+
pkthdr = Pkthdr.read io, header.magic
|
70
|
+
if decode
|
71
|
+
pkthdr.decode! header.magic, header.linktype
|
72
|
+
end
|
73
|
+
yield pkthdr
|
74
|
+
end
|
75
|
+
return header
|
76
|
+
end
|
77
|
+
|
78
|
+
# Read packets from PCAP
|
79
|
+
def self.read_packets io, decode=true
|
80
|
+
packets = []
|
81
|
+
each_pkthdr(io) { |pkthdr| packets << pkthdr.pkt }
|
82
|
+
return packets
|
83
|
+
end
|
84
|
+
|
85
|
+
# Assertion used during Pcap parsing
|
86
|
+
def self.assert cond, msg
|
87
|
+
if not cond
|
88
|
+
raise ParseError, msg
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Warnings from Pcap parsing are printed using this method.
|
93
|
+
def self.warning msg
|
94
|
+
$stderr.puts "WARNING: #{msg}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def == other
|
98
|
+
return self.class == other.class &&
|
99
|
+
self.header == other.header &&
|
100
|
+
self.pkthdrs == other.pkthdrs
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
require 'mu/pcap/header'
|
107
|
+
require 'mu/pcap/pkthdr'
|
108
|
+
require 'mu/pcap/packet'
|
109
|
+
require 'mu/pcap/ethernet'
|
110
|
+
require 'mu/pcap/ip'
|
111
|
+
require 'mu/pcap/ipv4'
|
112
|
+
require 'mu/pcap/ipv6'
|
113
|
+
require 'mu/pcap/tcp'
|
114
|
+
require 'mu/pcap/udp'
|
115
|
+
require 'mu/pcap/sctp'
|