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.
Files changed (203) hide show
  1. data/.document +5 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +64 -0
  4. data/Rakefile +57 -0
  5. data/VERSION +1 -0
  6. data/bin/pcap2par +49 -0
  7. data/bin/startpcapr +40 -0
  8. data/bin/stoppcapr +33 -0
  9. data/bin/xtractr +5 -0
  10. data/lib/environment.rb +106 -0
  11. data/lib/exe/xtractr +0 -0
  12. data/lib/mu/pcap.rb +110 -0
  13. data/lib/mu/pcap/ethernet.rb +148 -0
  14. data/lib/mu/pcap/header.rb +75 -0
  15. data/lib/mu/pcap/io_pair.rb +67 -0
  16. data/lib/mu/pcap/io_wrapper.rb +76 -0
  17. data/lib/mu/pcap/ip.rb +61 -0
  18. data/lib/mu/pcap/ipv4.rb +257 -0
  19. data/lib/mu/pcap/ipv6.rb +148 -0
  20. data/lib/mu/pcap/packet.rb +104 -0
  21. data/lib/mu/pcap/pkthdr.rb +155 -0
  22. data/lib/mu/pcap/reader.rb +61 -0
  23. data/lib/mu/pcap/reader/http_family.rb +170 -0
  24. data/lib/mu/pcap/sctp.rb +367 -0
  25. data/lib/mu/pcap/sctp/chunk.rb +123 -0
  26. data/lib/mu/pcap/sctp/chunk/data.rb +134 -0
  27. data/lib/mu/pcap/sctp/chunk/init.rb +100 -0
  28. data/lib/mu/pcap/sctp/chunk/init_ack.rb +68 -0
  29. data/lib/mu/pcap/sctp/parameter.rb +110 -0
  30. data/lib/mu/pcap/sctp/parameter/ip_address.rb +48 -0
  31. data/lib/mu/pcap/stream_packetizer.rb +72 -0
  32. data/lib/mu/pcap/tcp.rb +505 -0
  33. data/lib/mu/pcap/udp.rb +69 -0
  34. data/lib/mu/scenario/pcap.rb +164 -0
  35. data/lib/mu/scenario/pcap/fields.rb +50 -0
  36. data/lib/mu/scenario/pcap/rtp.rb +71 -0
  37. data/lib/pcapr_local.rb +159 -0
  38. data/lib/pcapr_local/config.rb +336 -0
  39. data/lib/pcapr_local/db.rb +197 -0
  40. data/lib/pcapr_local/scanner.rb +250 -0
  41. data/lib/pcapr_local/server.rb +178 -0
  42. data/lib/pcapr_local/www/favicon.ico +0 -0
  43. data/lib/pcapr_local/www/favicon.png +0 -0
  44. data/lib/pcapr_local/www/home/index.html +138 -0
  45. data/lib/pcapr_local/www/static/image/16x16/Cancel.png +0 -0
  46. data/lib/pcapr_local/www/static/image/16x16/Cancel.png.1 +0 -0
  47. data/lib/pcapr_local/www/static/image/16x16/Download.png +0 -0
  48. data/lib/pcapr_local/www/static/image/16x16/Folder3.png +0 -0
  49. data/lib/pcapr_local/www/static/image/16x16/Full Size.png +0 -0
  50. data/lib/pcapr_local/www/static/image/16x16/Minus.png +0 -0
  51. data/lib/pcapr_local/www/static/image/16x16/Plus.png +0 -0
  52. data/lib/pcapr_local/www/static/image/16x16/Search.png +0 -0
  53. data/lib/pcapr_local/www/static/image/16x16/User.png +0 -0
  54. data/lib/pcapr_local/www/static/image/48x48/Phone.png +0 -0
  55. data/lib/pcapr_local/www/static/image/48x48/Video.png +0 -0
  56. data/lib/pcapr_local/www/static/image/bar-orange.gif +0 -0
  57. data/lib/pcapr_local/www/static/image/beta.png +0 -0
  58. data/lib/pcapr_local/www/static/image/bg.png +0 -0
  59. data/lib/pcapr_local/www/static/image/blockquote.png +0 -0
  60. data/lib/pcapr_local/www/static/image/body-bg.png +0 -0
  61. data/lib/pcapr_local/www/static/image/body-h3.png +0 -0
  62. data/lib/pcapr_local/www/static/image/body-hl1-bg.png +0 -0
  63. data/lib/pcapr_local/www/static/image/body-hl1-h3.png +0 -0
  64. data/lib/pcapr_local/www/static/image/body-hl1-readmore.png +0 -0
  65. data/lib/pcapr_local/www/static/image/body-hl2-bg.png +0 -0
  66. data/lib/pcapr_local/www/static/image/body-hl2-h3.png +0 -0
  67. data/lib/pcapr_local/www/static/image/body-hl2-readmore.png +0 -0
  68. data/lib/pcapr_local/www/static/image/body-hl3-bg.png +0 -0
  69. data/lib/pcapr_local/www/static/image/body-hl3-h3.png +0 -0
  70. data/lib/pcapr_local/www/static/image/body-hl3-readmore.png +0 -0
  71. data/lib/pcapr_local/www/static/image/body-hl4-bg.png +0 -0
  72. data/lib/pcapr_local/www/static/image/body-hl4-h3.png +0 -0
  73. data/lib/pcapr_local/www/static/image/body-hl4-readmore.png +0 -0
  74. data/lib/pcapr_local/www/static/image/body-hl5-h3.png +0 -0
  75. data/lib/pcapr_local/www/static/image/body-hl6-h3.png +0 -0
  76. data/lib/pcapr_local/www/static/image/body-hl7-h3.png +0 -0
  77. data/lib/pcapr_local/www/static/image/body-hl8-h3.png +0 -0
  78. data/lib/pcapr_local/www/static/image/body-readmore.png +0 -0
  79. data/lib/pcapr_local/www/static/image/bottom-bg.png +0 -0
  80. data/lib/pcapr_local/www/static/image/bottom-l.png +0 -0
  81. data/lib/pcapr_local/www/static/image/bottom-r.png +0 -0
  82. data/lib/pcapr_local/www/static/image/btn-search.png +0 -0
  83. data/lib/pcapr_local/www/static/image/bullet-1.png +0 -0
  84. data/lib/pcapr_local/www/static/image/bullet-2.png +0 -0
  85. data/lib/pcapr_local/www/static/image/bullet-3.png +0 -0
  86. data/lib/pcapr_local/www/static/image/bullet-4.png +0 -0
  87. data/lib/pcapr_local/www/static/image/bullet-5.png +0 -0
  88. data/lib/pcapr_local/www/static/image/bullet-6.png +0 -0
  89. data/lib/pcapr_local/www/static/image/bullet-7.png +0 -0
  90. data/lib/pcapr_local/www/static/image/bullet-hl1.png +0 -0
  91. data/lib/pcapr_local/www/static/image/bullet-hl2.png +0 -0
  92. data/lib/pcapr_local/www/static/image/bullet-hl3.png +0 -0
  93. data/lib/pcapr_local/www/static/image/bullet-hl4.png +0 -0
  94. data/lib/pcapr_local/www/static/image/bullet-pathway.png +0 -0
  95. data/lib/pcapr_local/www/static/image/bullet-section1.png +0 -0
  96. data/lib/pcapr_local/www/static/image/bullet-section2.png +0 -0
  97. data/lib/pcapr_local/www/static/image/collapsed.gif +0 -0
  98. data/lib/pcapr_local/www/static/image/crosslink.png +0 -0
  99. data/lib/pcapr_local/www/static/image/expanded.gif +0 -0
  100. data/lib/pcapr_local/www/static/image/favicon.ico +0 -0
  101. data/lib/pcapr_local/www/static/image/favicon.png +0 -0
  102. data/lib/pcapr_local/www/static/image/icon-author.png +0 -0
  103. data/lib/pcapr_local/www/static/image/icon-created.png +0 -0
  104. data/lib/pcapr_local/www/static/image/p-expand.gif +0 -0
  105. data/lib/pcapr_local/www/static/image/pcapr-logo.png +0 -0
  106. data/lib/pcapr_local/www/static/image/powered-by.png +0 -0
  107. data/lib/pcapr_local/www/static/image/section1-bg.png +0 -0
  108. data/lib/pcapr_local/www/static/image/section1-h3.png +0 -0
  109. data/lib/pcapr_local/www/static/image/section1-readmore.png +0 -0
  110. data/lib/pcapr_local/www/static/image/section2-bg.png +0 -0
  111. data/lib/pcapr_local/www/static/image/section2-h3.png +0 -0
  112. data/lib/pcapr_local/www/static/image/section2-readmore.png +0 -0
  113. data/lib/pcapr_local/www/static/image/status-alert.png +0 -0
  114. data/lib/pcapr_local/www/static/image/status-download.png +0 -0
  115. data/lib/pcapr_local/www/static/image/status-info.png +0 -0
  116. data/lib/pcapr_local/www/static/image/status-note.png +0 -0
  117. data/lib/pcapr_local/www/static/image/tab-round.png +0 -0
  118. data/lib/pcapr_local/www/static/image/throbber.gif +0 -0
  119. data/lib/pcapr_local/www/static/image/user.jpg +0 -0
  120. data/lib/pcapr_local/www/static/script/closet/async.js +421 -0
  121. data/lib/pcapr_local/www/static/script/closet/closet.api.js +241 -0
  122. data/lib/pcapr_local/www/static/script/closet/closet.folders.js +94 -0
  123. data/lib/pcapr_local/www/static/script/closet/closet.js +187 -0
  124. data/lib/pcapr_local/www/static/script/closet/closet.mr.js +219 -0
  125. data/lib/pcapr_local/www/static/script/closet/closet.options.js +359 -0
  126. data/lib/pcapr_local/www/static/script/closet/closet.quantity.js +73 -0
  127. data/lib/pcapr_local/www/static/script/closet/closet.render.js +205 -0
  128. data/lib/pcapr_local/www/static/script/closet/closet.report.js +86 -0
  129. data/lib/pcapr_local/www/static/script/closet/closet.reports.http.js +135 -0
  130. data/lib/pcapr_local/www/static/script/closet/closet.reports.overview.js +163 -0
  131. data/lib/pcapr_local/www/static/script/closet/closet.reports.sip.js +159 -0
  132. data/lib/pcapr_local/www/static/script/closet/closet.reports.tcp.js +72 -0
  133. data/lib/pcapr_local/www/static/script/closet/closet.reports.visualize.js +263 -0
  134. data/lib/pcapr_local/www/static/script/closet/closet.util.js +40 -0
  135. data/lib/pcapr_local/www/static/script/jquery/jquery-1.4.2.min.js +154 -0
  136. data/lib/pcapr_local/www/static/script/jquery/jquery-ui.js +10921 -0
  137. data/lib/pcapr_local/www/static/script/jquery/jquery.flot.js +2123 -0
  138. data/lib/pcapr_local/www/static/script/jquery/jquery.flot.selection.js +184 -0
  139. data/lib/pcapr_local/www/static/script/jquery/jquery.flot.stack.js +184 -0
  140. data/lib/pcapr_local/www/static/script/jquery/jquery.form.js +643 -0
  141. data/lib/pcapr_local/www/static/script/jquery/jquery.jsonp.min.js +3 -0
  142. data/lib/pcapr_local/www/static/script/jquery/jquery.menu.js +142 -0
  143. data/lib/pcapr_local/www/static/script/jquery/jquery.suggest.js +308 -0
  144. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.core.js +203 -0
  145. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.slider.js +629 -0
  146. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.sortable.js +1055 -0
  147. data/lib/pcapr_local/www/static/script/jquery/jquery.ui.widget.js +236 -0
  148. data/lib/pcapr_local/www/static/script/json2.js +481 -0
  149. data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.cache.js +115 -0
  150. data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.template.js +117 -0
  151. data/lib/pcapr_local/www/static/script/sammy/sammy.js +1696 -0
  152. data/lib/pcapr_local/www/static/script/tipsy/jquery.tipsy.js +104 -0
  153. data/lib/pcapr_local/www/static/style/c3p0.css +116 -0
  154. data/lib/pcapr_local/www/static/style/jquery.suggest.css +27 -0
  155. data/lib/pcapr_local/www/static/style/page.css +1113 -0
  156. data/lib/pcapr_local/www/static/style/tipsy.css +7 -0
  157. data/lib/pcapr_local/www/templates/browse.services.template +10 -0
  158. data/lib/pcapr_local/www/templates/browse.template +77 -0
  159. data/lib/pcapr_local/www/templates/flows.template +38 -0
  160. data/lib/pcapr_local/www/templates/pcap.template +63 -0
  161. data/lib/pcapr_local/www/templates/sip.calls.template +35 -0
  162. data/lib/pcapr_local/www/templates/statistics.template +6 -0
  163. data/lib/pcapr_local/xtractr.rb +179 -0
  164. data/lib/pcapr_local/xtractr/instance.rb +172 -0
  165. data/pcapr-local.gemspec +297 -0
  166. data/test/mu/pcap/reader/tc_http_family.rb +251 -0
  167. data/test/mu/pcap/tc_ethernet.rb +71 -0
  168. data/test/mu/pcap/tc_header.rb +56 -0
  169. data/test/mu/pcap/tc_ipv4.rb +103 -0
  170. data/test/mu/pcap/tc_ipv6.rb +83 -0
  171. data/test/mu/pcap/tc_packet.rb +44 -0
  172. data/test/mu/pcap/tc_pair.rb +58 -0
  173. data/test/mu/pcap/tc_pkthdr.rb +33 -0
  174. data/test/mu/pcap/tc_reader.rb +76 -0
  175. data/test/mu/pcap/tc_tcp.rb +426 -0
  176. data/test/mu/pcap/tc_udp.rb +33 -0
  177. data/test/mu/pcap/tc_wrapper.rb +80 -0
  178. data/test/mu/scenario/pcap/tc_fields.rb +67 -0
  179. data/test/mu/scenario/pcap/tc_rtp.rb +135 -0
  180. data/test/mu/scenario/sip_signalled_call_1.pcap +0 -0
  181. data/test/mu/scenario/tc_pcap.rb +190 -0
  182. data/test/mu/scenario/test_data/arp.pcap +0 -0
  183. data/test/mu/scenario/test_data/dns.pcap +0 -0
  184. data/test/mu/scenario/test_data/http-v6.pcap +0 -0
  185. data/test/mu/scenario/test_data/http.pcap +0 -0
  186. data/test/mu/scenario/test_data/http_chunked.pcap +0 -0
  187. data/test/mu/scenario/test_data/http_deflate.pcap +0 -0
  188. data/test/mu/scenario/test_data/httpauth3.pcap +0 -0
  189. data/test/mu/scenario/test_data/icmp.pcap +0 -0
  190. data/test/mu/scenario/test_data/sip_signalled_call_1.pcap +0 -0
  191. data/test/mu/tc_pcap.rb +39 -0
  192. data/test/mu/testcase.rb +86 -0
  193. data/test/pcapr_local/arp.pcap +0 -0
  194. data/test/pcapr_local/data.js +3 -0
  195. data/test/pcapr_local/http_chunked.pcap +0 -0
  196. data/test/pcapr_local/tc_api.rb +181 -0
  197. data/test/pcapr_local/test.tgz +0 -0
  198. data/test/pcapr_local/test_scanner.rb +241 -0
  199. data/test/pcapr_local/test_xtractr.rb +219 -0
  200. data/test/pcapr_local/testcase.rb +107 -0
  201. data/test/test_export_to_scenario.sh +25 -0
  202. data/test/test_pcapr_local.rb +29 -0
  203. 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>&raquo;&nbsp;</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">&#9660;</span><span class="time">time</span></td>
14
+ <td>packets</td>
15
+ <td><span style="visibility:hidden">&#9660;</span><span class="src">src</span></td>
16
+ <td>sport</td>
17
+ <td><span style="visibility:hidden">&#9660;</span><span class="dst">dst</span></td>
18
+ <td><span class="dport">dport</span></td>
19
+ <td><span style="visibility:hidden">&#9660;</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:&nbsp;<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">&#9658;</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
+ &nbsp;&raquo;&nbsp;
6
+ <span style="font-weight:bold"><%= closet.util.escapeHTML(call.dst) %></span>
7
+ &nbsp;
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,6 @@
1
+ <div>
2
+ <%= stats.pcaps || 0 %> pcaps,
3
+ <%= stats.flows || 0 %> flows,
4
+ <%= stats.packets || 0 %> packets,
5
+ <%= closet.quantity.bytes(stats.bytes || 0) %>
6
+ </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
+