bettercap 1.1.9 → 1.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +2 -2
  3. data/TODO.md +5 -2
  4. data/bin/bettercap +19 -11
  5. data/lib/bettercap.rb +5 -1
  6. data/lib/bettercap/base/ifirewall.rb +2 -0
  7. data/lib/bettercap/base/ispoofer.rb +2 -0
  8. data/lib/bettercap/context.rb +5 -2
  9. data/lib/bettercap/discovery/{arp.rb → agents/arp.rb} +15 -14
  10. data/lib/bettercap/discovery/{base.rb → agents/base.rb} +4 -4
  11. data/lib/bettercap/discovery/{icmp.rb → agents/icmp.rb} +2 -3
  12. data/lib/bettercap/discovery/{udp.rb → agents/udp.rb} +2 -2
  13. data/lib/bettercap/{discovery.rb → discovery/discovery.rb} +8 -1
  14. data/lib/bettercap/factories/firewall_factory.rb +2 -0
  15. data/lib/bettercap/factories/parser_factory.rb +11 -5
  16. data/lib/bettercap/factories/spoofer_factory.rb +3 -1
  17. data/lib/bettercap/firewalls/linux.rb +2 -0
  18. data/lib/bettercap/firewalls/osx.rb +2 -0
  19. data/lib/bettercap/firewalls/redirection.rb +2 -1
  20. data/lib/bettercap/httpd/server.rb +2 -3
  21. data/lib/bettercap/logger.rb +27 -10
  22. data/lib/bettercap/monkey/packetfu/utils.rb +5 -5
  23. data/lib/bettercap/network.rb +26 -16
  24. data/lib/bettercap/options.rb +27 -6
  25. data/lib/bettercap/proxy/certstore.rb +4 -3
  26. data/lib/bettercap/proxy/module.rb +2 -2
  27. data/lib/bettercap/proxy/proxy.rb +5 -5
  28. data/lib/bettercap/proxy/request.rb +2 -2
  29. data/lib/bettercap/proxy/response.rb +2 -0
  30. data/lib/bettercap/proxy/stream_logger.rb +15 -3
  31. data/lib/bettercap/proxy/streamer.rb +3 -1
  32. data/lib/bettercap/proxy/thread_pool.rb +4 -2
  33. data/lib/bettercap/shell.rb +2 -0
  34. data/lib/bettercap/sniffer/parsers/base.rb +3 -12
  35. data/lib/bettercap/sniffer/parsers/custom.rb +21 -0
  36. data/lib/bettercap/sniffer/parsers/ftp.rb +2 -0
  37. data/lib/bettercap/sniffer/parsers/httpauth.rb +4 -5
  38. data/lib/bettercap/sniffer/parsers/https.rb +3 -4
  39. data/lib/bettercap/sniffer/parsers/irc.rb +2 -0
  40. data/lib/bettercap/sniffer/parsers/mail.rb +2 -0
  41. data/lib/bettercap/sniffer/parsers/ntlmss.rb +3 -3
  42. data/lib/bettercap/sniffer/parsers/post.rb +7 -7
  43. data/lib/bettercap/sniffer/parsers/url.rb +11 -11
  44. data/lib/bettercap/sniffer/sniffer.rb +8 -2
  45. data/lib/bettercap/spoofers/arp.rb +15 -5
  46. data/lib/bettercap/spoofers/none.rb +2 -0
  47. data/lib/bettercap/target.rb +29 -10
  48. data/lib/bettercap/update_checker.rb +2 -0
  49. data/lib/bettercap/version.rb +1 -1
  50. metadata +8 -40
  51. data/Rakefile +0 -7
  52. data/test/factories/firewall_factory_test.rb +0 -54
  53. data/test/factories/parser_factory_test.rb +0 -36
  54. data/test/factories/spoofer_factory_test.rb +0 -15
  55. data/test/firewalls/linux_firewall_test.rb +0 -72
  56. data/test/firewalls/osx_firewall_test.rb +0 -72
  57. data/test/helpers/mock_shell.rb +0 -17
  58. data/test/logger_test.rb +0 -12
  59. data/test/network_test.rb +0 -14
  60. data/test/pcap/ftp.pcap +0 -0
  61. data/test/pcap/http.pcap +0 -0
  62. data/test/pcap/packets.pcap +0 -0
  63. data/test/proxy/response_test.rb +0 -56
  64. data/test/shell_test.rb +0 -15
  65. data/test/sniffer/parsers/base_parser_test.rb +0 -20
  66. data/test/sniffer/parsers/ftp_parser_test.rb +0 -27
  67. data/test/sniffer/parsers/url_parser_test.rb +0 -25
  68. data/test/target_test.rb +0 -24
  69. data/test/test_helper.rb +0 -47
