pcapr-local 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
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,163 @@
1
+ var closet = closet || {};
2
+ closet.reports = closet.reports || [];
3
+ closet.reports.push({
4
+ name: 'overview',
5
+ title: 'Overview',
6
+ items: [{
7
+ name: 'services',
8
+ title: 'Services',
9
+ options: function(pcap, opts, cb) {
10
+ opts.choice('filter', '', { values: [''].concat(['udp', 'tcp']) });
11
+ opts.choice('limit', 10, { values: [ '', 10, 20, 50, 100 ] });
12
+ opts.choice('order', 'count', { values: ['count', 'service'] });
13
+ opts.boolean('descending', true);
14
+ opts.boolean('normalize', true);
15
+ cb();
16
+ },
17
+ create: function(ctx, $root, pcap, opts) {
18
+ if (opts.order) {
19
+ opts = $.extend({}, opts, {
20
+ order: { count: 'value', service: 'key' }[opts.order]
21
+ });
22
+ }
23
+
24
+ if (opts.filter) {
25
+ var q = 'flow.proto:' + (opts.filter === 'udp' ? 17 : 6);
26
+ var r = closet.mr.count('flow.service');
27
+ closet.api.flows.report(pcap._id, q, r, function(data) {
28
+ closet.render.table($root, data, opts);
29
+ });
30
+ } else {
31
+ closet.api.field.terms(pcap._id, 'pkt.service', null, null, function(data) {
32
+ closet.render.table($root, data, opts);
33
+ });
34
+ }
35
+ }
36
+ }, {
37
+ name: 'sizes',
38
+ title: 'Packet Size',
39
+ options: function(pcap, opts, cb) {
40
+ pcap.index.services.sort();
41
+ opts.choice('filter', '', { 'values': [''].concat(pcap.index.services) });
42
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
43
+ opts.choice('order', 'count', { values: ['count', 'size'] });
44
+ opts.boolean('descending', true);
45
+ opts.boolean('normalize', true);
46
+ cb();
47
+ },
48
+ create: function(ctx, $root, pcap, opts) {
49
+ if (opts.order) {
50
+ opts = $.extend({}, opts, {
51
+ order: { count: 'value', size: 'key' }[opts.order]
52
+ });
53
+ }
54
+
55
+ if (opts.filter) {
56
+ var q = 'pkt.service:' + opts.filter;
57
+ var r = closet.mr.count('pkt.length');
58
+ closet.api.packets.report(pcap._id, q, r, function(data) {
59
+ closet.render.table($root, data, opts);
60
+ });
61
+ } else {
62
+ closet.api.field.terms(pcap._id, 'pkt.length', null, null, function(data) {
63
+ closet.render.table($root, data, opts);
64
+ });
65
+ }
66
+ }
67
+ }, {
68
+ name: 'mac',
69
+ title: 'MAC Addresses',
70
+ apply: function(pcap, fields) {
71
+ return fields.hasOwnProperty('eth.addr');
72
+ },
73
+ options: function(pcap, opts, cb) {
74
+ pcap.index.services.sort();
75
+ opts.choice('filter', '', { 'values': [''].concat(pcap.index.services) });
76
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
77
+ opts.choice('order', 'count', { values: ['count', 'mac'] });
78
+ opts.boolean('descending', true);
79
+ opts.boolean('normalize', true);
80
+ cb();
81
+ },
82
+ create: function(ctx, $root, pcap, opts) {
83
+ if (opts.order) {
84
+ opts = $.extend({}, opts, {
85
+ order: { count: 'value', mac: 'key' }[opts.order]
86
+ });
87
+ }
88
+
89
+ if (opts.filter) {
90
+ var q = 'pkt.service:' + opts.filter;
91
+ var r = closet.mr.count('eth.addr');
92
+ closet.api.packets.report(pcap._id, q, r, function(data) {
93
+ closet.render.table($root, data, opts);
94
+ });
95
+ } else {
96
+ closet.api.field.terms(pcap._id, 'eth.addr', null, null, function(data) {
97
+ closet.render.table($root, data, opts);
98
+ });
99
+ }
100
+ }
101
+ }, {
102
+ name: 'ipv4',
103
+ title: 'IPv4 Addresses',
104
+ apply: function(pcap, fields) {
105
+ return fields.hasOwnProperty('ip.host');
106
+ },
107
+ options: function(pcap, opts, cb) {
108
+ pcap.index.services.sort();
109
+ opts.choice('filter', '', { 'values': [''].concat(pcap.index.services) });
110
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
111
+ opts.choice('order', 'count', { values: ['count', 'ip'] });
112
+ opts.boolean('descending', true);
113
+ opts.boolean('normalize', true);
114
+ cb();
115
+ },
116
+ create: function(ctx, $root, pcap, opts) {
117
+ if (opts.order) {
118
+ opts = $.extend({}, opts, {
119
+ order: { count: 'value', ip: 'key' }[opts.order]
120
+ });
121
+ }
122
+
123
+ // TODO: We'll need to have the ipv4/ipv6 type within each
124
+ // flow. This will allow us to rapidly scan for v4/v6 addresses
125
+ // instead of running through all the packets
126
+ var q = 'pkt.first:true ip.version:4';
127
+ q = q + (opts.filter ? ' pkt.service:' + opts.filter : '')
128
+ var r = closet.mr.count('ip.host');
129
+ closet.api.packets.report(pcap._id, q, r, function(data) {
130
+ closet.render.table($root, data, opts);
131
+ });
132
+ }
133
+ }, {
134
+ name: 'ipv6',
135
+ title: 'IPv6 Addresses',
136
+ apply: function(pcap, fields) {
137
+ return fields.hasOwnProperty('ipv6.host');
138
+ },
139
+ options: function(pcap, opts, cb) {
140
+ pcap.index.services.sort();
141
+ opts.choice('filter', '', { 'values': [''].concat(pcap.index.services) });
142
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
143
+ opts.choice('order', 'count', { values: ['count', 'ip'] });
144
+ opts.boolean('descending', true);
145
+ opts.boolean('normalize', true);
146
+ cb();
147
+ },
148
+ create: function(ctx, $root, pcap, opts) {
149
+ if (opts.order) {
150
+ opts = $.extend({}, opts, {
151
+ order: { count: 'value', ip: 'key' }[opts.order]
152
+ });
153
+ }
154
+
155
+ var q = 'pkt.first:true ip.version:6';
156
+ q = q + (opts.filter ? ' pkt.service:' + opts.filter : '');
157
+ var r = closet.mr.count('ipv6.host');
158
+ closet.api.packets.report(pcap._id, q, r, function(data) {
159
+ closet.render.table($root, data, opts);
160
+ });
161
+ }
162
+ }]
163
+ });
@@ -0,0 +1,159 @@
1
+ var closet = closet || {};
2
+ closet.reports = closet.reports || [];
3
+ closet.reports.push({
4
+ name: 'sip',
5
+ title: 'SIP',
6
+ apply: function(pcap, fields) {
7
+ return fields.hasOwnProperty('sip.method');
8
+ },
9
+ items: [{
10
+ name: 'calls',
11
+ title: 'Calls',
12
+ apply: function(pcap, fields) {
13
+ return fields.hasOwnProperty('sip.call.id');
14
+ },
15
+ create: function(ctx, $root, pcap, opts) {
16
+ var calls = [];
17
+
18
+ var $status = $root.find('span.throbber');
19
+
20
+ // 1. Find all the unique call-ids in the pcap
21
+ var q = 'pkt.service:sip* sip.method:invite';
22
+ var r = closet.mr.count('sip.call.id');
23
+ closet.api.packets.report(pcap._id, q, r, function(data) {
24
+ async.forEachSeries(data.rows,
25
+ function(row, next) {
26
+ var call = { id: row.key };
27
+ calls.push(call);
28
+
29
+ $status.text('processing call-id ' + call.id);
30
+
31
+ // 2. Find the first INVITE that has this callid. This tell us
32
+ // who started the call.
33
+ q = 'pkt.service:sip* sip.method:invite sip.call.id:' + closet.util.escapeQuery(call.id);
34
+ closet.api.packets.list(pcap._id, { q:q, limit:1, include_fields:true }, function(data) {
35
+ var pkt = data.rows[0];
36
+ call.src = pkt.src;
37
+ call.dst = pkt.dst;
38
+ pkt.fields = closet.util.kvsToObject(pkt.fields);
39
+ call.from = (pkt.fields['sip.from.user']||['unknown'])[0].replace(/;.*$/, '');
40
+ call.to = (pkt.fields['sip.to.user']||['unknown'])[0].replace(/;.*$/, '');
41
+
42
+ // 3. Find the unique SIP flows that make up this call
43
+ q = 'sip.call.id:' + closet.util.escapeQuery(call.id);
44
+ var r = closet.mr.count('pkt.flow');
45
+ closet.api.packets.report(pcap._id, q, r, function(data) {
46
+ var fids = $.map(data.rows, function(row) { return row.key; });
47
+
48
+ // 4. And fetch the flow information
49
+ var qf = 'flow.id:(' + fids.join('||') + ')';
50
+ closet.api.flows.list(pcap._id, { q: qf }, function(data) {
51
+ call.flows = data.rows;
52
+
53
+ // 5. Compute the span for this call-leg
54
+ call.span = {};
55
+ $.each(call.flows, function(_, flow) {
56
+ call.span.min = Math.min(call.span.min || flow.first, flow.first);
57
+ call.span.max = Math.max(call.span.max || flow.last, flow.last);
58
+ });
59
+
60
+ $status.text('extracting RTP streams for call-id ' + call.id);
61
+
62
+ // 5. For this call-id, look for sdp packets that contain RTP information
63
+ var qrtp = 'sip.call.id:' + closet.util.escapeQuery(call.id) + ' sdp.version:0';
64
+ closet.api.packets.list(pcap._id, { q: qrtp, include_fields: true }, function(data) {
65
+ var rtps = { ips: [], ports: [], media: [] };
66
+ $.each(data.rows, function(_, pkt) {
67
+ pkt.fields = closet.util.kvsToObject(pkt.fields);
68
+ var ips = pkt.fields['sdp.connection.info.address'];
69
+ var ports = pkt.fields['sdp.media.port'];
70
+ var media = pkt.fields['sdp.mime.type'];
71
+ rtps.ips = rtps.ips.concat(ips || []);
72
+ rtps.ports = rtps.ports.concat(ports || []);
73
+ rtps.media = rtps.media.concat(media || []);
74
+ });
75
+
76
+ // 6. And then stitch the RTP flows within the span together with the SIP flows
77
+ var qfrtp = 'flow.first:>=' + call.span.min + ' flow.last:<=' + call.span.max + ' flow.service:RTP flow.dst:(' + rtps.ips.join('||') + ') flow.dport:(' + rtps.ports.join('||') + ')';
78
+ closet.api.flows.list(pcap._id, { q: qfrtp }, function(data) {
79
+ call.flows = call.flows.concat(data.rows);
80
+ call.rtp = data.rows;
81
+ call.media = closet.util.unique(rtps.media);
82
+
83
+ // Compute the #total packets for this call-leg
84
+ var fids = $.map(call.flows, function(flow) { return flow.id; });
85
+ call.q = 'pkt.flow:(' + fids.join('||') + ')';
86
+ call.packets = 0;
87
+ $.each(call.flows, function(_, flow) {
88
+ call.packets += flow.packets;
89
+ });
90
+ next();
91
+ });
92
+ });
93
+ });
94
+ });
95
+ });
96
+ },
97
+ function() {
98
+ // TODO:
99
+ // - For each flow that matches the call-id, show the call-flow
100
+ // - Show statistics about the message types + error codes
101
+ ctx.render('/templates/sip.calls.template', { pcap: pcap, calls: calls }, function(content) {
102
+ $root.html(content);
103
+ });
104
+ }
105
+ );
106
+ });
107
+ }
108
+ }, {
109
+ name: 'phones',
110
+ title: 'Phones',
111
+ apply: function(pcap, fields) {
112
+ return fields.hasOwnProperty('sip.user.agent');
113
+ },
114
+ options: function(pcap, opts, cb) {
115
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
116
+ opts.choice('order', 'count', { values: [ 'count', 'phone' ] });
117
+ opts.boolean('descending', true);
118
+ opts.boolean('normalize', true);
119
+ cb();
120
+ },
121
+ create: function(ctx, $root, pcap, opts) {
122
+ opts = $.extend({}, opts, { value: 'count' });
123
+ if (opts.order) {
124
+ opts.order = { count: 'value', phone: 'key' }[opts.order];
125
+ }
126
+
127
+ var q = 'flow.service:sip*';
128
+ var r = closet.mr.count('sip.user.agent');
129
+ closet.api.flows.report(pcap._id, q, r, function(data) {
130
+ closet.render.table($root, data, opts);
131
+ });
132
+ }
133
+ },{
134
+ name: 'servers',
135
+ title: 'Servers',
136
+ apply: function(pcap, fields) {
137
+ return fields.hasOwnProperty('sip.server');
138
+ },
139
+ options: function(pcap, opts, cb) {
140
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
141
+ opts.choice('order', 'count', { values: [ 'count', 'server' ] });
142
+ opts.boolean('descending', true);
143
+ opts.boolean('normalize', true);
144
+ cb();
145
+ },
146
+ create: function(ctx, $root, pcap, opts) {
147
+ opts = $.extend({}, opts, { value: 'count' });
148
+ if (opts.order) {
149
+ opts.order = { count: 'value', server: 'key' }[opts.order];
150
+ }
151
+
152
+ var q = 'flow.service:sip*';
153
+ var r = closet.mr.count('sip.server');
154
+ closet.api.flows.report(pcap._id, q, r, function(data) {
155
+ closet.render.table($root, data, opts);
156
+ });
157
+ }
158
+ }]
159
+ });
@@ -0,0 +1,72 @@
1
+ var closet = closet || {};
2
+ closet.reports = closet.reports || [];
3
+ closet.reports.push({
4
+ name: 'tcp',
5
+ title: 'TCP',
6
+ apply: function(pcap, fields) {
7
+ return fields.hasOwnProperty('tcp.stream');
8
+ },
9
+ items: [{
10
+ name: 'dport',
11
+ title: 'Destination Ports',
12
+ options: function(pcap, opts, cb) {
13
+ var q = 'flow.proto:6';
14
+ var r = closet.mr.count('flow.service');
15
+ closet.api.flows.report(pcap._id, q, r, function(data) {
16
+ var services = $.map(data.rows, function(row) { return row.key; });
17
+ opts.choice('filter', '', { 'values': [''].concat(services) });
18
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
19
+ opts.choice('order', 'count', { values: ['count', 'port'] });
20
+ opts.boolean('descending', true);
21
+ opts.boolean('normalize', true);
22
+ cb();
23
+ });
24
+ },
25
+ create: function(ctx, $root, pcap, opts) {
26
+ if (opts.order) {
27
+ opts = $.extend({}, opts, {
28
+ order: { count: 'value', port: 'key' }[opts.order]
29
+ });
30
+ }
31
+
32
+ var q = 'flow.proto:6';
33
+ q = q + (opts.filter ? ' flow.service:' + opts.filter : '');
34
+ var r = closet.mr.count('flow.dport');
35
+ closet.api.flows.report(pcap._id, q, r, function(data) {
36
+ closet.render.table($root, data, opts);
37
+ });
38
+ }
39
+ }, {
40
+ name: 'bandwidth',
41
+ title: 'Bandwidth Utilization',
42
+ options: function(pcap, opts, cb) {
43
+ var q = 'flow.proto:6';
44
+ closet.api.flows.services(pcap._id, q, 0.0, pcap.index.about.duration, function(data) {
45
+ var services = $.map(data.rows, function(row) { return row.key; });
46
+ opts.choice('filter', '', { 'values': [''].concat(services) });
47
+ opts.choice('limit', 10, { 'values': [ '', 10, 20, 50, 100 ] });
48
+ opts.choice('order', 'bytes', { values: ['bytes', 'host'] });
49
+ opts.boolean('descending', true);
50
+ opts.boolean('normalize', false);
51
+ cb();
52
+ });
53
+ },
54
+ create: function(ctx, $root, pcap, opts) {
55
+ opts = $.extend({}, opts, { value: 'bytes' });
56
+ if (opts.order) {
57
+ opts.order = { bytes: 'value', host: 'key' }[opts.order]
58
+ }
59
+
60
+ var q = 'flow.proto:6';
61
+ q = q + (opts.filter ? ' flow.service:' + opts.filter : '');
62
+ var r = closet.mr.sum('flow.src', 'flow.bytes');
63
+ closet.api.flows.report(pcap._id, q, r, function(data) {
64
+ closet.render.table($root, data, opts);
65
+ });
66
+ }
67
+ }
68
+ // TODO:
69
+ // - connection rate
70
+ // - scans
71
+ ]
72
+ });
@@ -0,0 +1,263 @@
1
+ var closet = closet || {};
2
+ closet.reports = closet.reports || [];
3
+ closet.reports.push({
4
+ name: 'visualize',
5
+ title: 'Visualize',
6
+ apply: function(pcap, fields) {
7
+ return pcap.index.about.flows > 0;
8
+ },
9
+ items:[{
10
+ name: 'flows',
11
+ title: 'Flows',
12
+ options: function(pcap, opts, cb) {
13
+ closet.api.flows.clients(pcap._id, '*', 0.0, pcap.index.about.duration, function(data) {
14
+ var clients = $.map(data.rows, function(row) { return row.key; });
15
+ opts.choice('client', '', { values: [''].concat(clients) });
16
+ closet.api.flows.servers(pcap._id, '*', 0.0, pcap.index.about.duration, function(data) {
17
+ var servers = $.map(data.rows, function(row) { return row.key; });
18
+ opts.choice('server', '', { values: [''].concat(servers) });
19
+ closet.api.flows.services(pcap._id, '*', 0.0, pcap.index.about.duration, function(data) {
20
+ var services = $.map(data.rows, function(row) { return row.key; });
21
+ opts.choice('service', '', { values: [''].concat(services) });
22
+ opts.choice('group', 'service', { values: [ 'service', 'src', 'dst', 'dport' ] });
23
+ opts.choice('speed', 'normal', { values: [ 'normal', 'slow' ] });
24
+ cb();
25
+ });
26
+ });
27
+ });
28
+ },
29
+ create: (function() {
30
+ var globalReportId = 0;
31
+
32
+ return function(ctx, $root, pcap, opts) {
33
+ var reportId = ++globalReportId;
34
+ var buckets = {};
35
+ var flows = {};
36
+ var packets = [];
37
+ var npackets = 1;
38
+ var timeout = { normal:50, slow:100 }[opts.speed];
39
+ var paused = false;
40
+ var fetching = false;
41
+ var timerId;
42
+ $root.html(
43
+ '<a class="create" href="javascript:void(0)">create</a>' +
44
+ '<a class="pause" href="javascript:void(0)" style="display:none">pause</a>' +
45
+ '<p/>' +
46
+ '<div class="status" style="padding-bottom:20px"></div>' +
47
+ '<table class="replay">' +
48
+ '<tbody>' +
49
+ '</tbody>' +
50
+ '</table>' +
51
+ '<div class="info" style="margin-top:20px"></div>'
52
+ );
53
+
54
+ var $buckets = $root.find('table.replay tbody');
55
+ var $status = $root.find('div.status');
56
+ var $create = $root.find('a.create');
57
+ var $pause = $root.find('a.pause');
58
+ var $info = $root.find('div.info');
59
+
60
+ $pause.click(function() {
61
+ paused = !paused;
62
+ if (paused) {
63
+ $pause.text('resume');
64
+ } else {
65
+ $pause.text('pause');
66
+ $info.empty();
67
+ timerId = timerId || setTimeout(function() { _next(); }, timeout);
68
+ }
69
+ });
70
+
71
+ // Create the key used to bucketize the flows
72
+ var _key = function(flow) {
73
+ if (opts.group === 'dport') {
74
+ return (flow.proto === 6 ? 'tcp/' : 'udp/') + flow.dport;
75
+ }
76
+ return flow[opts.group];
77
+ };
78
+
79
+ // If we are filtering on a service, map the service to the set
80
+ // of ports so that we can filter the packets that belong to
81
+ // that flow
82
+ if (opts.service) {
83
+ var q = 'flow.service:' + closet.util.escapeQuery(opts.service.replace(/-\/.*/, ''));
84
+ var r = closet.mr.count('flow.dport');
85
+ closet.api.flows.report(pcap._id, q, r, function(data) {
86
+ opts.ports = $.map(data.rows, function(row) { return row.key; });
87
+ });
88
+ }
89
+
90
+ // Generate the query depending on the opts setting
91
+ var _query = function(id) {
92
+ var q = 'pkt.flow:>=1 pkt.id:>=' + (id+1);
93
+ if (opts.client) {
94
+ var client = closet.util.escapeQuery(opts.client);
95
+ q += ' ((pkt.dir:0 pkt.src:' + client + ') || (pkt.dir:1 pkt.dst:' + client + '))';
96
+ }
97
+ if (opts.server) {
98
+ var server = closet.util.escapeQuery(opts.server);
99
+ q += ' ((pkt.dir:0 pkt.dst:' + server + ') || (pkt.dir:1 pkt.src:' + server + '))';
100
+ }
101
+ if (opts.ports) {
102
+ var ports = opts.ports.join('||');
103
+ q += ' ((pkt.dir:0 udp.dstport|tcp.dstport:(' + ports + ')) || (pkt.dir:1 udp.srcport|tcp.srcport:(' + ports + ')))';
104
+ }
105
+ if (opts.group === 'dport') {
106
+ q += ' (ip.proto:(6||17))';
107
+ }
108
+
109
+ return q;
110
+ }
111
+
112
+ // Add a flow to the list. If it's the first flow for a bucket,
113
+ // create the bucket as well.
114
+ var _add = function(flow) {
115
+ var key = _key(flow);
116
+ if (!buckets.hasOwnProperty(key)) {
117
+ var $tr = $(
118
+ '<tr>' +
119
+ '<td style="white-space:nowrap">' + closet.util.escapeHTML(key) + '</td>' +
120
+ '<td class="flows"></td>' +
121
+ '</tr>'
122
+ );
123
+
124
+ $buckets.append($tr);
125
+ buckets[key] = {};
126
+ buckets[key].$td = $tr.find('td.flows');
127
+ buckets[key].nflows = 0;
128
+ }
129
+
130
+ var $span = $(
131
+ '<div class="flow" style="cursor:pointer;float:left;position:relative;background-color:#ffdddd;border:1px solid red;margin-right:5px;margin-left:5px;margin-right:10px;height:1.2em;width:30px">' +
132
+ '<div class="fill" style="position:absolute;background-color:#e66c25;height:100%;width:1%"></div>' +
133
+ '</div>'
134
+ );
135
+
136
+ $span.click(function() {
137
+ $info.html(
138
+ 'Flow&nbsp;<strong>' + flow.id + '</strong>.&nbsp;' +
139
+ '<strong>' + flow.src + '</strong>' +
140
+ '&nbsp;&raquo;&nbsp;' +
141
+ '<strong>' + flow.dst + '</strong>' + '&nbsp::&nbsp;' +
142
+ '<strong>' + closet.util.escapeHTML(flow.service) + '</strong>&nbsp;' +
143
+ closet.util.escapeHTML(flow.title.slice(0, 80)) +
144
+ '<p/>' +
145
+ '<img class="icon" src="/static/image/16x16/Download.png"/>' +
146
+ 'Download <a href="/pcaps/1/pcap/' + pcap._id + '/api/packets/slice?q=pkt.flow:' + flow.id + '">pcap</a> for this flow.'
147
+ );
148
+ });
149
+
150
+ buckets[key].$td.append($span);
151
+ buckets[key].nflows++;
152
+ flow.$span = $span;
153
+ flow.$fill = $span.find('div.fill');
154
+ flow.npackets = 1;
155
+ flows[flow.id] = flow;
156
+ };
157
+
158
+ // Remove a flow from the flow list and if it's the last
159
+ // flow in the bucket, remove the bucketized row as well
160
+ var _remove = function(flow) {
161
+ var key = _key(flow);
162
+ setTimeout(function() {
163
+ delete flows[flow.id];
164
+ flow.$fill.css('background-color', '#dd1122');
165
+ flow.$span.fadeOut(1000, function() {
166
+ $(this).remove();
167
+ if (--buckets[key].nflows === 0) {
168
+ var s_entry = buckets[key];
169
+ delete buckets[key];
170
+ s_entry.$td.parent('tr').remove();
171
+ }
172
+ });
173
+ }, 100);
174
+ };
175
+
176
+ var location = ctx.app.getLocation();
177
+ var _next = function() {
178
+ timerId = undefined;
179
+
180
+ // Abort if the user navigates to a different URL
181
+ if (ctx.app.getLocation() !== location) {
182
+ return;
183
+ }
184
+
185
+ // Abort if the user changes 'opts' to create a new viz
186
+ if (reportId !== globalReportId) {
187
+ return;
188
+ }
189
+
190
+ // Don't process this packet, if the visualization is paused
191
+ if (paused) {
192
+ return;
193
+ }
194
+
195
+ // When we hit the low water-mark, prefetch (async) the
196
+ // next set of packets
197
+ if (packets.length === 30 && !fetching) {
198
+ var last = packets[packets.length-1];
199
+ fetching = true;
200
+ closet.api.packets.list(pcap._id, { q: _query(last.id), limit: 40, terms: false }, function(data) {
201
+ fetching = false;
202
+ packets = packets.concat(data.rows);
203
+ timerId = timerId || setTimeout(function() { _next(); }, timeout);
204
+ });
205
+ }
206
+
207
+ // Process the next packet. If there are no more packets
208
+ // and we are not in the middle of fetching more chunks,
209
+ // then we are done.
210
+ var pkt = packets.shift();
211
+ if (pkt === undefined) {
212
+ if (!fetching) {
213
+ $status.empty();
214
+ $pause.hide();
215
+ $info.empty();
216
+ $create.show();
217
+ }
218
+ return;
219
+ }
220
+
221
+ // Update the status with the current packet
222
+ $status.html(
223
+ 'Packet &nbsp;<strong>' + pkt.src + '</strong>' +
224
+ '&nbsp;&raquo;&nbsp;' +
225
+ '<strong>' + pkt.dst + '</strong>' + '&nbsp::&nbsp;' +
226
+ '<strong>' + closet.util.escapeHTML(pkt.service) + '</strong>&nbsp;' +
227
+ closet.util.escapeHTML(pkt.title.slice(0, 80))
228
+ );
229
+
230
+ // Add/remove flows for this packet
231
+ if (!flows.hasOwnProperty(pkt.flow)) {
232
+ closet.api.flows.list(pcap._id, { q: 'flow.id:' + pkt.flow, limit: 1, terms: false }, function(data) {
233
+ var flow = data.rows[0];
234
+ _add(flow);
235
+ if (flow.first === flow.last) {
236
+ _remove(flow);
237
+ }
238
+ timerId = timerId || setTimeout(function() { _next(); }, timeout);
239
+ });
240
+ } else {
241
+ var flow = flows[pkt.flow];
242
+ var key = _key(flow);
243
+ flow.$fill.css('width', Math.floor((++flow.npackets)*100/flow.packets) + '%');
244
+ if (flow.last === pkt.id) {
245
+ _remove(flow);
246
+ }
247
+ timerId = timerId || setTimeout(function() { _next(); }, timeout);
248
+ }
249
+ };
250
+
251
+ $create.click(function() {
252
+ $(this).hide();
253
+ $pause.show();
254
+ $status.html('<span class="throbber">hang on</span>');
255
+ closet.api.packets.list(pcap._id, { q: _query(0), limit: 100, terms: false }, function(data) {
256
+ packets = data.rows;
257
+ _next();
258
+ });
259
+ });
260
+ };
261
+ }())
262
+ }]
263
+ });