pcapr-local 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/LICENSE.txt +20 -0
- data/README.md +64 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/bin/pcap2par +49 -0
- data/bin/startpcapr +40 -0
- data/bin/stoppcapr +33 -0
- data/bin/xtractr +5 -0
- data/lib/environment.rb +106 -0
- data/lib/exe/xtractr +0 -0
- data/lib/mu/pcap.rb +110 -0
- data/lib/mu/pcap/ethernet.rb +148 -0
- data/lib/mu/pcap/header.rb +75 -0
- data/lib/mu/pcap/io_pair.rb +67 -0
- data/lib/mu/pcap/io_wrapper.rb +76 -0
- data/lib/mu/pcap/ip.rb +61 -0
- data/lib/mu/pcap/ipv4.rb +257 -0
- data/lib/mu/pcap/ipv6.rb +148 -0
- data/lib/mu/pcap/packet.rb +104 -0
- data/lib/mu/pcap/pkthdr.rb +155 -0
- data/lib/mu/pcap/reader.rb +61 -0
- data/lib/mu/pcap/reader/http_family.rb +170 -0
- data/lib/mu/pcap/sctp.rb +367 -0
- data/lib/mu/pcap/sctp/chunk.rb +123 -0
- data/lib/mu/pcap/sctp/chunk/data.rb +134 -0
- data/lib/mu/pcap/sctp/chunk/init.rb +100 -0
- data/lib/mu/pcap/sctp/chunk/init_ack.rb +68 -0
- data/lib/mu/pcap/sctp/parameter.rb +110 -0
- data/lib/mu/pcap/sctp/parameter/ip_address.rb +48 -0
- data/lib/mu/pcap/stream_packetizer.rb +72 -0
- data/lib/mu/pcap/tcp.rb +505 -0
- data/lib/mu/pcap/udp.rb +69 -0
- data/lib/mu/scenario/pcap.rb +164 -0
- data/lib/mu/scenario/pcap/fields.rb +50 -0
- data/lib/mu/scenario/pcap/rtp.rb +71 -0
- data/lib/pcapr_local.rb +159 -0
- data/lib/pcapr_local/config.rb +336 -0
- data/lib/pcapr_local/db.rb +197 -0
- data/lib/pcapr_local/scanner.rb +250 -0
- data/lib/pcapr_local/server.rb +178 -0
- data/lib/pcapr_local/www/favicon.ico +0 -0
- data/lib/pcapr_local/www/favicon.png +0 -0
- data/lib/pcapr_local/www/home/index.html +138 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png.1 +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Download.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Folder3.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Full Size.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Minus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Plus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Search.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/User.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Phone.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Video.png +0 -0
- data/lib/pcapr_local/www/static/image/bar-orange.gif +0 -0
- data/lib/pcapr_local/www/static/image/beta.png +0 -0
- data/lib/pcapr_local/www/static/image/bg.png +0 -0
- data/lib/pcapr_local/www/static/image/blockquote.png +0 -0
- data/lib/pcapr_local/www/static/image/body-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl5-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl6-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl7-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl8-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-l.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-r.png +0 -0
- data/lib/pcapr_local/www/static/image/btn-search.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-5.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-6.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-7.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-pathway.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section2.png +0 -0
- data/lib/pcapr_local/www/static/image/collapsed.gif +0 -0
- data/lib/pcapr_local/www/static/image/crosslink.png +0 -0
- data/lib/pcapr_local/www/static/image/expanded.gif +0 -0
- data/lib/pcapr_local/www/static/image/favicon.ico +0 -0
- data/lib/pcapr_local/www/static/image/favicon.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-author.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-created.png +0 -0
- data/lib/pcapr_local/www/static/image/p-expand.gif +0 -0
- data/lib/pcapr_local/www/static/image/pcapr-logo.png +0 -0
- data/lib/pcapr_local/www/static/image/powered-by.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/status-alert.png +0 -0
- data/lib/pcapr_local/www/static/image/status-download.png +0 -0
- data/lib/pcapr_local/www/static/image/status-info.png +0 -0
- data/lib/pcapr_local/www/static/image/status-note.png +0 -0
- data/lib/pcapr_local/www/static/image/tab-round.png +0 -0
- data/lib/pcapr_local/www/static/image/throbber.gif +0 -0
- data/lib/pcapr_local/www/static/image/user.jpg +0 -0
- data/lib/pcapr_local/www/static/script/closet/async.js +421 -0
- data/lib/pcapr_local/www/static/script/closet/closet.api.js +241 -0
- data/lib/pcapr_local/www/static/script/closet/closet.folders.js +94 -0
- data/lib/pcapr_local/www/static/script/closet/closet.js +187 -0
- data/lib/pcapr_local/www/static/script/closet/closet.mr.js +219 -0
- data/lib/pcapr_local/www/static/script/closet/closet.options.js +359 -0
- data/lib/pcapr_local/www/static/script/closet/closet.quantity.js +73 -0
- data/lib/pcapr_local/www/static/script/closet/closet.render.js +205 -0
- data/lib/pcapr_local/www/static/script/closet/closet.report.js +86 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.http.js +135 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.overview.js +163 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.sip.js +159 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.tcp.js +72 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.visualize.js +263 -0
- data/lib/pcapr_local/www/static/script/closet/closet.util.js +40 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-1.4.2.min.js +154 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-ui.js +10921 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.js +2123 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.selection.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.stack.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.form.js +643 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.jsonp.min.js +3 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.menu.js +142 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.suggest.js +308 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.core.js +203 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.slider.js +629 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.sortable.js +1055 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.widget.js +236 -0
- data/lib/pcapr_local/www/static/script/json2.js +481 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.cache.js +115 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.template.js +117 -0
- data/lib/pcapr_local/www/static/script/sammy/sammy.js +1696 -0
- data/lib/pcapr_local/www/static/script/tipsy/jquery.tipsy.js +104 -0
- data/lib/pcapr_local/www/static/style/c3p0.css +116 -0
- data/lib/pcapr_local/www/static/style/jquery.suggest.css +27 -0
- data/lib/pcapr_local/www/static/style/page.css +1113 -0
- data/lib/pcapr_local/www/static/style/tipsy.css +7 -0
- data/lib/pcapr_local/www/templates/browse.services.template +10 -0
- data/lib/pcapr_local/www/templates/browse.template +77 -0
- data/lib/pcapr_local/www/templates/flows.template +38 -0
- data/lib/pcapr_local/www/templates/pcap.template +63 -0
- data/lib/pcapr_local/www/templates/sip.calls.template +35 -0
- data/lib/pcapr_local/www/templates/statistics.template +6 -0
- data/lib/pcapr_local/xtractr.rb +179 -0
- data/lib/pcapr_local/xtractr/instance.rb +172 -0
- data/pcapr-local.gemspec +297 -0
- data/test/mu/pcap/reader/tc_http_family.rb +251 -0
- data/test/mu/pcap/tc_ethernet.rb +71 -0
- data/test/mu/pcap/tc_header.rb +56 -0
- data/test/mu/pcap/tc_ipv4.rb +103 -0
- data/test/mu/pcap/tc_ipv6.rb +83 -0
- data/test/mu/pcap/tc_packet.rb +44 -0
- data/test/mu/pcap/tc_pair.rb +58 -0
- data/test/mu/pcap/tc_pkthdr.rb +33 -0
- data/test/mu/pcap/tc_reader.rb +76 -0
- data/test/mu/pcap/tc_tcp.rb +426 -0
- data/test/mu/pcap/tc_udp.rb +33 -0
- data/test/mu/pcap/tc_wrapper.rb +80 -0
- data/test/mu/scenario/pcap/tc_fields.rb +67 -0
- data/test/mu/scenario/pcap/tc_rtp.rb +135 -0
- data/test/mu/scenario/sip_signalled_call_1.pcap +0 -0
- data/test/mu/scenario/tc_pcap.rb +190 -0
- data/test/mu/scenario/test_data/arp.pcap +0 -0
- data/test/mu/scenario/test_data/dns.pcap +0 -0
- data/test/mu/scenario/test_data/http-v6.pcap +0 -0
- data/test/mu/scenario/test_data/http.pcap +0 -0
- data/test/mu/scenario/test_data/http_chunked.pcap +0 -0
- data/test/mu/scenario/test_data/http_deflate.pcap +0 -0
- data/test/mu/scenario/test_data/httpauth3.pcap +0 -0
- data/test/mu/scenario/test_data/icmp.pcap +0 -0
- data/test/mu/scenario/test_data/sip_signalled_call_1.pcap +0 -0
- data/test/mu/tc_pcap.rb +39 -0
- data/test/mu/testcase.rb +86 -0
- data/test/pcapr_local/arp.pcap +0 -0
- data/test/pcapr_local/data.js +3 -0
- data/test/pcapr_local/http_chunked.pcap +0 -0
- data/test/pcapr_local/tc_api.rb +181 -0
- data/test/pcapr_local/test.tgz +0 -0
- data/test/pcapr_local/test_scanner.rb +241 -0
- data/test/pcapr_local/test_xtractr.rb +219 -0
- data/test/pcapr_local/testcase.rb +107 -0
- data/test/test_export_to_scenario.sh +25 -0
- data/test/test_pcapr_local.rb +29 -0
- metadata +450 -0
data/lib/mu/pcap/udp.rb
ADDED
@@ -0,0 +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
|
@@ -0,0 +1,164 @@
|
|
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_SIZE_OPTS = "-n -o 'column.format: cum_size, \"%B\"'"
|
19
|
+
TSHARK_PSML_OPTS = %Q{#{TSHARK_OPTS} -o 'column.format: "Protocol", "%p", "Info", "%i"'}
|
20
|
+
|
21
|
+
MAX_PCAP_SIZE = 102400 # 100KB
|
22
|
+
MAX_RAW_PCAP_SIZE_MB = 25
|
23
|
+
MAX_RAW_PCAP_SIZE = MAX_RAW_PCAP_SIZE_MB * 1024 * 1000
|
24
|
+
EXCLUDE_FROM_SIZE_CHECK = ['rtp'].freeze
|
25
|
+
|
26
|
+
class PcapTooLarge < StandardError; end
|
27
|
+
|
28
|
+
def self.validate_pcap_size(path)
|
29
|
+
tshark_filter = EXCLUDE_FROM_SIZE_CHECK.map{ |proto| "not #{proto}" }.join " and "
|
30
|
+
io = ::IO.popen "tshark #{TSHARK_SIZE_OPTS} -r #{path} -R '#{tshark_filter}' | tail -1"
|
31
|
+
if ::IO.select [ io ], nil, nil, TSHARK_READ_TIMEOUT
|
32
|
+
if io.eof?
|
33
|
+
size = 0
|
34
|
+
else
|
35
|
+
last_line = io.readline
|
36
|
+
size = last_line.to_i
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if size.nil? or size == 0
|
41
|
+
size = File.size(path)
|
42
|
+
end
|
43
|
+
|
44
|
+
if size > MAX_PCAP_SIZE
|
45
|
+
raise PcapTooLarge, "Selected packets have a size of #{size} bytes which " +
|
46
|
+
"exceeds the #{MAX_PCAP_SIZE} byte maximum."
|
47
|
+
end
|
48
|
+
|
49
|
+
if size > MAX_RAW_PCAP_SIZE
|
50
|
+
raise PcapTooLarge, "Selected packets have a raw size of #{size} bytes which " +
|
51
|
+
"exceeds the #{MAX_RAW_PCAP_SIZE_MB}MB maximum."
|
52
|
+
end
|
53
|
+
|
54
|
+
return size
|
55
|
+
end
|
56
|
+
|
57
|
+
PAR_VERSION = 1
|
58
|
+
def self.export_to_par pcap_path, opts=nil
|
59
|
+
opts ||= {}
|
60
|
+
|
61
|
+
# Open pcap file
|
62
|
+
File.exist?(pcap_path) or raise "Cannot open file '#{pcap_path}'."
|
63
|
+
validate_pcap_size pcap_path
|
64
|
+
pcap = open pcap_path, 'rb'
|
65
|
+
|
66
|
+
# Get Mu::Pcap::Packets
|
67
|
+
packets = to_pcap_packets pcap, opts[:isolate_l7]
|
68
|
+
|
69
|
+
# Write normalized packets to tempfile
|
70
|
+
tmpdir = Dir.mktmpdir
|
71
|
+
norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
|
72
|
+
pcap = Mu::Pcap.from_packets packets
|
73
|
+
pcap.write norm_pcap
|
74
|
+
norm_pcap.close
|
75
|
+
|
76
|
+
# Get wireshark dissected field values for all packets.
|
77
|
+
`tshark -T fields #{TSHARK_OPTS} #{Fields::TSHARK_OPTS} -Eseparator='\xff' -r #{norm_pcap.path} > #{tmpdir}/fields`
|
78
|
+
fields = open "#{tmpdir}/fields", 'rb'
|
79
|
+
|
80
|
+
# Get wireshark dissected field values for all packets.
|
81
|
+
fields_array = []
|
82
|
+
if fields
|
83
|
+
packets.each do |packet|
|
84
|
+
fields_array << Fields.next_from_io(fields)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Protocol specific preprocessing, packets may be deleted.
|
89
|
+
Rtp.preprocess packets, fields_array
|
90
|
+
|
91
|
+
File.open "#{tmpdir}/packets.dump", 'wb' do |f|
|
92
|
+
Marshal.dump packets, f
|
93
|
+
end
|
94
|
+
|
95
|
+
# Create a second pcap with packets removed.
|
96
|
+
norm_pcap = File.open "#{tmpdir}/normalized.pcap", 'wb'
|
97
|
+
pcap = Mu::Pcap.from_packets packets
|
98
|
+
pcap.write norm_pcap
|
99
|
+
norm_pcap.close
|
100
|
+
|
101
|
+
# Dump PSML to file.
|
102
|
+
# (The no-op filter sometimes produces slighty more verbose descriptions.)
|
103
|
+
`tshark -T psml #{TSHARK_PSML_OPTS} -r #{norm_pcap.path} -R 'rtp or not rtp' > #{tmpdir}/psml`
|
104
|
+
|
105
|
+
# Dump PDML io file.
|
106
|
+
`tshark -T pdml #{TSHARK_OPTS} -r #{norm_pcap.path} > #{tmpdir}/pdml`
|
107
|
+
pdml = open "#{tmpdir}/pdml", 'rb'
|
108
|
+
|
109
|
+
# Create about
|
110
|
+
open "#{tmpdir}/about", 'w' do |about|
|
111
|
+
about.puts({:par_version => PAR_VERSION}.to_json)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Create zip
|
115
|
+
Dir.chdir tmpdir do
|
116
|
+
system "zip -q dissected.zip about pdml psml fields packets.dump normalized.pcap"
|
117
|
+
return open("dissected.zip")
|
118
|
+
end
|
119
|
+
ensure
|
120
|
+
if tmpdir
|
121
|
+
FileUtils.rm_rf tmpdir
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.to_pcap_packets io, isolate_l7=true
|
126
|
+
packets = []
|
127
|
+
|
128
|
+
# Read Pcap packets from Pcap
|
129
|
+
Mu::Pcap.each_pkthdr io do |pkthdr|
|
130
|
+
if pkthdr.len != pkthdr.caplen
|
131
|
+
raise Mu::Pcap::ParseError, "Error: Capture contains truncated packets. " +
|
132
|
+
"Try recapturing with an increased snapshot length."
|
133
|
+
end
|
134
|
+
if not pkthdr.pkt.is_a? Mu::Pcap::Ethernet
|
135
|
+
warning 'Unable to parse packet, skipping.'
|
136
|
+
end
|
137
|
+
packets << pkthdr.pkt
|
138
|
+
end
|
139
|
+
|
140
|
+
if (packets.length == 0)
|
141
|
+
raise Mu::Pcap::ParseError, "No valid packets found!"
|
142
|
+
end
|
143
|
+
|
144
|
+
packets = Mu::Pcap::IPv4.reassemble packets
|
145
|
+
|
146
|
+
if isolate_l7
|
147
|
+
packets = Mu::Pcap::Packet.isolate_l7 packets
|
148
|
+
end
|
149
|
+
|
150
|
+
packets = Mu::Pcap::Packet.normalize packets
|
151
|
+
packets = Mu::Pcap::TCP.split packets
|
152
|
+
|
153
|
+
packets
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.warning msg
|
157
|
+
$stderr.puts "WARNING: #{msg}"#, caller, $!
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
require 'mu/scenario/pcap/rtp'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
require 'mu/scenario/pcap'
|
6
|
+
|
7
|
+
module Mu
|
8
|
+
class Scenario
|
9
|
+
module Pcap
|
10
|
+
|
11
|
+
class Fields
|
12
|
+
FIELDS = [
|
13
|
+
:rtp,
|
14
|
+
:"rtp.setup-frame"
|
15
|
+
].freeze
|
16
|
+
FIELD_COUNT = FIELDS.length
|
17
|
+
SEPARATOR = "\xff".freeze
|
18
|
+
TSHARK_OPTS = "-Eseparator='#{SEPARATOR}'"
|
19
|
+
FIELDS.each do |field|
|
20
|
+
TSHARK_OPTS << " -e #{field}"
|
21
|
+
end
|
22
|
+
TSHARK_OPTS.freeze
|
23
|
+
|
24
|
+
def self.readline io
|
25
|
+
if ::IO.select [ io ], nil, nil, Pcap::TSHARK_READ_TIMEOUT
|
26
|
+
return io.readline.chomp
|
27
|
+
end
|
28
|
+
|
29
|
+
raise Errno::ETIMEDOUT, "read timed out"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.next_from_io io
|
33
|
+
if line = readline(io)
|
34
|
+
fields = line.split SEPARATOR, FIELD_COUNT
|
35
|
+
hash = {}
|
36
|
+
FIELDS.each do |key|
|
37
|
+
val = fields.shift
|
38
|
+
hash[key] = val.empty? ? nil : val
|
39
|
+
end
|
40
|
+
return hash
|
41
|
+
end
|
42
|
+
rescue Exception => e
|
43
|
+
Pcap.warning e.message
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
module Mu
|
6
|
+
class Scenario
|
7
|
+
module Pcap
|
8
|
+
module Rtp
|
9
|
+
|
10
|
+
TRUNC_COUNT = 5
|
11
|
+
|
12
|
+
def self.preprocess packets, fields_per_packet
|
13
|
+
signaled_by = []
|
14
|
+
prev_signal_frame = {}
|
15
|
+
packets.each_with_index do |packet, idx|
|
16
|
+
fields = fields_per_packet[idx]
|
17
|
+
if fields and fields[:rtp]
|
18
|
+
flow_id = packet.flow_id
|
19
|
+
if frame = fields[:"rtp.setup-frame"]
|
20
|
+
prev_signal_frame[flow_id] = frame
|
21
|
+
else
|
22
|
+
if frame = prev_signal_frame[flow_id]
|
23
|
+
fields[:"rtp.setup-frame"] = frame
|
24
|
+
else
|
25
|
+
packets[idx] = nil
|
26
|
+
fields_per_packet[idx] = nil
|
27
|
+
next
|
28
|
+
end
|
29
|
+
end
|
30
|
+
sig_idx = frame.to_i
|
31
|
+
signaled_by[idx] = sig_idx
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
flow_to_count = Hash.new 0
|
36
|
+
prev_setup_frame = {}
|
37
|
+
keep_frames = []
|
38
|
+
packets.each_with_index do |packet, idx|
|
39
|
+
if setup_frame = signaled_by[idx]
|
40
|
+
flow = packet.flow_id
|
41
|
+
count = flow_to_count[flow]
|
42
|
+
if setup_frame != prev_setup_frame[flow]
|
43
|
+
prev_setup_frame[flow] = setup_frame
|
44
|
+
count = 1
|
45
|
+
else
|
46
|
+
count += 1
|
47
|
+
end
|
48
|
+
if count <= TRUNC_COUNT
|
49
|
+
keep_frames << idx + 1
|
50
|
+
else
|
51
|
+
packets[idx] = nil
|
52
|
+
fields_per_packet[idx] = nil
|
53
|
+
end
|
54
|
+
flow_to_count[flow] = count
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
packets.reject! {|p| not p }
|
59
|
+
fields_per_packet.reject! {|p| not p }
|
60
|
+
|
61
|
+
filter = "not rtp"
|
62
|
+
keep_frames.each do |frame|
|
63
|
+
filter << " or frame.number == #{frame}"
|
64
|
+
end
|
65
|
+
|
66
|
+
return filter
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/pcapr_local.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'environment'
|
7
|
+
require 'pcapr_local/config'
|
8
|
+
require 'pcapr_local/scanner'
|
9
|
+
require 'pcapr_local/server'
|
10
|
+
require 'pcapr_local/xtractr'
|
11
|
+
require 'logger'
|
12
|
+
|
13
|
+
module PcaprLocal
|
14
|
+
START_SCRIPT = File.join(ROOT, 'bin/pcapr_start.rb')
|
15
|
+
|
16
|
+
# We share a single Logger across all of pcapr.Local.
|
17
|
+
Logger = Logger.new(STDOUT)
|
18
|
+
|
19
|
+
# Recreate logger using configured log location.
|
20
|
+
LOGFILE = "server.log"
|
21
|
+
def self.start_logging log_dir
|
22
|
+
if log_dir and not log_dir.empty?
|
23
|
+
if const_defined? :Logger
|
24
|
+
remove_const :Logger
|
25
|
+
end
|
26
|
+
|
27
|
+
logfile = File.join(log_dir, LOGFILE)
|
28
|
+
FileUtils.mkdir_p log_dir
|
29
|
+
const_set :Logger, Logger.new(logfile, 5)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Start xtractr instance manager.
|
34
|
+
def self.start_xtractr config
|
35
|
+
xtractr_config = config['xtractr'].merge(
|
36
|
+
"index_dir" => config.fetch("index_dir"),
|
37
|
+
"pcap_dir" => config.fetch("pcap_dir")
|
38
|
+
)
|
39
|
+
Xtractr.new xtractr_config
|
40
|
+
end
|
41
|
+
|
42
|
+
# Start file system scanner.
|
43
|
+
def self.start_scanner config, db, xtractr
|
44
|
+
scanner_config = config.fetch('scanner').merge(
|
45
|
+
"index_dir" => config.fetch("index_dir"),
|
46
|
+
"pcap_dir" => config.fetch("pcap_dir"),
|
47
|
+
"db" => db,
|
48
|
+
"xtractr" => xtractr
|
49
|
+
)
|
50
|
+
PcaprLocal::Scanner.start scanner_config
|
51
|
+
end
|
52
|
+
|
53
|
+
# Start webserver UI/API
|
54
|
+
def self.start_app config, db, scanner, xtractr
|
55
|
+
app_config = config.fetch "app"
|
56
|
+
root = File.expand_path(File.dirname(__FILE__))
|
57
|
+
app_file = File.join(root, "pcapr_local/server.rb")
|
58
|
+
PcaprLocal::Server.run! \
|
59
|
+
:app_file => app_file,
|
60
|
+
:dump_errors => true,
|
61
|
+
:logging => true,
|
62
|
+
:port => app_config.fetch("port"),
|
63
|
+
:bind => app_config.fetch("host"),
|
64
|
+
:db => db,
|
65
|
+
:scanner => scanner,
|
66
|
+
:xtractr => xtractr
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.get_db config
|
70
|
+
PcaprLocal::DB.get_db config.fetch("couch")
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.start config=nil
|
74
|
+
config ||= PcaprLocal::Config.config
|
75
|
+
|
76
|
+
# Check that server is not already running.
|
77
|
+
check_pid_file config['pidfile']
|
78
|
+
|
79
|
+
# Start logging.
|
80
|
+
if config["log_dir"]
|
81
|
+
start_logging config['log_dir']
|
82
|
+
end
|
83
|
+
start_msg = "Starting server at #{config['app']['host']}:#{config['app']['port']}"
|
84
|
+
Logger.info start_msg
|
85
|
+
puts start_msg
|
86
|
+
puts "Log is at #{config['log_dir']}/#{LOGFILE}"
|
87
|
+
|
88
|
+
# Deamonize
|
89
|
+
unless config['debug_mode']
|
90
|
+
puts "Moving server process to the background. Run 'stoppcapr' to stop the server."
|
91
|
+
Process.daemon
|
92
|
+
end
|
93
|
+
|
94
|
+
# Create pid file that will be deleted when we shutdown.
|
95
|
+
create_pid_file config['pidfile']
|
96
|
+
|
97
|
+
# Get database instance
|
98
|
+
db = get_db config
|
99
|
+
|
100
|
+
# Xtractr manager
|
101
|
+
xtractr = start_xtractr config
|
102
|
+
|
103
|
+
# Start scanner thread
|
104
|
+
scanner = start_scanner config, db, xtractr
|
105
|
+
|
106
|
+
# Start application server
|
107
|
+
start_app config, db, scanner, xtractr
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.check_pid_file file
|
111
|
+
if File.exist? file
|
112
|
+
# If we get Errno::ESRCH then process does not exist and
|
113
|
+
# we can safely cleanup the pid file.
|
114
|
+
pid = File.read(file).to_i
|
115
|
+
begin
|
116
|
+
Process.kill(0, pid)
|
117
|
+
rescue Errno::ESRCH
|
118
|
+
stale_pid = true
|
119
|
+
rescue
|
120
|
+
end
|
121
|
+
|
122
|
+
unless stale_pid
|
123
|
+
puts "Server is already running (pid=#{pid})"
|
124
|
+
exit
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.create_pid_file file
|
130
|
+
File.open(file, "w") { |f| f.puts Process.pid }
|
131
|
+
|
132
|
+
# Remove pid file during shutdown
|
133
|
+
at_exit do
|
134
|
+
Logger.info "Shutting down." rescue nil
|
135
|
+
if File.exist? file
|
136
|
+
File.unlink file
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Sends SIGTERM to process in pidfile. Server should trap this
|
142
|
+
# and shutdown cleanly.
|
143
|
+
def self.stop config=nil
|
144
|
+
user_config = Config.user_config_path
|
145
|
+
if File.exist?(user_config)
|
146
|
+
config = PcaprLocal::Config.config user_config
|
147
|
+
pid_file = config["pidfile"]
|
148
|
+
if pid_file and File.exist? pid_file
|
149
|
+
pid = Integer(File.read(pid_file))
|
150
|
+
Process.kill -15, -pid
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
if __FILE__ == $0
|
157
|
+
PcaprLocal.start
|
158
|
+
end
|
159
|
+
|