@@ -11,7 +11,7 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  require 'bettercap/logger'
13
13
 
14
- module Proxy
14
+ module BetterCap
15
15
  class StreamLogger
16
16
  @@MAX_REQ_SIZE = 50
17
17
 
@@ -23,12 +23,24 @@ class StreamLogger
23
23
  }
24
24
 
25
25
  def self.addr2s( addr )
26
- target = Context.get.find_target addr, nil
26
+ ctx = Context.get
27
+
28
+ return 'local' if addr == ctx.ifconfig[:ip_saddr]
29
+
30
+ target = ctx.find_target addr, nil
27
31
  return target.to_s_compact unless target.nil?
32
+
28
33
  addr
29
34
  end
30
35
 
31
- def self.log( is_https, client, request, response )
36
+ def self.log_raw( pkt, label, payload )
37
+ nl = if label.include?"\n" then "\n" else " " end
38
+ label = label.strip
39
+ Logger.raw( "[#{self.addr2s(pkt.ip_saddr)} > #{self.addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst}] " \
40
+ "[#{label.green}]#{nl}#{payload.strip.yellow}" )
41
+ end
42
+
43
+ def self.log_http( is_https, client, request, response )
32
44
  request_s = "#{is_https ? 'https' : 'http'}://#{request.host}#{request.url}"
33
45
  response_s = "( #{response.content_type} )"
34
46
  request_s = request_s.slice(0..@@MAX_REQ_SIZE) + '...' unless request_s.length <= @@MAX_REQ_SIZE
@@ -11,6 +11,7 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  require 'bettercap/logger'
13
13
 
14
+ module BetterCap
14
15
  module Proxy
15
16
  class Streamer
16
17
  def initialize( processor )
@@ -51,7 +52,7 @@ class Streamer
51
52
  rescue Exception => e
52
53
  Logger.debug e
53
54
  end
54
-
55
+
55
56
  elsif response.content_length.nil?
56
57
  Logger.debug "Reading response body using 1024 bytes chunks ..."
57
58
 
@@ -169,3 +170,4 @@ class Streamer
169
170
 
170
171
  end
171
172
  end
173
+ end
@@ -12,6 +12,7 @@ This project is released under the GPL 3 license.
12
12
  require 'thread'
13
13
 
14
14
  # Tnx to Puma ThreadPool!
15
+ module BetterCap
15
16
  module Proxy
16
17
  class ThreadPool
17
18
 
@@ -62,10 +63,10 @@ class ThreadPool
62
63
  #
63
64
  # Must be called with @mutex held!
64
65
  #
65
- def spawn_thread
66
+ def spawn_thread
66
67
  @spawned += 1
67
68
 
68
- th = Thread.new do
69
+ th = Thread.new do
69
70
  todo = @todo
70
71
  block = @block
71
72
  mutex = @mutex
@@ -199,3 +200,4 @@ class ThreadPool
199
200
  end
200
201
  end
201
202
  end
203
+ end
@@ -7,6 +7,7 @@ This project is released under the GPL 3 license.
7
7
  =end
8
8
  require 'bettercap/error'
9
9
 
10
+ module BetterCap
10
11
  module Shell
11
12
  class << self
12
13
 
@@ -38,3 +39,4 @@ module Shell
38
39
 
39
40
  end
40
41
  end
42
+ end
@@ -9,29 +9,20 @@ Blog : http://www.evilsocket.net/
9
9
  This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
- require 'bettercap/logger'
13
- require 'colorize'
14
-
12
+ module BetterCap
15
13
  class BaseParser
16
14
  def initialize
17
15
  @filters = []
18
16
  @name = 'BASE'
19
17
  end
20
18
 
21
- def addr2s( addr )
22
- target = Context.get.find_target addr, nil
23
- return target.to_s_compact unless target.nil?
24
- addr
25
- end
26
-
27
19
  def on_packet( pkt )
28
20
  s = pkt.to_s
29
21
  @filters.each do |filter|
30
22
  if s =~ filter
31
- Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
32
- "[#{@name}] ".green +
33
- pkt.payload.strip.yellow
23
+ StreamLogger.log_raw( pkt, @name, pkt.payload )
34
24
  end
35
25
  end
36
26
  end
37
27
  end
