pcapr-local 0.1.10
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.
- 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,7 @@
|
|
|
1
|
+
.tipsy { padding: 5px; font-size: 10px; opacity: 0.8; filter: alpha(opacity=80); background-repeat: no-repeat; background-image: url(tipsy.gif); }
|
|
2
|
+
.tipsy-inner { padding: 5px 8px 4px 8px; background-color: black; color: white; max-width: 200px; text-align: center; }
|
|
3
|
+
.tipsy-inner { -moz-border-radius:3px; -webkit-border-radius:3px; }
|
|
4
|
+
.tipsy-north { background-position: top center; }
|
|
5
|
+
.tipsy-south { background-position: bottom center; }
|
|
6
|
+
.tipsy-east { background-position: right center; }
|
|
7
|
+
.tipsy-west { background-position: left center; }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<div class="cloud" style="margin-top:10px;margin-bottom:10px">
|
|
2
|
+
<% closet.render.cloud(kvs, function(kv, size) { %>
|
|
3
|
+
<span style="font-size:<%= size %>px">
|
|
4
|
+
<a class="term" title="<%= kv.key %>" href="#/browse/service/<%= escape(kv.key) %>">
|
|
5
|
+
<%= closet.util.escapeHTML(kv.key) %>
|
|
6
|
+
</a>
|
|
7
|
+
<sup style="font-size:10px"><%= kv.value %></sup>
|
|
8
|
+
</span>
|
|
9
|
+
<% }); %>
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<% if (pcaps.filter) {%>
|
|
2
|
+
<span style="float:right">
|
|
3
|
+
<img class="icon" src="/static/image/16x16/Cancel.png"/>
|
|
4
|
+
<a href="#/browse">remove filter</a>
|
|
5
|
+
</span>
|
|
6
|
+
<% } %>
|
|
7
|
+
|
|
8
|
+
<ul id="p-main" class="s-result pcaps" style="margin-left: -40px">
|
|
9
|
+
<% $.each(pcaps.rows.slice(0, Math.min(closet.api.PAGE_SIZE, pcaps.rows.length)), function(i, row) { %>
|
|
10
|
+
<li class="l0">
|
|
11
|
+
<div class="p-body">
|
|
12
|
+
<% if (row.doc.index) { %>
|
|
13
|
+
<% var paths = row.doc.filename.split('/'); %>
|
|
14
|
+
<a class="term" href="#/browse/dir/">/</a>
|
|
15
|
+
<% $.each(paths.slice(0, paths.length-1), function(k, path) {%>
|
|
16
|
+
<a class="term" href="#/browse/dir/<%= escape(paths.slice(0,k+1).join('/')) %>">
|
|
17
|
+
<%= closet.util.escapeHTML(path) %>
|
|
18
|
+
</a> /
|
|
19
|
+
<% }); %>
|
|
20
|
+
<a href="#/browse/pcap/<%= escape(row.id) %>"><%= closet.util.escapeHTML(paths[paths.length-1]) %></a>
|
|
21
|
+
<% } else if (row.doc.status === 'indexing') { %>
|
|
22
|
+
<span class="throbber"/>
|
|
23
|
+
<span style="font-size:125%;font-weight:bold;color:#e66c25">
|
|
24
|
+
<%= closet.util.escapeHTML(row.doc.filename) %>
|
|
25
|
+
</span>
|
|
26
|
+
<% } else if (row.doc.status === 'failed' || row.doc.status === 'aborted') { %>
|
|
27
|
+
<a id="<%= row.doc._id %>" class="remove" href="javascript:void(0)">
|
|
28
|
+
<img class="icon" src="/static/image/16x16/Cancel.png"/>
|
|
29
|
+
</a>
|
|
30
|
+
<span style="font-size:125%;font-weight:bold;color:#dd1122">
|
|
31
|
+
<%= closet.util.escapeHTML(row.doc.filename) %>
|
|
32
|
+
</span>
|
|
33
|
+
<% } else {%>
|
|
34
|
+
<span style="font-size:125%;font-weight:bold;color:grey">
|
|
35
|
+
<%= closet.util.escapeHTML(row.doc.filename) %>
|
|
36
|
+
</span>
|
|
37
|
+
<% } %>
|
|
38
|
+
</div>
|
|
39
|
+
<% if (row.doc.index) { %>
|
|
40
|
+
<div class="p-protos">
|
|
41
|
+
<span>services: </span>
|
|
42
|
+
<% var services = row.doc.index.services.sort(); %>
|
|
43
|
+
<% $.each(services.slice(0,10), function(j, service) { %>
|
|
44
|
+
<a class="meta-proto" href="#/browse/service/<%= escape(service) %>">
|
|
45
|
+
<%= closet.util.escapeHTML(service) %>
|
|
46
|
+
</a>
|
|
47
|
+
<% }); %>
|
|
48
|
+
<% if (services.length > 10) { %>
|
|
49
|
+
<span>(<%= services.length-10 %> more)</span>
|
|
50
|
+
<% } %>
|
|
51
|
+
</div>
|
|
52
|
+
<% } %>
|
|
53
|
+
<div style="font-size: smaller">
|
|
54
|
+
<a href="#/browse/status/<%= escape(row.doc.status) %>">
|
|
55
|
+
<%= closet.util.escapeHTML(row.doc.status) %>
|
|
56
|
+
</a>,
|
|
57
|
+
<%= closet.quantity.timespan(new Date(row.doc.created_at)) %>,
|
|
58
|
+
<%= closet.quantity.bytes(row.doc.stat.size) %>
|
|
59
|
+
<% if (row.doc.index) { %>
|
|
60
|
+
<span>,
|
|
61
|
+
<%= closet.quantity.count(row.doc.index.about.flows,'flow') %>,
|
|
62
|
+
<%= closet.quantity.count(row.doc.index.about.packets,'packet') %>,
|
|
63
|
+
<%= row.doc.index.about.duration %> seconds
|
|
64
|
+
</span>
|
|
65
|
+
<% } %>
|
|
66
|
+
</div>
|
|
67
|
+
</li>
|
|
68
|
+
<% }); %>
|
|
69
|
+
</ul>
|
|
70
|
+
|
|
71
|
+
<% if (pcaps.rows.length === closet.api.PAGE_SIZE+1) { %>
|
|
72
|
+
<div style="margin-left: 20px">
|
|
73
|
+
<span>» </span>
|
|
74
|
+
<% var last = pcaps.rows[pcaps.rows.length-1]; %>
|
|
75
|
+
<a href="<%= path %>?nextkey=<%= escape(JSON.stringify(last.key)) %>&nextid=<%= escape(last.id) %>">more</a>
|
|
76
|
+
</div>
|
|
77
|
+
<% } %>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<div>
|
|
2
|
+
<% if (flows.length > 0) { %>
|
|
3
|
+
<span class="info">
|
|
4
|
+
We found <%= closet.quantity.count(flows.length, 'candidate flow') %>.
|
|
5
|
+
Select one to create a scenario!
|
|
6
|
+
</span>
|
|
7
|
+
<table id="flows-table">
|
|
8
|
+
<colgroup span="8"/>
|
|
9
|
+
<colgroup span="1" style="width:100%"/>
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<td>id</td>
|
|
13
|
+
<td><span style="visibility:hidden">▼</span><span class="time">time</span></td>
|
|
14
|
+
<td>packets</td>
|
|
15
|
+
<td><span style="visibility:hidden">▼</span><span class="src">src</span></td>
|
|
16
|
+
<td>sport</td>
|
|
17
|
+
<td><span style="visibility:hidden">▼</span><span class="dst">dst</span></td>
|
|
18
|
+
<td><span class="dport">dport</span></td>
|
|
19
|
+
<td><span style="visibility:hidden">▼</span><span class="service">service</span></td>
|
|
20
|
+
<td>title</td>
|
|
21
|
+
</tr>
|
|
22
|
+
</thead>
|
|
23
|
+
<% $.each(flows, function(_, flow) { %>
|
|
24
|
+
<tr class="flow">
|
|
25
|
+
<td><%= flow.id %>.</td>
|
|
26
|
+
<td><span class="field" id="flow.time"><%= flow.time.toFixed(4) %></span></td>
|
|
27
|
+
<td><span class="term" href="#/packets?pkt.flow:<%= escape(flow.id) %>"><%= escape(flow.packets) %></span></td>
|
|
28
|
+
<td><span class="term field" id="flow.src"><%= closet.util.escapeHTML(flow.src) %></span></td>
|
|
29
|
+
<td><span class="term"><%= flow.sport %></span></td>
|
|
30
|
+
<td><span class="term field" id="flow.dst"><%= closet.util.escapeHTML(flow.dst) %></span></td>
|
|
31
|
+
<td><span class="term" id="flow.dport"><%= flow.dport %></span></td>
|
|
32
|
+
<td><span class="term field" id="flow.service"><%= closet.util.escapeHTML(flow.service) %></span></td>
|
|
33
|
+
<td><a href="#/browse/pcap/<%= escape(pcap._id) %>/flow/<%= escape(flow.id) %>"><%= closet.util.escapeHTML(flow.title) %></a></td>
|
|
34
|
+
</tr>
|
|
35
|
+
<% }); %>
|
|
36
|
+
</table>
|
|
37
|
+
<% } %>
|
|
38
|
+
</div>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<div style="margin-top:20px;margin-bottom:20px">
|
|
2
|
+
<div class="p-body">
|
|
3
|
+
<% if (pcap.index) { %>
|
|
4
|
+
<% var paths = pcap.filename.split('/'); %>
|
|
5
|
+
<a class="term" href="#/browse/dir/">/</a>
|
|
6
|
+
<% $.each(paths.slice(0, paths.length-1), function(k, path) {%>
|
|
7
|
+
<a class="term" href="#/browse/dir/<%= escape(paths.slice(0,k+1).join('/')) %>">
|
|
8
|
+
<%= closet.util.escapeHTML(path) %>
|
|
9
|
+
</a> /
|
|
10
|
+
<% }); %>
|
|
11
|
+
<a class="extern" href="/pcaps/1/pcap/<%= escape(pcap._id) %>">
|
|
12
|
+
<%= closet.util.escapeHTML(paths[paths.length-1]) %>
|
|
13
|
+
</a>
|
|
14
|
+
<% } else if (pcap.status === 'indexing') { %>
|
|
15
|
+
<span class="throbber"/>
|
|
16
|
+
<span style="font-size:125%;font-weight:bold;color:#e66c25">
|
|
17
|
+
<%= closet.util.escapeHTML(pcap.filename) %>
|
|
18
|
+
</span>
|
|
19
|
+
<% } else if (pcap.status === 'failed') { %>
|
|
20
|
+
<span style="font-size:125%;font-weight:bold;color:#dd1122">
|
|
21
|
+
<%= closet.util.escapeHTML(row.doc.filename) %>
|
|
22
|
+
</span>
|
|
23
|
+
<% } else {%>
|
|
24
|
+
<span style="font-size:125%;font-weight:bold;color:grey">
|
|
25
|
+
<%= closet.util.escapeHTML(row.doc.filename) %>
|
|
26
|
+
</span>
|
|
27
|
+
<% } %>
|
|
28
|
+
</div>
|
|
29
|
+
<div style="font-size: smaller">
|
|
30
|
+
<a href="#/browse/status/<%= escape(pcap.status) %>"><%= pcap.status %></a>,
|
|
31
|
+
<%= closet.quantity.timespan(new Date(pcap.created_at)) %>,
|
|
32
|
+
<%= closet.quantity.bytes(pcap.stat.size) %>
|
|
33
|
+
<% if (pcap.index) { %>
|
|
34
|
+
<span>,
|
|
35
|
+
<%= closet.quantity.count(pcap.index.about.flows,'flow') %>,
|
|
36
|
+
<%= closet.quantity.count(pcap.index.about.packets,'packet') %>,
|
|
37
|
+
<%= pcap.index.about.duration %> seconds
|
|
38
|
+
</span>
|
|
39
|
+
<% } %>
|
|
40
|
+
</div>
|
|
41
|
+
<p/>
|
|
42
|
+
<div class="reports">
|
|
43
|
+
<span class="showonload" style="display:none">
|
|
44
|
+
Reports: <select class="reports" style="padding-right:20px"></select>
|
|
45
|
+
</span>
|
|
46
|
+
<h3 class="title"></h3>
|
|
47
|
+
<div class="settings" style="display:none; margin-top:-10px">
|
|
48
|
+
<span style="color:#105892">►</span>
|
|
49
|
+
<a class="toggler" href="javascript:void(0)">Settings</a>
|
|
50
|
+
<div class="options" style="display:none;margin-left:20px;border-left:2px dotted #d0d0d0;padding-left:10px">
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="report" style="margin-top:20px">
|
|
54
|
+
<p/><span class="throbber">analyzing</span>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div style="font-size: smaller">
|
|
59
|
+
Download a
|
|
60
|
+
<a target="_none" href="/pcaps/1/export_to_par/<%= escape(pcap._id) %>">PAR file </a>
|
|
61
|
+
.
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<div>
|
|
2
|
+
<% $.each(calls, function(_, call) { %>
|
|
3
|
+
<div>
|
|
4
|
+
Call <span style="font-weight:bold"><%= closet.util.escapeHTML(call.src) %></span>
|
|
5
|
+
»
|
|
6
|
+
<span style="font-weight:bold"><%= closet.util.escapeHTML(call.dst) %></span>
|
|
7
|
+
|
|
8
|
+
[<span style="font-size:smaller"><%= closet.util.escapeHTML(call.id) %></span>]
|
|
9
|
+
</div>
|
|
10
|
+
<div style="margin-left: 20px">
|
|
11
|
+
This call was made from <span style="font-weight:bold"><%= closet.util.escapeHTML(call.from) %></span> to
|
|
12
|
+
<span style="font-weight:bold"><%= closet.util.escapeHTML(call.to) %></span> and is made up
|
|
13
|
+
of <%= closet.quantity.count(call.flows.length,'flow') %> and
|
|
14
|
+
<%= closet.quantity.count(call.packets,'packet') %>.
|
|
15
|
+
|
|
16
|
+
<% if (call.rtp.length > 0) { %>
|
|
17
|
+
We also found <%= closet.quantity.count(call.rtp.length,'RTP stream') %> in this call
|
|
18
|
+
with the following media types:
|
|
19
|
+
<ul>
|
|
20
|
+
<% $.each(call.media, function(_, media) {%>
|
|
21
|
+
<li style="list-style: disc outside !important">
|
|
22
|
+
<%= closet.util.escapeHTML(media) %>
|
|
23
|
+
</li>
|
|
24
|
+
<% }); %>
|
|
25
|
+
</ul>
|
|
26
|
+
<% } %>
|
|
27
|
+
|
|
28
|
+
<div>
|
|
29
|
+
<img class="icon" src="/static/image/16x16/Download.png"/>
|
|
30
|
+
Download <a href="/pcaps/1/pcap/<%= escape(pcap._id) %>/api/packets/slice?q=<%=escape(call.q)%>">pcap</a> for this call.
|
|
31
|
+
</div>
|
|
32
|
+
<p/>
|
|
33
|
+
</div>
|
|
34
|
+
<% }); %>
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# http://www.mudynamics.com
|
|
2
|
+
# http://labs.mudynamics.com
|
|
3
|
+
# http://www.pcapr.net
|
|
4
|
+
|
|
5
|
+
require 'thread'
|
|
6
|
+
require 'net/http'
|
|
7
|
+
require 'uri'
|
|
8
|
+
|
|
9
|
+
module PcaprLocal
|
|
10
|
+
class Xtractr
|
|
11
|
+
class XtractrError < StandardError; end
|
|
12
|
+
|
|
13
|
+
EXE_PATH = File.join(ROOT, 'lib/exe/xtractr')
|
|
14
|
+
|
|
15
|
+
def initialize config
|
|
16
|
+
@xtractr_path = EXE_PATH
|
|
17
|
+
@idle_timeout = config.fetch("idle_timeout")
|
|
18
|
+
@index_dir = config.fetch("index_dir")
|
|
19
|
+
@reaper_interval = config.fetch("reaper_interval", REAPER_INTERVAL)
|
|
20
|
+
# Hash of index dir to xtractr instance.
|
|
21
|
+
@xcache = {}
|
|
22
|
+
# Lock to synchronize creation/destruction of xtractr instances.
|
|
23
|
+
@xcache_lock = Mutex.new
|
|
24
|
+
|
|
25
|
+
start_reaper
|
|
26
|
+
at_exit do
|
|
27
|
+
shutdown
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Idle xtractr process reaper runs every REAPER_INTERVAL seconds.
|
|
32
|
+
REAPER_INTERVAL = 10
|
|
33
|
+
|
|
34
|
+
# Start reaper thread.
|
|
35
|
+
def start_reaper
|
|
36
|
+
Thread.new do
|
|
37
|
+
loop do
|
|
38
|
+
begin
|
|
39
|
+
reap
|
|
40
|
+
rescue Exception
|
|
41
|
+
Logger.error "Exception while cleaning up idle processes: #{e.message}\n" + e.backtrace.join("\n")
|
|
42
|
+
end
|
|
43
|
+
sleep @reaper_interval
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Kill xtractr instances at exit (idle or not).
|
|
49
|
+
def shutdown
|
|
50
|
+
@xcache.each_value do |xtractr|
|
|
51
|
+
$stderr.puts "stopping xtractr process"
|
|
52
|
+
xtractr.stop rescue nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Kills idle xtractr processes
|
|
57
|
+
def reap
|
|
58
|
+
@xcache_lock.synchronize do
|
|
59
|
+
@xcache.to_a.each do |dir, xtractr|
|
|
60
|
+
if xtractr.lock.try_lock
|
|
61
|
+
# xtractr is not in use right now
|
|
62
|
+
begin
|
|
63
|
+
if xtractr.last_use + @idle_timeout < Time.new.to_f
|
|
64
|
+
xtractr.stop
|
|
65
|
+
@xcache.delete dir
|
|
66
|
+
end
|
|
67
|
+
ensure
|
|
68
|
+
xtractr.lock.unlock
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Does path look like an xtractr index directory?
|
|
76
|
+
def self.index_dir?(path)
|
|
77
|
+
File.exist? File.join(path, 'packets.db')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns timestamp (float) for xtractr index.
|
|
81
|
+
def self.index_time(path)
|
|
82
|
+
db_file = File.join(path, 'packets.db')
|
|
83
|
+
if File.exists? db_file
|
|
84
|
+
File.mtime(db_file).to_f
|
|
85
|
+
elsif File.exists? path
|
|
86
|
+
File.mtime(path).to_f
|
|
87
|
+
else
|
|
88
|
+
0.0
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class XtractrIndexingException < XtractrError; end
|
|
93
|
+
|
|
94
|
+
# Last line of normal xtractr indexing output.
|
|
95
|
+
RE_INDEXING_DONE = /optimizing terms\.db\.\.\.done/
|
|
96
|
+
|
|
97
|
+
# Indexes pcap it index_dir and returns hash containing xtractr summary data.
|
|
98
|
+
# Raises exception if indexing fails.
|
|
99
|
+
def index pcap_path, index_dir
|
|
100
|
+
FileUtils.mkdir_p index_dir
|
|
101
|
+
|
|
102
|
+
command = [@xtractr_path, 'index', index_dir, '--mode', 'forensics', pcap_path]
|
|
103
|
+
Logger.debug "running: #{command.inspect}"
|
|
104
|
+
xtractr_out = Tempfile.new "xtractr_out"
|
|
105
|
+
pid = fork do
|
|
106
|
+
# Xtractr forks a child process. Set process group so we
|
|
107
|
+
# can send signal to all processes in a group.
|
|
108
|
+
STDOUT.reopen xtractr_out
|
|
109
|
+
STDERR.reopen xtractr_out
|
|
110
|
+
exec *command
|
|
111
|
+
end
|
|
112
|
+
#XXX enforce timeout.
|
|
113
|
+
Process.wait pid
|
|
114
|
+
|
|
115
|
+
xtractr_out.rewind
|
|
116
|
+
output = xtractr_out.read
|
|
117
|
+
|
|
118
|
+
unless $?.exitstatus == 0 and Xtractr.index_dir?(index_dir) and output =~ RE_INDEXING_DONE
|
|
119
|
+
Logger.error "Indexing failed with output:\n" + output
|
|
120
|
+
raise XtractrIndexingException, "Indexing failed"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
return get_summary(index_dir)
|
|
124
|
+
ensure
|
|
125
|
+
xtractr_out.close! if xtractr_out
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def get_summary index_dir
|
|
129
|
+
# Start xtractr in browse mode and get summary data
|
|
130
|
+
browser = Instance.new index_dir, @xtractr_path
|
|
131
|
+
about = JSON.parse(browser.get('api/about')[2])
|
|
132
|
+
services = JSON.parse(browser.get('api/services')[2])
|
|
133
|
+
service_names = []
|
|
134
|
+
services["rows"].each do |row|
|
|
135
|
+
service_names << row['name'].downcase
|
|
136
|
+
end
|
|
137
|
+
return { :about => about, :services => service_names }
|
|
138
|
+
ensure
|
|
139
|
+
browser.stop if browser
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Forwards GET request to xtractr instance created for index_dir.
|
|
143
|
+
# A relative path will be expanded relative to the configured index_dir.
|
|
144
|
+
def get index_dir, url
|
|
145
|
+
xtractr = nil
|
|
146
|
+
xtractr = xtractr_for index_dir
|
|
147
|
+
xtractr.get url
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Forwards POST request to xtractr instance created for index_dir.
|
|
151
|
+
# A relative path will be expanded relative to the configured index_dir.
|
|
152
|
+
def post index_dir, url, body
|
|
153
|
+
xtractr = nil
|
|
154
|
+
xtractr = xtractr_for index_dir
|
|
155
|
+
xtractr.post url, body
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def xtractr_for index_dir
|
|
159
|
+
# Ensure index_dir path is absolute.
|
|
160
|
+
if index_dir.slice(0,1) != '/'
|
|
161
|
+
index_dir = File.expand_path(File.join(@index_dir, index_dir))
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Get or create xtractr instance.
|
|
165
|
+
@xcache_lock.synchronize do
|
|
166
|
+
xtractr = @xcache[index_dir]
|
|
167
|
+
if not xtractr
|
|
168
|
+
xtractr = Instance.new(index_dir, @xtractr_path)
|
|
169
|
+
@xcache[index_dir] = xtractr
|
|
170
|
+
end
|
|
171
|
+
return xtractr
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
require 'pcapr_local/xtractr/instance'
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# http://www.mudynamics.com
|
|
2
|
+
# http://labs.mudynamics.com
|
|
3
|
+
# http://www.pcapr.net
|
|
4
|
+
|
|
5
|
+
require 'thread'
|
|
6
|
+
require 'net/http'
|
|
7
|
+
require 'uri'
|
|
8
|
+
|
|
9
|
+
module PcaprLocal
|
|
10
|
+
class Xtractr
|
|
11
|
+
class Instance
|
|
12
|
+
attr_reader :last_use, :lock, :pid, :port
|
|
13
|
+
|
|
14
|
+
def initialize index_dir, xtractr_path
|
|
15
|
+
@index_dir = index_dir
|
|
16
|
+
@xtractr_path = xtractr_path
|
|
17
|
+
@lock = Mutex.new
|
|
18
|
+
@last_use = Time.new.to_f # for idle timeouts
|
|
19
|
+
@pid = nil
|
|
20
|
+
@port = nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Does GET request and returns response headers and body.
|
|
24
|
+
def get path_and_params
|
|
25
|
+
start if not @pid
|
|
26
|
+
@lock.synchronize do
|
|
27
|
+
@last_use = Time.new.to_f
|
|
28
|
+
|
|
29
|
+
# Make request to xtractr
|
|
30
|
+
uri = URI.parse("http://127.0.0.1:#{@port}/#{path_and_params}")
|
|
31
|
+
response = Net::HTTP.get_response(uri)
|
|
32
|
+
|
|
33
|
+
# Copy headers from response
|
|
34
|
+
headers = {}
|
|
35
|
+
response.each_header {|name,val| headers[name] = val}
|
|
36
|
+
|
|
37
|
+
return response.code.to_i, headers, response.body
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Does POST request and returns response headers and body.
|
|
42
|
+
def post path_and_params, post_body
|
|
43
|
+
start if not @pid
|
|
44
|
+
@lock.synchronize do
|
|
45
|
+
@last_use = Time.new.to_f
|
|
46
|
+
|
|
47
|
+
# Make request to xtractr
|
|
48
|
+
Net::HTTP.start('localhost', @port) do |http|
|
|
49
|
+
http.request_post "/#{path_and_params}", post_body do |response|
|
|
50
|
+
headers = {}
|
|
51
|
+
response.each_header {|name,val| headers[name] = val}
|
|
52
|
+
return response.code.to_i, headers, response.body
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Starts underlying process.
|
|
59
|
+
def start
|
|
60
|
+
@lock.synchronize do
|
|
61
|
+
return if @pid
|
|
62
|
+
err = nil
|
|
63
|
+
# There is a remote possibility that the random port we pick will be
|
|
64
|
+
# in use at the moment we try to bind to it. Thus the retries.
|
|
65
|
+
3.times do |n|
|
|
66
|
+
begin
|
|
67
|
+
do_start
|
|
68
|
+
return
|
|
69
|
+
rescue => err
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
raise err
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class XtractrStartupException < XtractrError; end
|
|
77
|
+
|
|
78
|
+
# Start xtractr
|
|
79
|
+
MAX_START_TIME = 30
|
|
80
|
+
RE_STARTED = /starting on http:/i
|
|
81
|
+
def do_start
|
|
82
|
+
port = Instance.free_local_port
|
|
83
|
+
command = [@xtractr_path, 'browse', @index_dir, '--port', port.to_s]
|
|
84
|
+
Logger.debug "running: #{command.inspect}"
|
|
85
|
+
xtractr_out = Tempfile.new "xtractr_out"
|
|
86
|
+
pid = fork do
|
|
87
|
+
# Xtractr forks a child process. Set process group so we
|
|
88
|
+
# can send signal to all processes in a group.
|
|
89
|
+
Process.setpgid $$, $$
|
|
90
|
+
STDOUT.reopen xtractr_out
|
|
91
|
+
STDERR.reopen xtractr_out
|
|
92
|
+
Dir.chdir @index_dir
|
|
93
|
+
exec *command
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
Timeout.timeout MAX_START_TIME do
|
|
98
|
+
# Wait for "starting" line.
|
|
99
|
+
while xtractr_out.grep(/starting on http:\/\/127\.0\.0\.1:#{port}/i).empty?
|
|
100
|
+
xtractr_out.rewind
|
|
101
|
+
sleep 0.01
|
|
102
|
+
end
|
|
103
|
+
# Sanity check that server is up.
|
|
104
|
+
Net::HTTP.start('127.0.0.1', port) do |http|
|
|
105
|
+
http.options("/")
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
rescue Timeout::Error, SystemCallError
|
|
109
|
+
Logger.error "Xtractr failed to start on port #{port}"
|
|
110
|
+
xtractr_out.rewind
|
|
111
|
+
Logger.error "Xtractr output: #{xtractr_out.read.inspect}"
|
|
112
|
+
kind_kill -pid
|
|
113
|
+
raise XtractrStartupException, "Timeout waiting for xtractr to startup"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
@last_use = Time.new.to_f
|
|
117
|
+
@pid = pid
|
|
118
|
+
@port = port
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Kills underlying xtractr process.
|
|
122
|
+
def stop
|
|
123
|
+
if @pid
|
|
124
|
+
kind_kill -@pid
|
|
125
|
+
@pid = nil
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Sends SIGTERM and waits for process to exit. If process does
|
|
130
|
+
# not exit within wait period, sends SIGKILL. Use a negative
|
|
131
|
+
# pid to kill all members of a process group.
|
|
132
|
+
def kind_kill pid, wait=1
|
|
133
|
+
begin
|
|
134
|
+
Process.kill 15, pid
|
|
135
|
+
# Wait for process (or all group members) to
|
|
136
|
+
# exit.
|
|
137
|
+
Timeout.timeout wait do
|
|
138
|
+
loop { Process.wait(pid) }
|
|
139
|
+
end
|
|
140
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
|
141
|
+
# Processes is dead or group has no members.
|
|
142
|
+
return
|
|
143
|
+
rescue Timeout::Error, StandardError
|
|
144
|
+
# Process did not shutdown in time (or there
|
|
145
|
+
# was an unexpected error).
|
|
146
|
+
Process.kill(9, pid) rescue nil
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Pick a random port over 10000 and verify that it can be bound to (on localhost)
|
|
151
|
+
def self.free_local_port
|
|
152
|
+
while true
|
|
153
|
+
port = rand(0xffff-10000) + 10000
|
|
154
|
+
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
|
155
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
|
156
|
+
addr = Socket.pack_sockaddr_in(port, '127.0.0.1')
|
|
157
|
+
begin
|
|
158
|
+
socket.bind addr
|
|
159
|
+
return port
|
|
160
|
+
rescue Errno::EADDRINUSE
|
|
161
|
+
next # port in use
|
|
162
|
+
ensure
|
|
163
|
+
socket.close
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
|