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