pcapr-local 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/LICENSE.txt +20 -0
- data/README.md +64 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/bin/pcap2par +49 -0
- data/bin/startpcapr +40 -0
- data/bin/stoppcapr +33 -0
- data/bin/xtractr +5 -0
- data/lib/environment.rb +106 -0
- data/lib/exe/xtractr +0 -0
- data/lib/mu/pcap.rb +110 -0
- data/lib/mu/pcap/ethernet.rb +148 -0
- data/lib/mu/pcap/header.rb +75 -0
- data/lib/mu/pcap/io_pair.rb +67 -0
- data/lib/mu/pcap/io_wrapper.rb +76 -0
- data/lib/mu/pcap/ip.rb +61 -0
- data/lib/mu/pcap/ipv4.rb +257 -0
- data/lib/mu/pcap/ipv6.rb +148 -0
- data/lib/mu/pcap/packet.rb +104 -0
- data/lib/mu/pcap/pkthdr.rb +155 -0
- data/lib/mu/pcap/reader.rb +61 -0
- data/lib/mu/pcap/reader/http_family.rb +170 -0
- data/lib/mu/pcap/sctp.rb +367 -0
- data/lib/mu/pcap/sctp/chunk.rb +123 -0
- data/lib/mu/pcap/sctp/chunk/data.rb +134 -0
- data/lib/mu/pcap/sctp/chunk/init.rb +100 -0
- data/lib/mu/pcap/sctp/chunk/init_ack.rb +68 -0
- data/lib/mu/pcap/sctp/parameter.rb +110 -0
- data/lib/mu/pcap/sctp/parameter/ip_address.rb +48 -0
- data/lib/mu/pcap/stream_packetizer.rb +72 -0
- data/lib/mu/pcap/tcp.rb +505 -0
- data/lib/mu/pcap/udp.rb +69 -0
- data/lib/mu/scenario/pcap.rb +164 -0
- data/lib/mu/scenario/pcap/fields.rb +50 -0
- data/lib/mu/scenario/pcap/rtp.rb +71 -0
- data/lib/pcapr_local.rb +159 -0
- data/lib/pcapr_local/config.rb +336 -0
- data/lib/pcapr_local/db.rb +197 -0
- data/lib/pcapr_local/scanner.rb +250 -0
- data/lib/pcapr_local/server.rb +178 -0
- data/lib/pcapr_local/www/favicon.ico +0 -0
- data/lib/pcapr_local/www/favicon.png +0 -0
- data/lib/pcapr_local/www/home/index.html +138 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png.1 +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Download.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Folder3.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Full Size.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Minus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Plus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Search.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/User.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Phone.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Video.png +0 -0
- data/lib/pcapr_local/www/static/image/bar-orange.gif +0 -0
- data/lib/pcapr_local/www/static/image/beta.png +0 -0
- data/lib/pcapr_local/www/static/image/bg.png +0 -0
- data/lib/pcapr_local/www/static/image/blockquote.png +0 -0
- data/lib/pcapr_local/www/static/image/body-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl5-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl6-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl7-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl8-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-l.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-r.png +0 -0
- data/lib/pcapr_local/www/static/image/btn-search.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-5.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-6.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-7.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-pathway.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section2.png +0 -0
- data/lib/pcapr_local/www/static/image/collapsed.gif +0 -0
- data/lib/pcapr_local/www/static/image/crosslink.png +0 -0
- data/lib/pcapr_local/www/static/image/expanded.gif +0 -0
- data/lib/pcapr_local/www/static/image/favicon.ico +0 -0
- data/lib/pcapr_local/www/static/image/favicon.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-author.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-created.png +0 -0
- data/lib/pcapr_local/www/static/image/p-expand.gif +0 -0
- data/lib/pcapr_local/www/static/image/pcapr-logo.png +0 -0
- data/lib/pcapr_local/www/static/image/powered-by.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/status-alert.png +0 -0
- data/lib/pcapr_local/www/static/image/status-download.png +0 -0
- data/lib/pcapr_local/www/static/image/status-info.png +0 -0
- data/lib/pcapr_local/www/static/image/status-note.png +0 -0
- data/lib/pcapr_local/www/static/image/tab-round.png +0 -0
- data/lib/pcapr_local/www/static/image/throbber.gif +0 -0
- data/lib/pcapr_local/www/static/image/user.jpg +0 -0
- data/lib/pcapr_local/www/static/script/closet/async.js +421 -0
- data/lib/pcapr_local/www/static/script/closet/closet.api.js +241 -0
- data/lib/pcapr_local/www/static/script/closet/closet.folders.js +94 -0
- data/lib/pcapr_local/www/static/script/closet/closet.js +187 -0
- data/lib/pcapr_local/www/static/script/closet/closet.mr.js +219 -0
- data/lib/pcapr_local/www/static/script/closet/closet.options.js +359 -0
- data/lib/pcapr_local/www/static/script/closet/closet.quantity.js +73 -0
- data/lib/pcapr_local/www/static/script/closet/closet.render.js +205 -0
- data/lib/pcapr_local/www/static/script/closet/closet.report.js +86 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.http.js +135 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.overview.js +163 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.sip.js +159 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.tcp.js +72 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.visualize.js +263 -0
- data/lib/pcapr_local/www/static/script/closet/closet.util.js +40 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-1.4.2.min.js +154 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-ui.js +10921 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.js +2123 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.selection.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.stack.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.form.js +643 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.jsonp.min.js +3 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.menu.js +142 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.suggest.js +308 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.core.js +203 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.slider.js +629 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.sortable.js +1055 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.widget.js +236 -0
- data/lib/pcapr_local/www/static/script/json2.js +481 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.cache.js +115 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.template.js +117 -0
- data/lib/pcapr_local/www/static/script/sammy/sammy.js +1696 -0
- data/lib/pcapr_local/www/static/script/tipsy/jquery.tipsy.js +104 -0
- data/lib/pcapr_local/www/static/style/c3p0.css +116 -0
- data/lib/pcapr_local/www/static/style/jquery.suggest.css +27 -0
- data/lib/pcapr_local/www/static/style/page.css +1113 -0
- data/lib/pcapr_local/www/static/style/tipsy.css +7 -0
- data/lib/pcapr_local/www/templates/browse.services.template +10 -0
- data/lib/pcapr_local/www/templates/browse.template +77 -0
- data/lib/pcapr_local/www/templates/flows.template +38 -0
- data/lib/pcapr_local/www/templates/pcap.template +63 -0
- data/lib/pcapr_local/www/templates/sip.calls.template +35 -0
- data/lib/pcapr_local/www/templates/statistics.template +6 -0
- data/lib/pcapr_local/xtractr.rb +179 -0
- data/lib/pcapr_local/xtractr/instance.rb +172 -0
- data/pcapr-local.gemspec +297 -0
- data/test/mu/pcap/reader/tc_http_family.rb +251 -0
- data/test/mu/pcap/tc_ethernet.rb +71 -0
- data/test/mu/pcap/tc_header.rb +56 -0
- data/test/mu/pcap/tc_ipv4.rb +103 -0
- data/test/mu/pcap/tc_ipv6.rb +83 -0
- data/test/mu/pcap/tc_packet.rb +44 -0
- data/test/mu/pcap/tc_pair.rb +58 -0
- data/test/mu/pcap/tc_pkthdr.rb +33 -0
- data/test/mu/pcap/tc_reader.rb +76 -0
- data/test/mu/pcap/tc_tcp.rb +426 -0
- data/test/mu/pcap/tc_udp.rb +33 -0
- data/test/mu/pcap/tc_wrapper.rb +80 -0
- data/test/mu/scenario/pcap/tc_fields.rb +67 -0
- data/test/mu/scenario/pcap/tc_rtp.rb +135 -0
- data/test/mu/scenario/sip_signalled_call_1.pcap +0 -0
- data/test/mu/scenario/tc_pcap.rb +190 -0
- data/test/mu/scenario/test_data/arp.pcap +0 -0
- data/test/mu/scenario/test_data/dns.pcap +0 -0
- data/test/mu/scenario/test_data/http-v6.pcap +0 -0
- data/test/mu/scenario/test_data/http.pcap +0 -0
- data/test/mu/scenario/test_data/http_chunked.pcap +0 -0
- data/test/mu/scenario/test_data/http_deflate.pcap +0 -0
- data/test/mu/scenario/test_data/httpauth3.pcap +0 -0
- data/test/mu/scenario/test_data/icmp.pcap +0 -0
- data/test/mu/scenario/test_data/sip_signalled_call_1.pcap +0 -0
- data/test/mu/tc_pcap.rb +39 -0
- data/test/mu/testcase.rb +86 -0
- data/test/pcapr_local/arp.pcap +0 -0
- data/test/pcapr_local/data.js +3 -0
- data/test/pcapr_local/http_chunked.pcap +0 -0
- data/test/pcapr_local/tc_api.rb +181 -0
- data/test/pcapr_local/test.tgz +0 -0
- data/test/pcapr_local/test_scanner.rb +241 -0
- data/test/pcapr_local/test_xtractr.rb +219 -0
- data/test/pcapr_local/testcase.rb +107 -0
- data/test/test_export_to_scenario.sh +25 -0
- data/test/test_pcapr_local.rb +29 -0
- metadata +450 -0
@@ -0,0 +1,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
|