28
+ end
@@ -0,0 +1,21 @@
1
+ =begin
2
+
3
+ BETTERCAP
4
+
5
+ Author : Simone 'evilsocket' Margaritelli
6
+ Email : evilsocket@gmail.com
7
+ Blog : http://www.evilsocket.net/
8
+
9
+ This project is released under the GPL 3 license.
10
+
11
+ =end
12
+ require 'bettercap/sniffer/parsers/base'
13
+
14
+ module BetterCap
15
+ class CustomParser < BaseParser
16
+ def initialize( filter )
17
+ @filters = [ filter ]
18
+ @name = 'DATA'
19
+ end
20
+ end
21
+ end
@@ -11,9 +11,11 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
+ module BetterCap
14
15
  class FtpParser < BaseParser
15
16
  def initialize
16
17
  @filters = [ /(USER|PASS)\s+.+/ ]
17
18
  @name = 'FTP'
18
19
  end
19
20
  end
21
+ end
@@ -13,6 +13,7 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
  require 'base64'
15
15
 
16
+ module BetterCap
16
17
  class HttpauthParser < BaseParser
17
18
  def on_packet( pkt )
18
19
  lines = pkt.to_s.split("\n")
@@ -31,14 +32,12 @@ class HttpauthParser < BaseParser
31
32
  decoded = Base64.decode64(encoded)
32
33
  user, pass = decoded.split(':')
33
34
 
34
- Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
35
- '[HTTP BASIC AUTH]'.green + " http://#{hostname}#{path} - username=#{user} password=#{pass}".yellow
35
+ StreamLogger.log_raw( pkt, '[HTTP BASIC AUTH]'.green + " http://#{hostname}#{path} - username=#{user} password=#{pass}".yellow )
36
36
 
37
37
  elsif line =~ /Authorization:\s*Digest\s+(.+)/i
38
- Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
39
- '[HTTP DIGEST AUTH]'.green + " http://#{hostname}#{path}\n#{$1}".yellow
40
-
38
+ StreamLogger.log_raw( pkt, '[HTTP DIGEST AUTH]'.green + " http://#{hostname}#{path}\n#{$1}".yellow )
41
39
  end
42
40
  end
43
41
  end
44
42
  end
43
+ end
@@ -13,6 +13,7 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
  require 'resolv'
15
15
 
16
+ module BetterCap
16
17
  class HttpsParser < BaseParser
17
18
  @@prev = nil
18
19
 
@@ -28,10 +29,7 @@ class HttpsParser < BaseParser
28
29
  end
29
30
 
30
31
  if @@prev.nil? or @@prev != hostname
31
- Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
32
- '[HTTPS] '.green +
33
- "https://#{hostname}/".yellow
34
-
32
+ StreamLogger.log_raw( pkt, 'HTTPS', "https://#{hostname}/" )
35
33
  @@prev = hostname
36
34
  end
37
35
  end
@@ -40,3 +38,4 @@ class HttpsParser < BaseParser
40
38
  end
41
39
  end
42
40
  end
41
+ end
@@ -11,9 +11,11 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
+ module BetterCap
14
15
  class IrcParser < BaseParser
15
16
  def initialize
16
17
  @filters = [ /NICK\s+.+/, /NS IDENTIFY\s+.+/, /nickserv :identify\s+.+/ ]
17
18
  @name = 'IRC'
18
19
  end
19
20
  end
21
+ end
@@ -11,9 +11,11 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
+ module BetterCap
14
15
  class MailParser < BaseParser
15
16
  def initialize
16
17
  @filters = [ /(\d+ )?(auth|authenticate) ([a-z\-_0-9]+)/i ]
17
18
  @name = 'MAIL'
18
19
  end
19
20
  end
21
+ end
@@ -12,6 +12,7 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
+ module BetterCap
15
16
  class NtlmssParser < BaseParser
16
17
  def bin2hex( data )
17
18
  hex = ''
@@ -30,9 +31,8 @@ class NtlmssParser < BaseParser
30
31
  s = pkt.to_s
31
32
  if s =~ /NTLMSSP\x00\x03\x00\x00\x00.+/
32
33
  # TODO: Parse NTLMSSP packet.
33
- Logger.raw "[#{addr2s(pkt.ip_saddr)} > #{addr2s(pkt.ip_daddr)} #{pkt.proto.last}] " +
34
- '[NTLMSS] '.green +
35
- bin2hex( pkt.payload ).yellow
34
+ StreamLogger.log_raw( pkt, 'NTLMSS', bin2hex( pkt.payload ) )
36
35
  end
37
36
  end
38
37
  end
38
+ end
@@ -12,13 +12,13 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
+ module BetterCap
15
16
  class PostParser < BaseParser
