hidden-hippo 0.1.0

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +47 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +16 -0
  5. data/Gemfile +3 -0
  6. data/HACKING.md +78 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +31 -0
  9. data/Rakefile +5 -0
  10. data/Vagrantfile +24 -0
  11. data/bin/hh +4 -0
  12. data/ci/install-tshark.sh +21 -0
  13. data/config/gui-dev.ru +6 -0
  14. data/config/mongoid.yml +20 -0
  15. data/gui/public/favicon.png +0 -0
  16. data/gui/public/hidden-hippo.css +18 -0
  17. data/gui/public/user-placeholder.png +0 -0
  18. data/gui/views/dossier.rhtml +12 -0
  19. data/gui/views/index.rhtml +25 -0
  20. data/gui/views/layout.rhtml +31 -0
  21. data/gui/views/possibilities.rhtml +10 -0
  22. data/hidden-hippo.gemspec +31 -0
  23. data/hippo-small.png +0 -0
  24. data/hippo.png +0 -0
  25. data/lib/hidden_hippo/cli/app.rb +23 -0
  26. data/lib/hidden_hippo/cli/database.rb +47 -0
  27. data/lib/hidden_hippo/cli/gui.rb +51 -0
  28. data/lib/hidden_hippo/daemon.rb +90 -0
  29. data/lib/hidden_hippo/dossier.rb +23 -0
  30. data/lib/hidden_hippo/extractors/dhcp_hostname_extractor.rb +16 -0
  31. data/lib/hidden_hippo/extractors/dns_history_extractor.rb +25 -0
  32. data/lib/hidden_hippo/extractors/dns_llmnr_extractor.rb +18 -0
  33. data/lib/hidden_hippo/extractors/http_request_url_extractor.rb +15 -0
  34. data/lib/hidden_hippo/extractors/mdns_hostname_extractor.rb +18 -0
  35. data/lib/hidden_hippo/gui.rb +21 -0
  36. data/lib/hidden_hippo/packets/dhcp.rb +13 -0
  37. data/lib/hidden_hippo/packets/dns.rb +23 -0
  38. data/lib/hidden_hippo/packets/http.rb +13 -0
  39. data/lib/hidden_hippo/packets/packet.rb +73 -0
  40. data/lib/hidden_hippo/paths.rb +15 -0
  41. data/lib/hidden_hippo/possibilities.rb +63 -0
  42. data/lib/hidden_hippo/reader.rb +36 -0
  43. data/lib/hidden_hippo/scanner.rb +51 -0
  44. data/lib/hidden_hippo/update.rb +3 -0
  45. data/lib/hidden_hippo/updator.rb +49 -0
  46. data/lib/hidden_hippo/version.rb +3 -0
  47. data/lib/hidden_hippo.rb +23 -0
  48. data/spec/db_daemon_spec.rb +7 -0
  49. data/spec/dns_scanner_spec.rb +41 -0
  50. data/spec/dossier_spec.rb +72 -0
  51. data/spec/extractors/dhcp_hostname_extractor_spec.rb +43 -0
  52. data/spec/extractors/dns_history_extractor_spec.rb +52 -0
  53. data/spec/extractors/dns_llmnr_extractor_spec.rb +45 -0
  54. data/spec/extractors/http_request_url_extractor_spec.rb +23 -0
  55. data/spec/extractors/mdns_hostname_extractor_spec.rb +45 -0
  56. data/spec/fixtures/dns_elise.pcap +0 -0
  57. data/spec/fixtures/dns_reddit_eth.pcap +0 -0
  58. data/spec/fixtures/tcp_noise.pcap +0 -0
  59. data/spec/gui_daemon_spec.rb +7 -0
  60. data/spec/hidden_hippo_spec.rb +32 -0
  61. data/spec/packet_spec.rb +88 -0
  62. data/spec/possibilities_spec.rb +113 -0
  63. data/spec/spec_helper.rb +33 -0
  64. data/spec/support/cli_controller_examples.rb +136 -0
  65. data/spec/updator_spec.rb +37 -0
  66. metadata +274 -0
