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
@@ -0,0 +1,250 @@
|
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
require 'find'
|
6
|
+
require 'set'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'tempfile'
|
9
|
+
require 'digest/md5'
|
10
|
+
|
11
|
+
module PcaprLocal
|
12
|
+
class Scanner
|
13
|
+
|
14
|
+
# Creates scanner instance and starts it.
|
15
|
+
def self.start config
|
16
|
+
scanner = Scanner.new config
|
17
|
+
scanner.start
|
18
|
+
scanner
|
19
|
+
end
|
20
|
+
|
21
|
+
# Runs scanner loop in separate thread.
|
22
|
+
def start
|
23
|
+
Logger.info "Starting scanner thread"
|
24
|
+
Thread.new do
|
25
|
+
loop do
|
26
|
+
begin
|
27
|
+
scan
|
28
|
+
@db.compact!
|
29
|
+
rescue Exception => e
|
30
|
+
Logger.error "Exception during scanning: #{e.message}\n" + e.backtrace.join("\n")
|
31
|
+
end
|
32
|
+
sleep @scan_interval
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize config
|
38
|
+
@db = config.fetch('db')
|
39
|
+
@xtractr = config.fetch('xtractr')
|
40
|
+
@pcap_dir = File.expand_path(config.fetch('pcap_dir'))
|
41
|
+
@index_dir = File.expand_path(config.fetch('index_dir'))
|
42
|
+
@queue_delay = config.fetch('queue_delay')
|
43
|
+
@scan_interval = config.fetch('interval')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Removes doc from database and corresponding index file.
|
47
|
+
# Does _not_ remove original pcap.
|
48
|
+
def remove_doc doc
|
49
|
+
@db.delete_doc(doc)
|
50
|
+
if filename = doc['filename']
|
51
|
+
FileUtils.rm_f pcap_path(filename)
|
52
|
+
remove_index_for(filename)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def scan
|
57
|
+
# Get list of all pcaps
|
58
|
+
pcaps = self.find_pcaps
|
59
|
+
# Cleanup db and queue new pcaps
|
60
|
+
reconcile_with_db pcaps
|
61
|
+
# Index queued pcaps.
|
62
|
+
self.index
|
63
|
+
end
|
64
|
+
|
65
|
+
RE_PCAP = /\.p?cap\Z/
|
66
|
+
|
67
|
+
# Returns a set of pcap files (relative paths)
|
68
|
+
def find_pcaps
|
69
|
+
if not File.directory?(@pcap_dir) or not File.readable?(@pcap_dir)
|
70
|
+
return Set.new
|
71
|
+
end
|
72
|
+
|
73
|
+
pcaps = Set.new
|
74
|
+
pcap_prefix_size = @pcap_dir.size + 1 # /
|
75
|
+
Find.find @pcap_dir do |path|
|
76
|
+
# Don't recurse into ".pcapr_local" or other "." dirs
|
77
|
+
if File.basename(path) =~ /^\./
|
78
|
+
Find.prune
|
79
|
+
end
|
80
|
+
|
81
|
+
# Should be a file ending in .cap or .pcap
|
82
|
+
next unless path =~ RE_PCAP and File.file?(path)
|
83
|
+
|
84
|
+
rel_path = path[pcap_prefix_size..-1]
|
85
|
+
pcaps << rel_path
|
86
|
+
end
|
87
|
+
pcaps
|
88
|
+
end
|
89
|
+
|
90
|
+
def requeue_pcap rel_path
|
91
|
+
res = @db.view("pcaps/by_filename", :key => rel_path)
|
92
|
+
return nil if res['rows'].empty?
|
93
|
+
|
94
|
+
id = res['rows'][0]["id"]
|
95
|
+
@db.update_doc id do |doc|
|
96
|
+
doc['status'] = 'queued'
|
97
|
+
doc.delete 'index'
|
98
|
+
doc
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Adds pcap to db with status set to "queued". Returns nil w/out
|
103
|
+
# updating db if the pcap was modified within the last queue_delay
|
104
|
+
# seconds (because pcap may not be completely copied to pcap_dir).
|
105
|
+
def add_pcap relative_path
|
106
|
+
now = Time.new
|
107
|
+
stat = File.stat(File.join(@pcap_dir, relative_path))
|
108
|
+
if now - stat.mtime < @queue_delay
|
109
|
+
return
|
110
|
+
end
|
111
|
+
|
112
|
+
# Pick determistic doc id based on path and pcap size.
|
113
|
+
# (for testing convenience).
|
114
|
+
id = Digest::MD5.new
|
115
|
+
id << "#{relative_path}:#{stat.size}"
|
116
|
+
|
117
|
+
doc = CouchRest::Document.new({
|
118
|
+
:_id => id.to_s,
|
119
|
+
:type => 'pcap',
|
120
|
+
:filename => relative_path,
|
121
|
+
:status => 'queued',
|
122
|
+
:stat => {
|
123
|
+
:inode => stat.ino,
|
124
|
+
:size => stat.size,
|
125
|
+
:ctime => stat.ctime,
|
126
|
+
},
|
127
|
+
:created_at => now,
|
128
|
+
:updated_at => now,
|
129
|
+
})
|
130
|
+
@db.save_doc doc
|
131
|
+
|
132
|
+
doc
|
133
|
+
end
|
134
|
+
|
135
|
+
# Indexes all documents in queue. Returns count of documents indexed.
|
136
|
+
def index
|
137
|
+
count = 0
|
138
|
+
@db.each_in_view("pcaps/queued", :include_docs => true) do |row|
|
139
|
+
index_pcap row['doc']
|
140
|
+
count += 1
|
141
|
+
end
|
142
|
+
count
|
143
|
+
end
|
144
|
+
|
145
|
+
# Creates xtractr index for pcap. Updates status from "queued" to "indexing" to "indexed".
|
146
|
+
# Any exception will result in a status of "failed" with the exception's message copied
|
147
|
+
# to the document's message attribute.
|
148
|
+
def index_pcap pcap
|
149
|
+
relative_path = pcap["filename"]
|
150
|
+
pcap_path = File.join(File.expand_path(@pcap_dir), relative_path)
|
151
|
+
index_dir = File.join(File.expand_path(@index_dir), relative_path)
|
152
|
+
|
153
|
+
# Index
|
154
|
+
Logger.info "Indexing #{relative_path}"
|
155
|
+
begin
|
156
|
+
@db.update_doc pcap["_id"] do |doc|
|
157
|
+
doc["status"] = "indexing"
|
158
|
+
doc
|
159
|
+
end
|
160
|
+
|
161
|
+
index_data = @xtractr.index pcap_path, index_dir
|
162
|
+
|
163
|
+
@db.update_doc pcap["_id"] do |doc|
|
164
|
+
doc['index'] = index_data
|
165
|
+
doc['status'] = 'indexed'
|
166
|
+
doc
|
167
|
+
end
|
168
|
+
rescue
|
169
|
+
Logger.warn "Indexing failure: #{$!.message}"
|
170
|
+
@db.update_doc pcap["_id"] do |doc|
|
171
|
+
doc['status'] = "failed"
|
172
|
+
doc['message'] = $!.message
|
173
|
+
doc
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
return
|
178
|
+
end
|
179
|
+
|
180
|
+
def pcap_path rel_path
|
181
|
+
if rel_path.is_a? Hash
|
182
|
+
rel_path = rel_path[:filename] or raise "path not found in #{rel_path.inspect}"
|
183
|
+
end
|
184
|
+
File.expand_path File.join(@pcap_dir, rel_path)
|
185
|
+
end
|
186
|
+
|
187
|
+
def index_path rel_path
|
188
|
+
if rel_path.is_a? Hash
|
189
|
+
rel_path = rel_path.fetch :filename
|
190
|
+
end
|
191
|
+
File.expand_path File.join(@index_dir, rel_path)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Because FileUtils.rm_rf is too dangerous.
|
195
|
+
def remove_index_for rel_path
|
196
|
+
target = index_path rel_path
|
197
|
+
if File.directory? target
|
198
|
+
FileUtils.rm_rf Dir.glob("#{target}/*.db")
|
199
|
+
FileUtils.rmdir target rescue nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Checks each pcap in the database, purging or requeueing documents as necessary.
|
204
|
+
# Any pcaps in fs_pcaps that are not in the database are added.
|
205
|
+
def reconcile_with_db fs_pcaps
|
206
|
+
fs_pcaps = fs_pcaps.dup
|
207
|
+
|
208
|
+
indexed = Set.new
|
209
|
+
@db.each_in_view("pcaps/indexed") do |row|
|
210
|
+
indexed << row['key']
|
211
|
+
end
|
212
|
+
|
213
|
+
@db.each_in_view("pcaps/by_filename") do |row|
|
214
|
+
path = row['key']
|
215
|
+
|
216
|
+
# Delete record if from database if pcap is not present on the
|
217
|
+
# file system.
|
218
|
+
if not fs_pcaps.include? path
|
219
|
+
Logger.warn "Indexer: removing database entry for missing pcap #{path}"
|
220
|
+
@db.delete_doc @db.get(row['id'])
|
221
|
+
remove_index_for(path)
|
222
|
+
|
223
|
+
next
|
224
|
+
end
|
225
|
+
|
226
|
+
# Requeue pcap if xtractr index is missing or is older than the pcap.
|
227
|
+
if indexed.include? path
|
228
|
+
pcap_index_dir = File.join(@index_dir, path)
|
229
|
+
if not Xtractr.index_dir?(pcap_index_dir)
|
230
|
+
Logger.warn "Index is missing, requeueing #{path}"
|
231
|
+
requeue_pcap path
|
232
|
+
elsif Xtractr.index_time(pcap_index_dir) < File.mtime(pcap_path(path)).to_f
|
233
|
+
Logger.info "Pcap is newer than index, requeueing #{path}"
|
234
|
+
requeue_pcap path
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
fs_pcaps.delete path
|
239
|
+
end
|
240
|
+
|
241
|
+
# Remaining pcaps are unknown, add them to database
|
242
|
+
fs_pcaps.each do |path|
|
243
|
+
Logger.debug "New pcap: #{path}"
|
244
|
+
add_pcap path
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# http://www.mudynamics.com
|
2
|
+
# http://labs.mudynamics.com
|
3
|
+
# http://www.pcapr.net
|
4
|
+
|
5
|
+
require 'rack'
|
6
|
+
require 'rack/contrib/jsonp'
|
7
|
+
require 'sinatra'
|
8
|
+
require 'pcapr_local/db'
|
9
|
+
require 'pcapr_local/scanner'
|
10
|
+
require 'pcapr_local/xtractr'
|
11
|
+
require 'mu/scenario/pcap'
|
12
|
+
|
13
|
+
module PcaprLocal
|
14
|
+
class Server < Sinatra::Base
|
15
|
+
set :app_file, __FILE__
|
16
|
+
root = File.expand_path(File.dirname(__FILE__))
|
17
|
+
set :public, File.join(root, 'www')
|
18
|
+
|
19
|
+
use Rack::JSONP
|
20
|
+
mime_type :template, 'application/octet-stream'
|
21
|
+
mime_type :par, 'application/octet-stream'
|
22
|
+
|
23
|
+
helpers do
|
24
|
+
# View as object by joining keys and values.
|
25
|
+
# e.g. {"key" => "foo", "value" => "bar"}
|
26
|
+
# becomes
|
27
|
+
# {"foo" => "bar"}
|
28
|
+
def as_object result
|
29
|
+
obj = {}
|
30
|
+
result['rows'].each do |row|
|
31
|
+
key = row['key']
|
32
|
+
if key.is_a? Array
|
33
|
+
# behave like javascript Array.toString
|
34
|
+
key = key.join ","
|
35
|
+
end
|
36
|
+
|
37
|
+
val = row['value']
|
38
|
+
if val.is_a? Array
|
39
|
+
# behave like javascript Array.toString
|
40
|
+
key = key.join ","
|
41
|
+
end
|
42
|
+
|
43
|
+
obj[key] = val
|
44
|
+
end
|
45
|
+
obj
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Main page.
|
50
|
+
get '/' do
|
51
|
+
redirect '/home/index.html'
|
52
|
+
end
|
53
|
+
|
54
|
+
# Count of pcaps by status.
|
55
|
+
get '/pcaps/1/status' do
|
56
|
+
db = settings.db
|
57
|
+
content_type :json
|
58
|
+
result = db.view 'pcaps/by_status', :group => true
|
59
|
+
return as_object(result).to_json
|
60
|
+
end
|
61
|
+
|
62
|
+
# High level statistics.
|
63
|
+
get '/pcaps/1/statistics' do
|
64
|
+
db = settings.db
|
65
|
+
content_type :json
|
66
|
+
|
67
|
+
result = db.view 'pcaps/statistics', :group => true
|
68
|
+
|
69
|
+
as_object(result).to_json
|
70
|
+
end
|
71
|
+
|
72
|
+
VIEWS = {
|
73
|
+
'date' => 'pcaps/by_created_at',
|
74
|
+
'path' => 'pcaps/by_path',
|
75
|
+
'status' => 'pcaps/by_status',
|
76
|
+
'service' => 'pcaps/by_service',
|
77
|
+
'keyword' => 'pcaps/by_keyword',
|
78
|
+
'filename' => 'pcaps/by_filename',
|
79
|
+
'directory' => 'pcaps/by_directory',
|
80
|
+
}
|
81
|
+
|
82
|
+
# List/query pcaps by CouchDB view.
|
83
|
+
get '/pcaps/1/list' do
|
84
|
+
content_type :json
|
85
|
+
db = settings.db
|
86
|
+
query = params.dup
|
87
|
+
by = query.delete('by')
|
88
|
+
by ||= 'date'
|
89
|
+
view = VIEWS[by]
|
90
|
+
query.delete 'callback'
|
91
|
+
['startkey', 'endkey', 'key'].each do |key|
|
92
|
+
if val = query[key]
|
93
|
+
# JSON parser doesn't work unless value is an array or
|
94
|
+
# hash. (I.e. this fails: JSON.parse(1.to_json)
|
95
|
+
# So enclosing the value in a top level array.
|
96
|
+
parsed = JSON.parse("[#{val}]").pop
|
97
|
+
query[key] = parsed
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
db.view(view, query).to_json
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns doc for pcap.
|
105
|
+
get '/pcaps/1/about/:id' do
|
106
|
+
content_type :json
|
107
|
+
settings.db.get(params[:id]).to_json
|
108
|
+
end
|
109
|
+
|
110
|
+
# Deletes document if it has a failed status.
|
111
|
+
get '/pcaps/1/remove/:id' do
|
112
|
+
content_type :json
|
113
|
+
id = params[:id]
|
114
|
+
doc = settings.db.get(params[:id])
|
115
|
+
if doc and doc['status'] == 'failed'
|
116
|
+
settings.scanner.remove_doc doc
|
117
|
+
{'ok' => true}.to_json
|
118
|
+
else
|
119
|
+
{'error' => true, 'reason' => 'status is not failed'}.to_json
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Explore pcap on pcapr.
|
124
|
+
get '/pcaps/1/pcap/:id' do
|
125
|
+
id = params[:id]
|
126
|
+
doc = settings.db.get(params[:id])
|
127
|
+
if doc and doc['index']
|
128
|
+
version = doc['index']['about']['version']
|
129
|
+
location = "http://www.pcapr.net/xtractr/explore?version=#{version}&" \
|
130
|
+
"url=http://#{request.host_with_port}#{request.path}"
|
131
|
+
redirect location
|
132
|
+
else
|
133
|
+
return {"error" => true , "message" => "not found"}.to_json
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Forward requests (GET) to xtractr browser instance.
|
138
|
+
get '/pcaps/1/pcap/:id/*' do
|
139
|
+
id = params[:id]
|
140
|
+
path = params[:splat][0]
|
141
|
+
url = "#{path}?#{request.query_string}"
|
142
|
+
doc = settings.db.get(params[:id])
|
143
|
+
status, headers, body = settings.xtractr.get(doc['filename'], url)
|
144
|
+
headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
145
|
+
return status, headers, body
|
146
|
+
end
|
147
|
+
|
148
|
+
# Forward requests (POST) to xtractr browser instance.
|
149
|
+
post '/pcaps/1/pcap/:id/*' do
|
150
|
+
id = params[:id]
|
151
|
+
path = params[:splat][0]
|
152
|
+
url = "#{path}?#{request.query_string}"
|
153
|
+
doc = settings.db.get(params[:id])
|
154
|
+
status, headers, body = settings.xtractr.post(doc['filename'], url, request.body.read)
|
155
|
+
headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
156
|
+
return status, headers, body
|
157
|
+
end
|
158
|
+
|
159
|
+
# Download archive that includes dissected and normalized pcap.
|
160
|
+
get '/pcaps/1/export_to_par/:id' do
|
161
|
+
content_type :par
|
162
|
+
id = params[:id]
|
163
|
+
doc = settings.db.get(params[:id])
|
164
|
+
raise "not found" unless doc
|
165
|
+
path = settings.scanner.pcap_path(doc)
|
166
|
+
io = Mu::Scenario::Pcap.export_to_par path
|
167
|
+
filename = File.basename(doc['filename']).gsub(/[.][^.]*$/, '.par')
|
168
|
+
return 200, {'Content-Disposition' => "filename=#{filename}" }, io
|
169
|
+
end
|
170
|
+
|
171
|
+
# Record any exceptions in main logfile.
|
172
|
+
after '/pcaps/*' do
|
173
|
+
if err=request.env['sinatra.error']
|
174
|
+
PcaprLocal::Logger.error "UI got an error: #{err.message}\n#{err.backtrace.join("\n")}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,138 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
5
|
+
<link rel="shortcut icon" href="/static/image/favicon.ico"/>
|
6
|
+
<title>pcapr.Local - A place to organize your packet captures</title>
|
7
|
+
|
8
|
+
<link rel="stylesheet" type="text/css" href="/static/style/page.css" media="all"/>
|
9
|
+
<link rel="stylesheet" type="text/css" href="/static/style/c3p0.css" media="all"/>
|
10
|
+
<link rel="stylesheet" type="text/css" href="/static/style/jquery.suggest.css" media="all"/>
|
11
|
+
<link rel="stylesheet" type="text/css" href="/static/style/tipsy.css" media="all"/>
|
12
|
+
|
13
|
+
<script type="text/javascript" src="/static/script/json2.js">
|
14
|
+
</script>
|
15
|
+
|
16
|
+
<script type="text/javascript" src="/static/script/jquery/jquery-1.4.2.min.js">
|
17
|
+
</script>
|
18
|
+
<script type="text/javascript" src="/static/script/jquery/jquery-ui.js">
|
19
|
+
</script>
|
20
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.ui.core.js">
|
21
|
+
</script>
|
22
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.ui.widget.js">
|
23
|
+
</script>
|
24
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.ui.sortable.js">
|
25
|
+
</script>
|
26
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.ui.slider.js">
|
27
|
+
</script>
|
28
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.form.js">
|
29
|
+
</script>
|
30
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.menu.js">
|
31
|
+
</script>
|
32
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.suggest.js">
|
33
|
+
</script>
|
34
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.flot.js">
|
35
|
+
</script>
|
36
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.flot.stack.js">
|
37
|
+
</script>
|
38
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.flot.selection.js">
|
39
|
+
</script>
|
40
|
+
<script type="text/javascript" src="/static/script/jquery/jquery.jsonp.min.js">
|
41
|
+
</script>
|
42
|
+
<script type="text/javascript" src="/static/script/tipsy/jquery.tipsy.js">
|
43
|
+
</script>
|
44
|
+
|
45
|
+
<script type="text/javascript" src="/static/script/sammy/sammy.js">
|
46
|
+
</script>
|
47
|
+
<script type="text/javascript" src="/static/script/sammy/plugins/sammy.template.js">
|
48
|
+
</script>
|
49
|
+
<script type="text/javascript" src="/static/script/sammy/plugins/sammy.cache.js">
|
50
|
+
</script>
|
51
|
+
|
52
|
+
<script type="text/javascript" src="/static/script/closet/closet.api.js">
|
53
|
+
</script>
|
54
|
+
<script type="text/javascript" src="/static/script/closet/closet.js">
|
55
|
+
</script>
|
56
|
+
<script type="text/javascript" src="/static/script/closet/closet.util.js">
|
57
|
+
</script>
|
58
|
+
<script type="text/javascript" src="/static/script/closet/closet.quantity.js">
|
59
|
+
</script>
|
60
|
+
<script type="text/javascript" src="/static/script/closet/closet.render.js">
|
61
|
+
</script>
|
62
|
+
<script type="text/javascript" src="/static/script/closet/closet.folders.js">
|
63
|
+
</script>
|
64
|
+
<script type="text/javascript" src="/static/script/closet/closet.mr.js">
|
65
|
+
</script>
|
66
|
+
<script type="text/javascript" src="/static/script/closet/closet.options.js">
|
67
|
+
</script>
|
68
|
+
<script type="text/javascript" src="/static/script/closet/closet.report.js">
|
69
|
+
</script>
|
70
|
+
<script type="text/javascript" src="/static/script/closet/closet.reports.visualize.js">
|
71
|
+
</script>
|
72
|
+
<script type="text/javascript" src="/static/script/closet/closet.reports.overview.js">
|
73
|
+
</script>
|
74
|
+
<script type="text/javascript" src="/static/script/closet/closet.reports.tcp.js">
|
75
|
+
</script>
|
76
|
+
<script type="text/javascript" src="/static/script/closet/closet.reports.http.js">
|
77
|
+
</script>
|
78
|
+
<script type="text/javascript" src="/static/script/closet/closet.reports.sip.js">
|
79
|
+
</script>
|
80
|
+
|
81
|
+
|
82
|
+
<script type="text/javascript" src="/static/script/closet/async.js">
|
83
|
+
</script>
|
84
|
+
|
85
|
+
|
86
|
+
<script type="text/javascript">
|
87
|
+
(function($) {
|
88
|
+
$(function() { closet.load(); });
|
89
|
+
}(jQuery));
|
90
|
+
</script>
|
91
|
+
</head>
|
92
|
+
<body id="page_bg" class="f-default">
|
93
|
+
<div id="container">
|
94
|
+
<div id="mainbody">
|
95
|
+
<div class="wrapper">
|
96
|
+
<div class="content beta">
|
97
|
+
<div style="float:left;font-size:large;margin-top:6px;margin-left:5px;padding-right:40px">
|
98
|
+
<a style="text-decoration:none;outline:none" href="http://www.mudynamics.com">
|
99
|
+
<img class="icon" src="/static/image/favicon.png"/>
|
100
|
+
</a>
|
101
|
+
<span style="font-weight:bold;letter-spacing:0.1em">pcapr.Local</span>
|
102
|
+
</div>
|
103
|
+
|
104
|
+
<div class="browse">
|
105
|
+
<div class="floater" style="float:right">
|
106
|
+
<table style="display:inline">
|
107
|
+
<tr>
|
108
|
+
<td><img class="icon" src="/static/image/16x16/Search.png"/></td>
|
109
|
+
<td><input id="search" type="text"/> </td>
|
110
|
+
<td>
|
111
|
+
<div class="statistics" style="float:right"></div>
|
112
|
+
</td>
|
113
|
+
</tr>
|
114
|
+
</table>
|
115
|
+
</div>
|
116
|
+
<ul class="tab">
|
117
|
+
<li class="active">
|
118
|
+
<a id="browse" href="#/browse"><span>Browse</span></a>
|
119
|
+
</li>
|
120
|
+
<li>
|
121
|
+
<a id="folders" href="#/folders"><span>Folders</span></a>
|
122
|
+
</li>
|
123
|
+
<li>
|
124
|
+
<a id="services" href="#/services"><span>Services</span></a>
|
125
|
+
</li>
|
126
|
+
</ul>
|
127
|
+
<div class="browse-tab">
|
128
|
+
</div>
|
129
|
+
</div>
|
130
|
+
|
131
|
+
<div class="footer">
|
132
|
+
</div>
|
133
|
+
</div>
|
134
|
+
</div>
|
135
|
+
</div>
|
136
|
+
</div>
|
137
|
+
</body>
|
138
|
+
</html>
|