16
- def on_packet( pkt )
17
- s = pkt.to_s
18
- if s =~ /POST\s+[^\s]+\s+HTTP.+/
19
- Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
20
- "[POST]\n".green +
21
- pkt.payload.strip.yellow
22
- end
17
+ def on_packet( pkt )
18
+ s = pkt.to_s
19
+ if s =~ /POST\s+[^\s]+\s+HTTP.+/
20
+ StreamLogger.log_raw( pkt, "POST\n", pkt.payload )
23
21
  end
22
+ end
23
+ end
24
24
  end
@@ -12,17 +12,17 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
+ module BetterCap
15
16
  class UrlParser < BaseParser
16
- def on_packet( pkt )
17
- s = pkt.to_s
18
- if s =~ /GET\s+([^\s]+)\s+HTTP.+Host:\s+([^\s]+).+/m
19
- host = $2
20
- url = $1
21
- if not url =~ /.+\.(png|jpg|jpeg|bmp|gif|img|ttf|woff|css|js).*/i
22
- Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
23
- '[GET] '.green +
24
- "http://#{host}#{url}".yellow
25
- end
26
- end
17
+ def on_packet( pkt )
18
+ s = pkt.to_s
19
+ if s =~ /GET\s+([^\s]+)\s+HTTP.+Host:\s+([^\s]+).+/m
20
+ host = $2
21
+ url = $1
22
+ if not url =~ /.+\.(png|jpg|jpeg|bmp|gif|img|ttf|woff|css|js).*/i
23
+ StreamLogger.log_raw( pkt, 'GET', "http://#{host}#{url}" )
24
+ end
27
25
  end
26
+ end
27
+ end
28
28
  end
@@ -14,6 +14,7 @@ require 'bettercap/factories/parser_factory'
14
14
  require 'colorize'
15
15
  require 'packetfu'
16
16
 
17
+ module BetterCap
17
18
  class Sniffer
18
19
  include PacketFu
19
20
 
@@ -87,12 +88,16 @@ class Sniffer
87
88
  def self.setup( ctx )
88
89
  @@ctx = ctx
89
90
 
90
- if !@@ctx.options.sniffer_pcap.nil?
91
+ unless @@ctx.options.sniffer_pcap.nil?
91
92
  @@pcap = PcapFile.new
92
93
  Logger.warn "Saving packets to #{@@ctx.options.sniffer_pcap} ."
93
94
  end
94
95
 
95
- @@parsers = ParserFactory.load_by_names @@ctx.options.parsers
96
+ if @@ctx.options.custom_parser.nil?
97
+ @@parsers = ParserFactory.load_by_names @@ctx.options.parsers
98
+ else
99
+ @@parsers = ParserFactory.load_custom @@ctx.options.custom_parser
100
+ end
96
101
 
97
102
  @@cap = Capture.new(
98
103
  iface: @@ctx.options.iface,
@@ -101,3 +106,4 @@ class Sniffer
101
106
  )
102
107
  end
103
108
  end
109
+ end
@@ -16,6 +16,7 @@ require 'bettercap/network'
16
16
  require 'bettercap/logger'
17
17
  require 'colorize'
18
18
 
19
+ module BetterCap
19
20
  class ArpSpoofer < ISpoofer
20
21
  def initialize
21
22
  @ctx = Context.get
@@ -52,7 +53,7 @@ class ArpSpoofer < ISpoofer
52
53
  def start
53
54
  Logger.info "Starting ARP spoofer ( #{@ctx.options.half_duplex ? 'Half' : 'Full'} Duplex ) ..."
54
55
 
55
- stop() unless !@running
56
+ stop() if @running
56
57
  @running = true
57
58
 
58
59
  @ctx.firewall.enable_forwarding(true) unless @forwarding
@@ -75,7 +76,7 @@ class ArpSpoofer < ISpoofer
75
76
  # we're only interested in 'who-has' packets
76
77
  if pkt.arp_opcode == 1 and pkt.arp_dst_mac.to_s == '00:00:00:00:00:00'
77
78
  is_from_us = ( pkt.arp_src_ip.to_s == @ctx.ifconfig[:ip_saddr] )
78
- if !is_from_us
79
+ unless is_from_us
79
80
  Logger.info "[ARP] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
80
81
 
81
82
  send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.ifconfig[:eth_saddr], pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
@@ -109,16 +110,24 @@ class ArpSpoofer < ISpoofer
109
110
  @ctx.targets.each do |target|
110
111
  # targets could change, update mac addresses if needed
111
112
  if target.mac.nil?
112
- Logger.warn "Getting target #{target.ip} MAC address ..."
113
-
114
113
  hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
115
114
  if hw.nil?