@@ -0,0 +1,23 @@
1
+ require 'mongoid'
2
+ require 'hidden_hippo/possibilities'
3
+
4
+ module HiddenHippo
5
+ # Holds all the information for a specific mac address
6
+ class Dossier
7
+ include Mongoid::Document
8
+
9
+ field :_id, type: String, default: ->{ mac_address }
10
+
11
+ field :mac_address, type: String
12
+ validates_presence_of :mac_address
13
+
14
+ field :name, type: Possibilities, default: ->{ Possibilities.new }
15
+ field :hostname, type: Possibilities, default: ->{ Possibilities.new }
16
+ field :username, type: Possibilities, default: ->{ Possibilities.new }
17
+ field :email, type: Possibilities, default: ->{ Possibilities.new }
18
+ field :device, type: Possibilities, default: ->{ Possibilities.new }
19
+ field :gender, type: Possibilities, default: ->{ Possibilities.new }
20
+ field :age, type: Possibilities, default: ->{ Possibilities.new }
21
+ field :history, type: Possibilities, default: ->{ Possibilities.new }
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'hidden_hippo/update'
2
+
3
+ module HiddenHippo
4
+ module Extractors
5
+ class DhcpHostnameExtractor
6
+ def initialize(queue)
7
+ @queue = queue
8
+ end
9
+
10
+ def call(packet)
11
+ @queue << Update.new(packet.mac_src,
12
+ {hostname: packet.fqdn_name || packet.opt_hostname})
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ require 'hidden_hippo/update'
2
+
3
+ module HiddenHippo
4
+ module Extractors
5
+ class DnsHistoryExtractor
6
+ def initialize(queue)
7
+ @queue = queue
8
+ end
9
+
10
+ def call(packet)
11
+ return unless packet.udp_dest_port == 53
12
+ return if packet.dns_qry_name.delete(" ") == ""
13
+
14
+ if packet.request?
15
+ @queue << Update.new(packet.mac_src, {history: packet.dns_qry_name})
16
+ end
17
+
18
+ if packet.response?
19
+ @queue << Update.new(packet.mac_dest, {history: packet.dns_qry_name})
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ require 'hidden_hippo/update'
2
+
3
+ module HiddenHippo
4
+ module Extractors
5
+ class DnsLlmnrExtractor
6
+ def initialize(queue)
7
+ @queue = queue
8
+ end
9
+
10
+ def call(packet)
11
+ return unless packet.udp_dest_port == 5355
12
+ return unless packet.request?
13
+
14
+ @queue << Update.new(packet.mac_src, {hostname: packet.dns_qry_name})
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'hidden_hippo/update'
2
+
3
+ module HiddenHippo
4
+ module Extractors
5
+ class HttpRequestUrlExtractor
6
+ def initialize(queue)
7
+ @queue = queue
8
+ end
9
+
10
+ def call(packet)
11
+ @queue << Update.new(packet.mac_src, {history: packet.full_uri})
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ require 'hidden_hippo/update'
2
+
3
+ module HiddenHippo
4
+ module Extractors
5
+ class MdnsHostnameExtractor
6
+ def initialize(queue)
7
+ @queue = queue
8
+ end
9
+
10
+ def call(packet)
11
+ return unless packet.udp_dest_port == 5353
12
+ return unless packet.response?
13
+
14
+ @queue << Update.new(packet.mac_src, {hostname: packet.ptr})
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ require 'sinatra/base'
2
+ require 'hidden_hippo'
3
+ require 'hidden_hippo/dossier'
4
+
5
+ module HiddenHippo
6
+ class Gui < Sinatra::Application
7
+ set :root, proc { (HiddenHippo.gem_root + 'gui').to_s }
8
+
9
+ get '/' do
10
+ dossiers = Dossier.all
11
+
12
+ erb :index, locals: {dossiers: dossiers}
13
+ end
14
+
15
+ get '/:mac' do
16
+ dossier = Dossier.find params[:mac]
17
+
18
+ erb :dossier, locals: {dossier: dossier}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ require 'hidden_hippo/packets/packet'
2
+
3
+ module HiddenHippo
4
+ module Packets
5
+ class Dhcp < Packet
6
+ filter 'bootp'
7
+
8
+ field :fqdn_name, tshark: 'bootp.fqdn.name'
9
+ field :opt_hostname, tshark: 'bootp.option.hostname'
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ require 'hidden_hippo/packets/packet'
2
+
3
+ module HiddenHippo
4
+ module Packets
5
+ class Dns < Packet
6
+ filter 'dns'
7
+
8
+ field :a, tshark: 'dns.a'
9
+ field :ptr, tshark: 'dns.ptr.domain_name'
10
+ field :dns_qry_name, tshark: 'dns.qry.name'
11
+
12
+ field :response, tshark: 'dns.flags.response', conv: ->(f) {f.to_i == 1}
13
+
14
+ def response?
15
+ response
16
+ end
17
+
18
+ def request?
19
+ !response?
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ require 'hidden_hippo/packets/packet'
2
+
3
+ module HiddenHippo
4
+ module Packets
5
+ class Http < Packet
6
+ filter 'http'
7
+
8
+ field :full_uri, tshark: 'http.request.full_uri'
9
+ field :referer, tshark: 'http.referer'
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,73 @@
1
+ module HiddenHippo
2
+ module Packets
3
+ Field = Struct.new :name, :tshark_field, :converter
4
+
5
+ class Packet
6
+ def self.fields
7
+ unless @fields
8
+ super_fields = self == Packet ? [] : superclass.fields.dup
9
+ @fields = super_fields
10
+ end
11
+ @fields
12
+ end
13
+
14
+ def self.tshark_fields
15
+ fields.map &:tshark_field
16
+ end
17
+
18
+ def self.filter(filter=nil)
19
+ @filter = filter if filter
20
+ @filter
21
+ end
22
+
23
+ def self.field(name, options={})
24
+ fields << Field.new(name, options[:tshark], options[:conv])
25
+ attr_accessor name
26
+ name
27
+ end
28
+
29
+ def self.parse(hash)
30
+ instance = new
31
+
32
+ reverse_names = Hash[*fields.map{|f| [f.tshark_field, f]}.flatten]
33
+
34
+ hash.each do |k, v|
35
+ field = reverse_names[k]
36
+
37
+ next unless field
38
+
39
+ conv = converter field
40
+ instance.public_send "#{field.name}=", conv.(v)
41
+ end
42
+
43
+ instance
44
+ end
45
+
46
+ # default fields
47
+ field :eth_mac_src, tshark: 'eth.src'
48
+ field :eth_mac_dest, tshark: 'eth.dst'
49
+ field :wlan_mac_src, tshark: 'wlan.sa'
50
+ field :wlan_mac_dest, tshark: 'wlan.da'
51
+
52
+ field :udp_src_port, tshark: 'udp.srcport', conv: :to_i
53
+ field :tcp_src_port, tshark: 'tcp.srcport', conv: :to_i
54
+ field :udp_dest_port, tshark: 'udp.dstport', conv: :to_i
55
+ field :tcp_dest_port, tshark: 'tcp.dstport', conv: :to_i
56
+
57
+ def mac_src
58
+ eth_mac_src || wlan_mac_src
59
+ end
60
+
61
+ def mac_dest
62
+ eth_mac_dest || wlan_mac_dest
63
+ end
64
+
65
+ private
66
+
67
+ def self.converter(field)
68
+ conv = field.converter || ->(x){x}
69
+ conv.is_a?(Symbol) ? conv.to_proc : conv
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,15 @@
1
+ require 'pathname'
2
+
3
+ module HiddenHippo
4
+ module Paths
5
+ def home
6
+ @home ||= if ENV['HIDDEN_HIPPO_HOME']
7
+ Pathname.new(ENV['HIDDEN_HIPPO_HOME'])
8
+ else
9
+ Pathname.new(ENV['HOME']) + '.hidden-hippo'
10
+ end
11
+ @home.mkpath
12
+ @home
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ module HiddenHippo
2
+ # Represents multiple possible values
3
+ #
4
+ # Each possible value has a support count. Adding possible values is done with #<<.
5
+ # Retrieving the support count for a possible value is done with #[].
6
+ class Possibilities
7
+ include Enumerable
8
+
9
+ class << self
10
+ def demongoize(doc)
11
+ HiddenHippo::Possibilities.new doc
12
+ end
13
+
14
+ def mongoize(value)
15
+ case value
16
+ when Possibilities then value.mongoize
17
+ else value
18
+ end
19
+ end
20
+
21
+ def evolve(value)
22
+ case value
23
+ when Possibilities then value.mongoize
24
+ else value
25
+ end
26
+ end
27
+
28
+ def resizable?
29
+ true
30
+ end
31
+ end
32
+
33
+ def initialize(counts={})
34
+ @counts = counts
35
+ @counts.keep_if {|_, c| c.kind_of?(Integer)}
36
+ @counts.default_proc = proc {0}
37
+ end
38
+
39
+ def [](value)
40
+ @counts[value]
41
+ end
42
+
43
+ def <<(value)
44
+ @counts[value.to_s.gsub(/[.$]/, '_')] += 1
45
+ end
46
+
47
+ def each(&block)
48
+ @counts.sort_by{|c| - c.last}.map(&:first).each &block
49
+ end
50
+
51
+ def each_with_support(&block)
52
+ @counts.sort_by{|c| - c.last}.each &block
53
+ end
54
+
55
+ def mongoize
56
+ @counts
57
+ end
58
+
59
+ def resizable?
60
+ Possibilities.resizable?
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ require 'hidden_hippo/scanner'
2
+ require 'hidden_hippo/updator'
3
+
4
+ require 'hidden_hippo/packets/dns'
5
+ require 'hidden_hippo/packets/dhcp'
6
+ require 'hidden_hippo/packets/http'
7
+
8
+ require 'hidden_hippo/extractors/mdns_hostname_extractor'
9
+ require 'hidden_hippo/extractors/dhcp_hostname_extractor'
10
+ require 'hidden_hippo/extractors/http_request_url_extractor'
11
+ require 'hidden_hippo/extractors/dns_llmnr_extractor'
12
+ require 'thread'
13
+
14
+ module HiddenHippo
15
+ class Reader
16
+ def initialize(file)
17
+ @file = file
18
+ updator_queue = Queue.new
19
+ @updator = Updator.new updator_queue
20
+ @scanners = []
21
+ @scanners << Scanner.new(file, Packets::Dns,
22
+ Extractors::MdnsHostnameExtractor.new(updator_queue),
23
+ Extractors::DnsLlmnrExtractor.new(updator_queue))
24
+ @scanners << Scanner.new(file, Packets::Dhcp,
25
+ Extractors::DhcpHostnameExtractor.new(updator_queue))
26
+ @scanners << Scanner.new(file, Packets::Http,
27
+ Extractors::HttpRequestUrlExtractor.new(updator_queue))
28
+ end
29
+
30
+ def call
31
+ @updator.start
32
+ @scanners.each &:call
33
+ @updator.stop
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,51 @@
1
+ require 'open3'
2
+
3
+ module HiddenHippo
4
+ class Scanner
5
+ def initialize(file, packet_class, *extractors)
6
+ @file = file
7
+ @extractors = extractors
8
+ @packet_class = packet_class
9
+ end
10
+
11
+ def call
12
+ # call Tshark
13
+ tshark_fields = @packet_class.tshark_fields
14
+
15
+ args = [
16
+ '-2', '-Tfields', '-q',
17
+ '-r', @file,
18
+ '-R', @packet_class.filter,
19
+ *tshark_fields.map {|f| ['-e', f]}.flatten
20
+ ]
21
+
22
+ Open3.popen3(%w(tshark tshark), *args) do |stdin, stdout, stderr, waiter|
23
+ # we don't need those
24
+ stdin.close
25
+
26
+ stdout.each do |line|
27
+ if line.count("\t") != tshark_fields.size - 1
28
+ puts 'Warinig: tshark returned a line of the wrong size. Ignoring it.'
29
+ puts "Offending line: #{line}"
30
+ next
31
+ end
32
+
33
+ split_line = line.chomp.split("\t").map {|f| f.empty? ? nil : f}
34
+
35
+ assoc = tshark_fields.zip split_line
36
+ packet = @packet_class.parse Hash[*assoc.flatten]
37
+
38
+ @extractors.each do |extractor|
39
+ extractor.call(packet)
40
+ end
41
+ end
42
+
43
+ if waiter.value != 0
44
+ puts "Warning: tshark exited with status code #{waiter.value}."
45
+ puts "tshark #{args.join(' ')}"
46
+ puts stderr.read
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module HiddenHippo
2
+ Update = Struct.new :mac_address, :fields
3
+ end
@@ -0,0 +1,49 @@
1
+ require 'thread'
2
+ require 'hidden_hippo/dossier'
3
+
4
+ module HiddenHippo
5
+ class Updator
6
+ def initialize(queue)
7
+ @queue = queue
8
+ end
9
+
10
+ def start
11
+ return if @thread
12
+ @thread = Thread.new do
13
+ loop do
14
+ update = @queue.pop
15
+
16
+ break if update == :stop
17
+
18
+ dossier = dossier update.mac_address
19
+
20
+ update.fields.each do |key, value|
21
+ dossier.public_send(key) << value
22
+ end
23
+
24
+ begin
25
+ dossier.save
26
+ rescue => e
27
+ puts 'shit'
28
+ p e
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def stop
35
+ @queue << :stop
36
+ @thread.join
37
+ end
38
+
39
+ private
40
+
41
+ def dossier(mac_address)
42
+ if Dossier.where(mac_address: mac_address).exists?
43
+ Dossier.find mac_address
44
+ else
45
+ Dossier.new mac_address: mac_address
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module HiddenHippo
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,23 @@
1
+ require 'pathname'
2
+ require 'hidden_hippo/version'
3
+ require 'hidden_hippo/cli/app'
4
+
5
+ module HiddenHippo
6
+ def pid_exists?(pid)
7
+ Process.kill 0, pid
8
+ return true
9
+ rescue
10
+ return false
11
+ end
12
+ module_function :pid_exists?
13
+
14
+ def configure_db!(env = :production)
15
+ Mongoid.load! gem_root + 'config/mongoid.yml', env
16
+ end
17
+ module_function :configure_db!
18
+
19
+ def gem_root
20
+ Pathname.new(__FILE__) + '../../'
21
+ end
22
+ module_function :gem_root
23
+ end
@@ -0,0 +1,7 @@
1
+ require 'support/cli_controller_examples'
2
+
3
+ describe 'hh db', :noisy do
4
+ let(:name) {'db'}
5
+
6
+ it_behaves_like 'cli daemon controller'
7
+ end
@@ -0,0 +1,41 @@
1
+ require 'hidden_hippo/scanner'
2
+ require 'hidden_hippo/packets/dns'
3
+
4
+ describe 'HiddenHippo::Scanner(Dns)' do
5
+
6
+ describe '#call' do
7
+ it 'should parse the pcap file' do
8
+ extractor = double('extractor')
9
+ scanner = HiddenHippo::Scanner.new('spec/fixtures/dns_elise.pcap', HiddenHippo::Packets::Dns, extractor)
10
+
11
+ expect(extractor).to receive(:call) do |packet|
12
+ expect(packet.ptr).to eq 'Elises-MacBook-Pro.local'
13
+ expect(packet.mac_src).to eq 'a8:bb:cf:09:9b:bc'
14
+ expect(packet.mac_dest).to eq '01:00:5e:00:00:fb'
15
+ end
16
+
17
+ scanner.call
18
+ end
19
+
20
+ it 'should skip non dns lines' do
21
+ extractor = double('extractor')
22
+ scanner = HiddenHippo::Scanner.new 'spec/fixtures/tcp_noise.pcap', HiddenHippo::Packets::Dns, extractor
23
+
24
+ expect(extractor).not_to receive(:call)
25
+
26
+ scanner.call
27
+ end
28
+
29
+ it 'should find the mac addresses in eth traffic' do
30
+ extractor = double('extractor')
31
+ scanner = HiddenHippo::Scanner.new('spec/fixtures/dns_reddit_eth.pcap', HiddenHippo::Packets::Dns, extractor)
32
+
33
+ expect(extractor).to receive(:call).at_least(:once) do |packet|
34
+ expect(packet.mac_src).not_to be_nil
35
+ expect(packet.mac_dest).not_to be_nil
36
+ end
37
+
38
+ scanner.call
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,72 @@
1
+ require 'hidden_hippo/dossier'
2
+
3
+ describe HiddenHippo::Dossier, :db do
4
+ before do
5
+ HiddenHippo::Dossier.delete_all
6
+ end
7
+
8
+ it 'should require a mac address' do
9
+ expect(HiddenHippo::Dossier.new).to validate_presence_of :mac_address
10
+ end
11
+
12
+ it 'should use the mac address as an id' do
13
+ dossier = HiddenHippo::Dossier.new mac_address: '00:aa:bb:cc:dd:ee:ff'
14
+ dossier.save!
15
+
16
+ expect(HiddenHippo::Dossier.find('00:aa:bb:cc:dd:ee:ff')).to eq dossier
17
+ end
18
+
19
+ it 'should allow possibilities to be modified in place' do
20
+ dossier = HiddenHippo::Dossier.new mac_address: 'aa:bb:cc:dd:ee:ff:aa'
21
+
22
+ dossier.name << 'Steve'
23
+
24
+ expect(dossier.name).to contain_exactly 'Steve'
25
+ end
26
+
27
+ it 'should serialize properly' do
28
+ dossier = HiddenHippo::Dossier.new(mac_address: '11:00:11:00:11:00:11')
29
+
30
+ dossier.name << 'John'
31
+ dossier.name << 'Billy'
32
+ dossier.name << 'Billy'
33
+ dossier.name << 'Steve'
34
+
35
+ dossier.username << 'billydakid89'
36
+ dossier.username << 'billydakid89'
37
+ dossier.username << 'billydakid89'
38
+
39
+ dossier.age << 40
40
+ dossier.age << 41
41
+ dossier.age << 41
42
+ dossier.age << 42
43
+
44
+ dossier.save!
45
+
46
+ found = HiddenHippo::Dossier.find '11:00:11:00:11:00:11'
47
+ expect(dossier).to eq found
48
+ expect(found.name).to contain_exactly 'John', 'Billy', 'Steve'
49
+ expect(found.username).to contain_exactly 'billydakid89'
50
+ expect(found.age).to contain_exactly '40', '41', '42'
51
+ end
52
+
53
+ it 'should deserialize support counts as ints' do
54
+ dossier = HiddenHippo::Dossier.new(mac_address: '11:00:11:00:11:00:11')
55
+
56
+ dossier.name << 'Whoopie'
57
+
58
+ dossier.save!
59
+
60
+ found = HiddenHippo::Dossier.find '11:00:11:00:11:00:11'
61
+ expect(found.name['Whoopie']).to eq 1
62
+ end
63
+
64
+ it 'should update possibilities' do
65
+ HiddenHippo::Dossier.create mac_address: 'find me'
66
+ dossier = HiddenHippo::Dossier.find 'find me'
67
+ dossier.name << 'thing'
68
+ dossier.save
69
+
70
+ expect(HiddenHippo::Dossier.find('find me').name['thing']).to eq 1
71
+ end
72
+ end
@@ -0,0 +1,43 @@
1
+ require 'thread'
2
+ require 'hidden_hippo/extractors/dhcp_hostname_extractor'
3
+
4
+ describe HiddenHippo::Extractors::DhcpHostnameExtractor do
5
+ let(:queue) {Queue.new}
6
+ let(:extractor) {HiddenHippo::Extractors::DhcpHostnameExtractor.new queue}
7
+
8
+ describe '#call' do
9
+ it 'should not have a fqdn_name' do
10
+ packet = HiddenHippo::Packets::Dhcp.new
11
+ packet.eth_mac_src = 'dhcp some mac'
12
+ packet.fqdn_name = nil
13
+ packet.opt_hostname = 'hidden_hippo_super_mac'
14
+
15
+ extractor.call packet
16
+ out = queue.pop true
17
+ expect(out.mac_address).to eq 'dhcp some mac'
18
+ expect(out.fields).to eq ({hostname: 'hidden_hippo_super_mac'})
19
+ end
20
+ it 'should use a fqdn_name' do
21
+ packet = HiddenHippo::Packets::Dhcp.new
22
+ packet.eth_mac_src = 'dhcp some mac'
23
+ packet.fqdn_name = 'hidden_hippo_fqdn'
24
+ packet.opt_hostname = 'hidden_hippo_super_mac'
25
+
26
+ extractor.call packet
27
+ out = queue.pop true
28
+ expect(out.mac_address).to eq 'dhcp some mac'
29
+ expect(out.fields).to eq ({hostname: 'hidden_hippo_fqdn'})
30
+ end
31
+ it 'should not have a hostname' do
32
+ packet = HiddenHippo::Packets::Dhcp.new
33
+ packet.eth_mac_src = 'dhcp some mac'
34
+ packet.fqdn_name = 'hidden_hippo_super_mac'
35
+ packet.opt_hostname = nil
36
+
37
+ extractor.call packet
38
+ out = queue.pop true
39
+ expect(out.mac_address).to eq 'dhcp some mac'
40
+ expect(out.fields).to eq ({hostname: 'hidden_hippo_super_mac'})
41
+ end
42
+ end
43
+ end