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,336 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ module PcaprLocal
6
+ module Config
7
+
8
+ HOME = ENV["HOME"] || File.expand_path('./')
9
+
10
+ DEFAULT_CONFIG = {
11
+ # Shared config.
12
+ "install_dir" => "#{HOME}/pcapr.Local/",
13
+ "pcap_dir" => "#{HOME}/pcapr.Local/pcaps",
14
+ "index_dir" => "#{HOME}/pcapr.Local/indexes",
15
+
16
+ # UI (Sinatra)
17
+ "app" => {
18
+ "host" => '127.0.0.1',
19
+ "port" => 8080,
20
+ },
21
+
22
+ # Pcap scanning
23
+ "scanner" => {
24
+ "interval" => 60, # scan every n seconds.
25
+ "queue_delay" => 60, # skip file if was modified in last n seconds.
26
+ },
27
+
28
+ # Couch
29
+ "couch" => {
30
+ "uri" => 'http://127.0.0.1:5984/',
31
+ "database" => "pcapr_local"
32
+ },
33
+
34
+ # Xtractr
35
+ "xtractr" => {
36
+ "path" => 'xtractr',
37
+ "idle_timeout" => 60 # kill xtractr browser after n seconds of idle time
38
+ }
39
+ }
40
+
41
+
42
+ # Tuple of required dependencies and ubuntu package name
43
+ REQUIRED_EXES = [
44
+ ["tshark", "tshark"],
45
+ ["zip", "zip"],
46
+ ]
47
+
48
+ def self.assert_environment
49
+ check_platform
50
+
51
+ REQUIRED_EXES.each do |exe_package|
52
+ check_exe *exe_package
53
+ end
54
+ end
55
+
56
+ def self.check_exe exe, package_name
57
+ if not system "sh -c 'type #{exe} > /dev/null 2>&1'"
58
+ $stderr.puts <<HERE
59
+
60
+ pcapr.Local requires the #{exe} executable to function but #{exe} is either not
61
+ installed or cannot be found in your PATH. Please install #{exe} and ensure
62
+ that it is in your PATH.
63
+
64
+ HERE
65
+
66
+ if system "sh -c 'type apt-get > /dev/null 2>&1'"
67
+ $stderr.puts <<HERE
68
+ If #{exe} is not installed you may be able to install it with:
69
+ 'sudo apt-get install #{package_name}'
70
+
71
+ HERE
72
+ end
73
+
74
+ exit 1
75
+ end
76
+ end
77
+
78
+ # Exit unless we are on Linux (the xtractr exe is linux only).
79
+ def self.check_platform
80
+ if not RUBY_PLATFORM =~ /linux/i
81
+ $stderr.puts "Sorry, pcapr.Local only runs on linux :("
82
+ exit 1
83
+ end
84
+ end
85
+
86
+
87
+ # Return configuration as a Hash. Optionally takes an external
88
+ # configuration file.
89
+ def self.user_config_path
90
+ raise "HOME environment variable is not set" unless ENV['HOME']
91
+ config_dir = File.join(ENV['HOME'], '.pcapr_local')
92
+ File.join(config_dir, 'config')
93
+ end
94
+
95
+ def self.config config_path=nil
96
+ config_path ||= user_config_path
97
+
98
+ if not File.exist? config_path
99
+ self.create_config_file config_path
100
+ end
101
+
102
+ config = DEFAULT_CONFIG.dup
103
+ begin
104
+ user_config = JSON.parse(File.read(config_path))
105
+ rescue JSON::ParserError => e
106
+ raise "Config file is not well formed JSON, please correct or delete #{config_path}"
107
+ end
108
+ config = config_merge(config, user_config)
109
+
110
+ # Derived config (not persisted)
111
+ config['pidfile'] = File.join(config.fetch('install_dir'), '.server.pid')
112
+ config['log_dir'] = File.join(config.fetch('install_dir'), 'log')
113
+
114
+ return config
115
+ end
116
+
117
+ private
118
+
119
+ def self.create_config_file config_path
120
+ config = get_user_config
121
+ FileUtils.mkdir_p File.dirname(config_path)
122
+ File.open config_path, 'w' do |f|
123
+ f.puts JSON.pretty_generate(config)
124
+ end
125
+ puts "\nThank you. Configuration is saved at #{config_path}."
126
+ sleep 2
127
+ end
128
+
129
+ # Recursively applies updates in update config to start config.
130
+ # Does not apply updates unless they are already present in the
131
+ # starting config
132
+ def self.config_merge start, update
133
+ start = start.dup
134
+ start.to_a.each do |key, val|
135
+ next unless update.include? key
136
+ update_val = update[key]
137
+ if val.is_a? Hash
138
+ if update_val.is_a? Hash
139
+ # Both values are hashes, recurse.
140
+ start[key] = config_merge(val, update_val)
141
+ else
142
+ # Update is not expected type, ignore it.
143
+ end
144
+ else
145
+ start[key] = update_val
146
+ end
147
+ end
148
+ return start
149
+ end
150
+
151
+ Opt = Struct.new :key, :default, :validate, :question
152
+
153
+ # Interactively gathers configuration from the user and returns config hash.
154
+ def self.get_user_config
155
+ config = JSON.parse(DEFAULT_CONFIG.to_json)
156
+ user_opts = []
157
+
158
+ # install dir
159
+ pcap_dir = Opt.new "install_dir"
160
+ pcap_dir.question = "Where should pcapr.Local store user files?"
161
+ pcap_dir.default = "#{HOME}/pcapr.Local"
162
+ pcap_dir.validate = :dir
163
+ user_opts << pcap_dir
164
+
165
+ # pcap dir
166
+ pcap_dir = Opt.new "pcap_dir"
167
+ pcap_dir.question = "Which directory would you like to scan for indexable pcaps?"
168
+ pcap_dir.default = Proc.new { File.join(config["install_dir"], 'pcaps') }
169
+ pcap_dir.validate = :dir
170
+ user_opts << pcap_dir
171
+
172
+ # index dir
173
+ index_dir = Opt.new "index_dir"
174
+ index_dir.question = "Where would you like to store index files?"
175
+ index_dir.default = Proc.new { File.join(config["install_dir"], 'indexes') }
176
+ index_dir.validate = :dir
177
+ user_opts << index_dir
178
+
179
+ # host
180
+ app_host = Opt.new "app.host"
181
+ app_host.question = "What IP address should pcapr.Local run on? Use 0.0.0.0 to listen on all interfaces."
182
+ app_host.default = "127.0.0.1"
183
+ app_host.validate = :app_host
184
+ user_opts << app_host
185
+
186
+ # port
187
+ app_port = Opt.new "app.port"
188
+ app_port.question = "What port should pcapr.Local listen on?"
189
+ app_port.default = "8080"
190
+ app_port.validate = :app_port
191
+ user_opts << app_port
192
+
193
+ # CouchDB database name
194
+ database = Opt.new "couch.database"
195
+ database.question = "Pick a name for your CouchDB database (database will be created automatically)."
196
+ user = ENV['LOGNAME'] || Process.uid
197
+ database.default = "pcapr_local_#{user}"
198
+ database.validate = :db_name
199
+ user_opts << database
200
+
201
+ # CouchDB server
202
+ couch_uri = Opt.new "couch.uri"
203
+ couch_uri.question = "pcapr.Local requires CouchDB to run. Where is your CouchDB server?"
204
+ couch_uri.default = "http://127.0.0.1:5984"
205
+ couch_uri.validate = :couch_uri
206
+ user_opts << couch_uri
207
+
208
+ user_opts.each do |opt|
209
+ ask_user opt, config
210
+ end
211
+
212
+ return config
213
+ end
214
+
215
+ def self.ask_user opt, config
216
+ stty_save = `stty -g`.chomp
217
+ begin
218
+ # Ask question.
219
+ puts "", opt.question
220
+
221
+ # Show default value in prompt and get answer.
222
+ default = opt.default
223
+ if opt.default.is_a? Proc
224
+ default = opt.default.call
225
+ else
226
+ default = opt.default
227
+ end
228
+ choice = Readline.readline("[#{default}] ").strip
229
+ if choice.empty?
230
+ choice = default
231
+ end
232
+
233
+ # Validate and possibly change answer.
234
+ if opt.validate
235
+ choice = Validate.send(opt.validate, choice, config)
236
+ end
237
+ rescue Validate::Error => e
238
+ puts "\nError: #{e.message}"
239
+ retry
240
+ rescue Interrupt
241
+ system("stty", stty_save)
242
+ puts "\naborting"
243
+ exit 1
244
+ end
245
+
246
+ opt_set config, opt.key, choice
247
+ end
248
+
249
+ # Navigates nested hashes to set options in the form "foo.bar.baz"
250
+ def self.opt_set hash, dotted_name, value
251
+ keys = dotted_name.split('.')
252
+ last_key = keys.pop
253
+ keys.each {|k| hash = hash[k] }
254
+ hash[last_key] = value
255
+ end
256
+
257
+ module Validate
258
+ class Error < StandardError ; end
259
+
260
+ def self.app_host host, config
261
+ begin
262
+ server = TCPServer.new host, 0
263
+ server.close
264
+ rescue => e
265
+ raise Error, "Got error '#{e.message}' when trying to create server for this host. Please pick a different host."
266
+ end
267
+ return host
268
+ end
269
+
270
+ def self.dir dir, config
271
+ begin
272
+ dir = File.expand_path dir
273
+ FileUtils.mkdir_p dir
274
+ rescue
275
+ raise Error, "Directory (#{dir}) could not be created. Please choose a different directory."
276
+ end
277
+ return dir
278
+ end
279
+
280
+ def self.app_port port, config
281
+ begin
282
+ port = Integer(port)
283
+ rescue
284
+ raise Error, "'#{port}' is not a valid port."
285
+ end
286
+ begin
287
+ server = TCPServer.new config["app_host"], port
288
+ server.close
289
+ rescue => e
290
+ raise Error, "Got error '#{e.message}' when trying to listen on #{config['app_host']}:#{port}."
291
+ end
292
+ return port
293
+ end
294
+
295
+ def self.db_name name, config
296
+ unless name =~ /\A[a-zA-Z0-9_]+\Z/
297
+ raise Error, "Database name can include only letters numbers and underscores."
298
+ end
299
+ return name
300
+ end
301
+
302
+ def self.couch_uri uri, config
303
+ # Check couch install is reachable.
304
+ begin
305
+ server = CouchRest::Server.new uri
306
+ server.info
307
+ rescue => e
308
+ err = "Could not connect to couchdb at #{uri}. Got error '#{e.message}'\n"
309
+ if system('which apt-get > /dev/null')
310
+ err << "If CouchDB is not installed you may be able to install it with:\n"
311
+ err << " 'sudo apt-get install couchdb'"
312
+ else
313
+ err << "Is CouchDB installed?"
314
+ end
315
+ raise Error, err
316
+ end
317
+
318
+ # Check that credentials are sufficient by actually creating the database.
319
+ db_name = config['couch']['database']
320
+ begin
321
+ db = DB.get_db "uri" => uri, "database" => db_name, "host" => 'foo', "port" => 3
322
+ rescue RestClient::Exception => e
323
+ err = "Got '#{e.message}' while creating database.\n"
324
+ err << "If you have authentication enabled in CouchDB, please include username and\n"
325
+ err << "password in the URI like:\n"
326
+ err << " http://user:password@127.0.0.1:5984\n"
327
+ raise Error, err
328
+ end
329
+ return uri
330
+ end
331
+ end
332
+ end
333
+ end
334
+
335
+
336
+
@@ -0,0 +1,197 @@
1
+ # http://www.mudynamics.com
2
+ # http://labs.mudynamics.com
3
+ # http://www.pcapr.net
4
+
5
+ require 'couchrest'
6
+
7
+ module PcaprLocal
8
+ class DB
9
+ def self.get_db config
10
+ database = config.fetch "database"
11
+ base = config.fetch "uri"
12
+
13
+ couch_url = File.join(base, database)
14
+ db = CouchRest.database! couch_url
15
+ patch_db db
16
+
17
+ begin
18
+ design = db.get("_design/pcaps")
19
+ rescue RestClient::ResourceNotFound
20
+ design = CouchRest::Design.new
21
+ design.name = "pcaps"
22
+ design.database = db
23
+ db.save_doc design
24
+ end
25
+
26
+ db.update_doc "_design/pcaps" do |design|
27
+ design['views'] = self.views
28
+ design
29
+ end
30
+
31
+ db
32
+ end
33
+
34
+ # Patch couch to include create and update times for each document.
35
+ def self.patch_db pcapr_local
36
+ def pcapr_local.update_doc id, &block
37
+ super(id) do |doc|
38
+ doc = block.call doc
39
+ doc['updated_at'] = Time.now
40
+ doc
41
+ end
42
+ end
43
+
44
+ def pcapr_local.save_doc *args
45
+ doc = args[0]
46
+ now = Time.now
47
+ doc["created_at"] ||= now
48
+ doc["updated_at"] ||= now
49
+ super
50
+ end
51
+
52
+ # Performs a query and yields each row to the the supplied block.
53
+ # Uses paging to split the query into page_size chunks.
54
+ def pcapr_local.each_in_view view, query=nil, page_size=50 #block
55
+ query ||= {}
56
+ limit = page_size + 1
57
+ query[:limit] = limit
58
+
59
+ begin
60
+ res = self.view(view, query)
61
+ rows = res['rows']
62
+ next_doc = rows.size == limit ? rows.pop : nil
63
+ if next_doc
64
+ query[:startkey] = next_doc['key']
65
+ query[:startkey_docid] = next_doc['id']
66
+ end
67
+
68
+ rows.each do |row|
69
+ yield row
70
+ end
71
+ end while next_doc
72
+ end
73
+
74
+ nil
75
+ end
76
+
77
+ def self.views
78
+ {
79
+ "by_created_at" => {
80
+ "map" => %q(function(doc) {
81
+ if (doc.type === 'pcap') {
82
+ emit(Date.parse(doc.created_at), null);
83
+ }
84
+ })
85
+ },
86
+ "by_inode" => {
87
+ "map" => %q(function(doc) {
88
+ if (doc.type === 'pcap') {
89
+ emit(doc.stat.inode, null);
90
+ }
91
+ })
92
+ },
93
+ "by_filename" => {
94
+ "map" => %q(function(doc) {
95
+ if (doc.type === 'pcap') {
96
+ emit(doc.filename, null);
97
+ }
98
+ })
99
+ },
100
+ "by_directory" => {
101
+ "map" => %q(function(doc) {
102
+ if (doc.type === 'pcap') {
103
+ var paths = doc.filename.split('/');
104
+ paths.unshift(paths.length-1);
105
+ paths.pop();
106
+ paths.push(Date.parse(doc.created_at));
107
+ emit(paths,1);
108
+ }
109
+ }),
110
+ "reduce" => '_sum'
111
+ },
112
+ "by_path" => {
113
+ "map" => %q(function(doc) {
114
+ if (doc.type === 'pcap') {
115
+ var paths = doc.filename.split('/');
116
+ paths.pop();
117
+ emit(paths,1);
118
+ }
119
+ }),
120
+ "reduce" => '_sum'
121
+ },
122
+ "by_service" => {
123
+ "map" => %q(function(doc) {
124
+ if (doc.type === 'pcap' && doc.index) {
125
+ var services = doc.index.services;
126
+ for (var i=0; i<services.length; ++i) {
127
+ emit([ services[i], Date.parse(doc.created_at) ], 1);
128
+ }
129
+ }
130
+ }),
131
+ "reduce" => '_sum'
132
+ },
133
+ "indexed" => {
134
+ "map" => %q(function(doc) {
135
+ if (doc.type === 'pcap') {
136
+ if (doc.status === 'indexed') {
137
+ emit(doc.filename, null);
138
+ }
139
+ }
140
+ })
141
+ },
142
+ "queued" => {
143
+ "map" => %q(function(doc) {
144
+ if (doc.type === 'pcap') {
145
+ if (doc.status === 'queued' || doc.status === 'indexing') {
146
+ emit(doc.filename, null);
147
+ }
148
+ }
149
+ })
150
+ },
151
+ "by_status" => {
152
+ "map" => %q(function(doc) {
153
+ if (doc.type === 'pcap') {
154
+ emit([ doc.status, Date.parse(doc.created_at) ], 1);
155
+ }
156
+ }),
157
+ "reduce" => '_sum'
158
+ },
159
+ "by_keyword" => {
160
+ "map" => %q(function(doc) {
161
+ if (doc.type === 'pcap') {
162
+ var keywords = {};
163
+ var paths = doc.filename.split('/');
164
+ for (var p in paths) {
165
+ var tokens = paths[p].toLowerCase().split(/[ -_\.\\\/\(\)]/);
166
+ for (var t in tokens) {
167
+ if (tokens[t].length > 2) {
168
+ keywords[tokens[t]] = true;
169
+ }
170
+ }
171
+ }
172
+
173
+ for (var k in keywords) {
174
+ emit([ k, Date.parse(doc.created_at) ], 1);
175
+ }
176
+ }
177
+ }),
178
+ "reduce" => '_sum'
179
+ },
180
+ "statistics" => {
181
+ "map" => %q(function(doc) {
182
+ if (doc.type === 'pcap') {
183
+ emit('pcaps', 1);
184
+ emit('bytes', doc.stat.size);
185
+ if (doc.index) {
186
+ emit('packets', doc.index.about.packets);
187
+ emit('flows', doc.index.about.flows);
188
+ emit('services', doc.index.about.services);
189
+ }
190
+ }
191
+ }),
192
+ "reduce" => '_sum'
193
+ }
194
+ }
195
+ end
196
+ end
197
+ end