116
- Logger.warn "Couldn't determine target MAC"
115
+ Logger.warn "Couldn't determine target #{ip} MAC!"
117
116
  next
118
117
  else
119
118
  Logger.info " Target MAC : #{hw}"
120
119
  target.mac = hw
121
120
  end
121
+ # target was specified by MAC address
122
+ elsif target.ip_refresh
123
+ ip = Network.get_ip_address( @ctx, target.mac )
124
+ if ip.nil?
125
+ Logger.warn "Couldn't determine target #{target.mac} IP!"
126
+ next
127
+ else
128
+ Logger.info "Target #{target.mac} IP : #{ip}" if target.ip.nil? or target.ip != ip
129
+ target.ip = ip
130
+ end
122
131
  end
123
132
 
124
133
  send_spoofed_packet( @gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
@@ -159,3 +168,4 @@ class ArpSpoofer < ISpoofer
159
168
  sleep 1
160
169
  end
161
170
  end
171
+ end
@@ -12,6 +12,7 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/base/ispoofer'
13
13
  require 'bettercap/logger'
14
14
 
15
+ module BetterCap
15
16
  class NoneSpoofer < ISpoofer
16
17
  def initialize
17
18
  Logger.warn 'Spoofing disabled.'
@@ -21,3 +22,4 @@ class NoneSpoofer < ISpoofer
21
22
 
22
23
  def stop; end
23
24
  end
25
+ end
@@ -12,8 +12,9 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/logger'
13
13
  require 'socket'
14
14
 
15
+ module BetterCap
15
16
  class Target
16
- attr_accessor :ip, :mac, :vendor, :hostname
17
+ attr_accessor :ip, :mac, :vendor, :hostname, :ip_refresh
17
18
 
18
19
  NBNS_TIMEOUT = 30
19
20
  NBNS_PORT = 137
@@ -23,11 +24,19 @@ class Target
23
24
  @@prefixes = nil
24
25
 
25
26
  def initialize( ip, mac=nil )
26
- @ip = ip
27
- @mac = normalized_mac mac unless mac.nil?
27
+ if Network.is_ip?(ip)
28
+ @ip = ip
29
+ @ip_refresh = false
30
+ else
31
+ @ip = nil
32
+ mac = ip
33
+ @ip_refresh = true
34
+ end
35
+
36
+ @mac = Target.normalized_mac(mac) unless mac.nil?
28
37
  @vendor = Target.lookup_vendor(@mac) unless mac.nil?
29
38
  @hostname = nil
30
- @resolver = Thread.new { resolve! }
39
+ @resolver = Thread.new { resolve! } unless Context.get.options.no_target_nbns or @ip.nil?
31
40
  end
32
41
 
33
42
  def sortable_ip
@@ -35,12 +44,12 @@ class Target
35
44
  end
36
45
 
37
46
  def mac=(value)
38
- @mac = normalized_mac value
47
+ @mac = Target.normalized_mac(value)
39
48
  @vendor = Target.lookup_vendor(@mac) if not @mac.nil?
40
49
  end
41
50
 
42
51
  def to_s
43
- s = sprintf( '%-15s : %-17s', @ip, @mac )
52
+ s = sprintf( '%-15s : %-17s', if @ip.nil? then '???' else @ip end, @mac )
44
53
  s += " / #{@hostname}" unless @hostname.nil?
45
54
  s += if @vendor.nil? then " ( ??? )" else " ( #{@vendor} )" end
46
55
  s
@@ -55,15 +64,24 @@ class Target
55
64
  end
56
65
 
57
66
  def equals?(ip, mac)
58
- ( @ip == ip && ( mac.nil? || @mac == normalized_mac(mac) ) )
67
+ # compare by ip
68
+ if mac.nil?
69
+ return ( @ip == ip )
70
+ # compare by mac
71
+ elsif !@mac.nil? and ( @mac == mac )
72
+ Logger.info "Found IP #{ip} for target #{@mac}!" if @ip.nil?
73
+ @ip = ip
74
+ return true
75
+ end
76
+ false
59
77
  end
60
78
 
61
- private
62
-
63
- def normalized_mac(v)
79
+ def self.normalized_mac(v)
64
80
  v.split(':').map { |e| if e.size == 2 then e.upcase else "0#{e.upcase}" end }.join(':')
65
81
  end
66
82
 
83
+ private
84
+
67
85
  def resolve!
68
86
  resp, sock = nil, nil
69
87
  begin
@@ -103,3 +121,4 @@ private
103
121
  @@prefixes[ mac.split(':')[0,3].join('').upcase ]
104
122
  end
105
123
  end
124
+ end