bettercap 1.1.10 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.md +8 -7
  3. data/bin/bettercap +8 -2
  4. data/lib/bettercap.rb +5 -4
  5. data/lib/bettercap/context.rb +54 -8
  6. data/lib/bettercap/discovery/agents/arp.rb +17 -4
  7. data/lib/bettercap/discovery/agents/base.rb +16 -52
  8. data/lib/bettercap/discovery/agents/icmp.rb +25 -17
  9. data/lib/bettercap/discovery/agents/udp.rb +9 -20
  10. data/lib/bettercap/discovery/{discovery.rb → thread.rb} +10 -9
  11. data/lib/bettercap/error.rb +1 -2
  12. data/lib/bettercap/factories/{firewall_factory.rb → firewall.rb} +11 -7
  13. data/lib/bettercap/factories/{parser_factory.rb → parser.rb} +13 -3
  14. data/lib/bettercap/factories/{spoofer_factory.rb → spoofer.rb} +10 -3
  15. data/lib/bettercap/firewalls/base.rb +76 -0
  16. data/lib/bettercap/firewalls/linux.rb +26 -16
  17. data/lib/bettercap/firewalls/osx.rb +22 -13
  18. data/lib/bettercap/firewalls/redirection.rb +15 -1
  19. data/lib/bettercap/httpd/server.rb +5 -0
  20. data/lib/bettercap/logger.rb +29 -8
  21. data/lib/bettercap/network.rb +105 -105
  22. data/lib/bettercap/options.rb +99 -41
  23. data/lib/bettercap/packet_queue.rb +92 -0
  24. data/lib/bettercap/proxy/certstore.rb +49 -43
  25. data/lib/bettercap/proxy/module.rb +4 -2
  26. data/lib/bettercap/proxy/proxy.rb +7 -2
  27. data/lib/bettercap/proxy/request.rb +28 -16
  28. data/lib/bettercap/proxy/response.rb +23 -2
  29. data/lib/bettercap/proxy/stream_logger.rb +6 -0
  30. data/lib/bettercap/proxy/streamer.rb +13 -5
  31. data/lib/bettercap/proxy/thread_pool.rb +6 -14
  32. data/lib/bettercap/shell.rb +5 -3
  33. data/lib/bettercap/sniffer/parsers/base.rb +7 -1
  34. data/lib/bettercap/sniffer/parsers/custom.rb +6 -1
  35. data/lib/bettercap/sniffer/parsers/ftp.rb +8 -5
  36. data/lib/bettercap/sniffer/parsers/httpauth.rb +4 -1
  37. data/lib/bettercap/sniffer/parsers/https.rb +4 -1
  38. data/lib/bettercap/sniffer/parsers/irc.rb +8 -5
  39. data/lib/bettercap/sniffer/parsers/mail.rb +8 -5
  40. data/lib/bettercap/sniffer/parsers/ntlmss.rb +21 -18
  41. data/lib/bettercap/sniffer/parsers/post.rb +4 -1
  42. data/lib/bettercap/sniffer/parsers/url.rb +4 -1
  43. data/lib/bettercap/sniffer/sniffer.rb +7 -3
  44. data/lib/bettercap/spoofers/arp.rb +69 -94
  45. data/lib/bettercap/spoofers/base.rb +132 -0
  46. data/lib/bettercap/spoofers/icmp.rb +200 -0
  47. data/lib/bettercap/spoofers/none.rb +8 -2
  48. data/lib/bettercap/target.rb +117 -90
  49. data/lib/bettercap/update_checker.rb +6 -0
  50. data/lib/bettercap/version.rb +3 -1
  51. metadata +24 -8
  52. data/lib/bettercap/base/ifirewall.rb +0 -46
  53. data/lib/bettercap/base/ispoofer.rb +0 -32
@@ -0,0 +1,132 @@
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
+ module BetterCap
13
+ module Spoofers
14
+ # Base class for BetterCap::Spoofers modules.
15
+ class Base
16
+ # Will raise NotImplementedError .
17
+ def initialize
18
+ not_implemented_method!
19
+ end
20
+ # Will raise NotImplementedError .
21
+ def start
22
+ not_implemented_method!
23
+ end
24
+ # Will raise NotImplementedError .
25
+ def stop
26
+ not_implemented_method!
27
+ end
28
+
29
+ private
30
+
31
+ def sniff_packets( filter )
32
+ begin
33
+ @capture = PacketFu::Capture.new(
34
+ iface: @ctx.options.iface,
35
+ filter: filter,
36
+ start: true
37
+ )
38
+ rescue Exception => e
39
+ Logger.error e.message
40
+ end
41
+
42
+ @capture.stream.each do |p|
43
+ begin
44
+ if not @running
45
+ Logger.debug 'Stopping thread ...'
46
+ Thread.exit
47
+ break
48
+ end
49
+
50
+ pkt = PacketFu::Packet.parse p rescue nil
51
+
52
+ yield( pkt ) unless pkt.nil?
53
+
54
+ rescue Exception => e
55
+ Logger.error e.message
56
+ end
57
+ end
58
+ end
59
+
60
+ def spoof_loop( delay )
61
+ prev_size = @ctx.targets.size
62
+ loop do
63
+ if not @running
64
+ Logger.debug 'Stopping spoofing thread ...'
65
+ Thread.exit
66
+ break
67
+ end
68
+
69
+ size = @ctx.targets.size
70
+ if size > prev_size
71
+ Logger.warn "Aquired #{size - prev_size} new targets."
72
+ elsif size < prev_size
73
+ Logger.warn "Lost #{prev_size - size} targets."
74
+ end
75
+
76
+ Logger.debug "Spoofing #{@ctx.targets.size} targets ..."
77
+
78
+ update_targets!
79
+
80
+ @ctx.targets.each do |target|
81
+ yield(target)
82
+ end
83
+
84
+ prev_size = @ctx.targets.size
85
+
86
+ sleep(delay)
87
+ end
88
+ end
89
+
90
+ def update_gateway!
91
+ Logger.info "Getting gateway #{@ctx.gateway} MAC address ..."
92
+
93
+ hw = Network.get_hw_address( @ctx.ifconfig, @ctx.gateway )
94
+ raise BetterCap::Error, "Couldn't determine router MAC" if hw.nil?
95
+
96
+ @gateway = Target.new( @ctx.gateway, hw )
97
+
98
+ Logger.info " #{@gateway}"
99
+ end
100
+
101
+ def update_targets!
102
+ @ctx.targets.each do |target|
103
+ # targets could change, update mac addresses if needed
104
+ if target.mac.nil?
105
+ hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
106
+ if hw.nil?
107
+ Logger.warn "Couldn't determine target #{ip} MAC!"
108
+ next
109
+ else
110
+ Logger.info " Target MAC : #{hw}"
111
+ target.mac = hw
112
+ end
113
+ # target was specified by MAC address
114
+ elsif target.ip_refresh
115
+ ip = Network.get_ip_address( @ctx, target.mac )
116
+ if ip.nil?
117
+ Logger.warn "Couldn't determine target #{target.mac} IP!"
118
+ next
119
+ else
120
+ Logger.info "Target #{target.mac} IP : #{ip}" if target.ip.nil? or target.ip != ip
121
+ target.ip = ip
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ def not_implemented_method!
128
+ raise NotImplementedError, 'Spoofers::Base: Unimplemented method!'
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,200 @@
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/spoofers/base'
13
+ require 'bettercap/error'
14
+ require 'bettercap/context'
15
+ require 'bettercap/network'
16
+ require 'bettercap/logger'
17
+ require 'colorize'
18
+ require 'net/dns'
19
+ require 'resolv'
20
+
21
+ module BetterCap
22
+ module Spoofers
23
+ # Class to create ICMP redirection packets.
24
+ class ICMPRedirectPacket < PacketFu::Packet
25
+ ICMP_REDIRECT = 5
26
+ ICMP_REDIRECT_HOST = 1
27
+
28
+ IP_PROTO_ICMP = 1
29
+ IP_PROTO_UDP = 17
30
+
31
+ include PacketFu::EthHeaderMixin
32
+ include PacketFu::IPHeaderMixin
33
+ include PacketFu::ICMPHeaderMixin
34
+ include PacketFu::UDPHeaderMixin
35
+
36
+ attr_accessor :eth_header, :ip_header, :icmp_header, :ip_encl_header
37
+
38
+ def initialize(args={})
39
+ @eth_header = PacketFu::EthHeader.new(args).read(args[:eth])
40
+
41
+ @ip_header = PacketFu::IPHeader.new(args).read(args[:ip])
42
+ @ip_header.ip_proto = IP_PROTO_ICMP
43
+
44
+ @icmp_header = PacketFu::ICMPHeader.new(args).read(args[:icmp])
45
+ @icmp_header.icmp_type = ICMP_REDIRECT
46
+ @icmp_header.icmp_code = ICMP_REDIRECT_HOST
47
+
48
+ @ip_encl_header = PacketFu::IPHeader.new(args).read(args[:ip])
49
+ @ip_encl_header.ip_proto = IP_PROTO_UDP
50
+
51
+ @udp_dummy = PacketFu::UDPPacket.new
52
+ @udp_dummy.udp_src = 53
53
+ @udp_dummy.udp_dst = 53
54
+
55
+ @ip_header.body = @icmp_header
56
+ @eth_header.body = @ip_header
57
+
58
+ @headers = [@eth_header, @ip_header, @icmp_header]
59
+ super
60
+ end
61
+
62
+ def update!( gateway, target, local, address2redirect )
63
+ @eth_header.eth_src = PacketFu::EthHeader.mac2str(gateway.mac)
64
+ @ip_header.ip_saddr = gateway.ip
65
+
66
+ @eth_header.eth_dst = PacketFu::EthHeader.mac2str(target.mac)
67
+ @ip_header.ip_daddr = target.ip
68
+
69
+ @udp_dummy.ip_saddr = target.ip
70
+ @udp_dummy.ip_daddr = address2redirect
71
+ @udp_dummy.recalc
72
+
73
+ @icmp_header.body = local.split('.').collect(&:to_i).pack('C*') +
74
+ @udp_dummy.ip_header.to_s
75
+
76
+ recalc
77
+ end
78
+ end
79
+
80
+ # This class is responsible of performing ICMP redirect attack on the network.
81
+ class Icmp < Base
82
+ # Initialize the BetterCap::Spoofers::Icmp object.
83
+ def initialize
84
+ @ctx = Context.get
85
+ @forwarding = @ctx.firewall.forwarding_enabled?
86
+ @gateway = nil
87
+ @local = @ctx.ifconfig[:ip_saddr]
88
+ @spoof_thread = nil
89
+ @watch_thread = nil
90
+ @running = false
91
+ @entries = [ '8.8.8.8', '8.8.4.4', # Google DNS
92
+ '208.67.222.222', '208.67.220.220' ] # OpenDNS
93
+
94
+ update_gateway!
95
+ end
96
+
97
+ # Send ICMP redirect to the +target+, redirecting the gateway ip and
98
+ # everything in the @entries list of addresses to us.
99
+ def send_spoofed_packet( target )
100
+ ( [@gateway.ip] + @entries ).each do |address|
101
+ begin
102
+ Logger.debug "Sending ICMP Redirect to #{target.to_s_compact} redirecting #{address} to us ..."
103
+
104
+ pkt = ICMPRedirectPacket.new
105
+ pkt.update!( @gateway, target, @local, address )
106
+ @ctx.packets.push(pkt)
107
+ rescue Exception => e
108
+ Logger.debug "#{self.class.name} : #{e.message}"
109
+ end
110
+ end
111
+ end
112
+
113
+ # Start the ICMP redirect spoofing.
114
+ def start
115
+ Logger.info "Starting ICMP redirect spoofer ..."
116
+
117
+ stop() if @running
118
+ @running = true
119
+
120
+ @ctx.firewall.enable_forwarding(true) unless @forwarding
121
+ @ctx.firewall.enable_send_redirects(false)
122
+
123
+ @spoof_thread = Thread.new { icmp_spoofer }
124
+ @watch_thread = Thread.new { dns_watcher }
125
+ end
126
+
127
+ # Stop the ICMP redirect spoofing, reset firewall state.
128
+ def stop
129
+ raise 'ICMP redirect spoofer is not running' unless @running
130
+
131
+ Logger.info 'Stopping ICMP redirect spoofer ...'
132
+
133
+ Logger.debug "Resetting packet forwarding to #{@forwarding} ..."
134
+ @ctx.firewall.enable_forwarding( @forwarding )
135
+
136
+ @running = false
137
+ begin
138
+ @spoof_thread.exit
139
+ rescue; end
140
+
141
+ begin
142
+ @workers.map(&:exit)
143
+ rescue; end
144
+ end
145
+
146
+ private
147
+
148
+ def is_interesting_packet?(pkt)
149
+ return false if pkt.ip_saddr == @local
150
+ @ctx.targets.each do |target|
151
+ if target.ip and ( target.ip == pkt.ip_saddr or target.ip == pkt.ip_daddr )
152
+ return true
153
+ end
154
+ end
155
+ false
156
+ end
157
+
158
+ def dns_watcher
159
+ Logger.info 'DNS watcher started ...'
160
+
161
+ sniff_packets('udp and port 53') { |pkt|
162
+ next unless is_interesting_packet?(pkt)
163
+
164
+ dns = Net::DNS::Packet.parse(pkt.payload) rescue nil
165
+ next if dns.nil?
166
+
167
+ Logger.debug dns.inspect
168
+
169
+ if dns.header.anCount > 0
170
+ dns.answer.each do |a|
171
+ if a.respond_to?(:address)
172
+ Logger.debug "[DNS] Redirecting #{a.address.to_s} ..."
173
+ @entries << a.address.to_s unless @entries.include?(a.address.to_s)
174
+ end
175
+ end
176
+ end
177
+
178
+ if dns.header.qdCount > 0
179
+ name = dns.question.first.qName
180
+ if name =~ /\.$/
181
+ name = name[0,name.size-1]
182
+ end
183
+ Logger.info "[#{'DNS'.green}] #{pkt.ip_saddr} is requesting '#{name}' address ..."
184
+ Resolv.each_address(name) do |ip|
185
+ @entries << ip unless @entries.include?(ip)
186
+ end
187
+ end
188
+ }
189
+ end
190
+
191
+ def icmp_spoofer
192
+ spoof_loop(3) { |target|
193
+ unless target.ip.nil? or target.mac.nil?
194
+ send_spoofed_packet target
195
+ end
196
+ }
197
+ end
198
+ end
199
+ end
200
+ end
@@ -9,17 +9,23 @@ Blog : http://www.evilsocket.net/
9
9
  This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
- require 'bettercap/base/ispoofer'
12
+ require 'bettercap/spoofers/base'
13
13
  require 'bettercap/logger'
14
14
 
15
15
  module BetterCap
16
- class NoneSpoofer < ISpoofer
16
+ module Spoofers
17
+ # Dummy class used to disable spoofing.
18
+ class None < Base
19
+ # Initialize the non-spoofing class.
17
20
  def initialize
18
21
  Logger.warn 'Spoofing disabled.'
19
22
  end
20
23
 
24
+ # This does nothing.
21
25
  def start; end
22
26
 
27
+ # This does nothing.
23
28
  def stop; end
24
29
  end
25
30
  end
31
+ end
@@ -13,112 +13,139 @@ require 'bettercap/logger'
13
13
  require 'socket'
14
14
 
15
15
  module BetterCap
16
+ # This class represents a target, namely a single endpoint device on the
17
+ # network.
16
18
  class Target
17
- attr_accessor :ip, :mac, :vendor, :hostname, :ip_refresh
18
-
19
- NBNS_TIMEOUT = 30
20
- NBNS_PORT = 137
21
- NBNS_BUFSIZE = 65536
22
- NBNS_REQUEST = "\x82\x28\x0\x0\x0\x1\x0\x0\x0\x0\x0\x0\x20\x43\x4B\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x0\x0\x21\x0\x1"
23
-
24
- @@prefixes = nil
25
-
26
- def initialize( ip, 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?
37
- @vendor = Target.lookup_vendor(@mac) unless mac.nil?
38
- @hostname = nil
39
- @resolver = Thread.new { resolve! } unless Context.get.options.no_target_nbns or @ip.nil?
19
+ # The IP address of this device.
20
+ attr_accessor :ip
21
+ # The MAC address of the device network interface.
22
+ attr_accessor :mac
23
+ # Vendor of the device network interface if available.
24
+ attr_accessor :vendor
25
+ # NetBIOS hostname of the device if available.
26
+ attr_accessor :hostname
27
+ # True if the IP attribute of this target needs to be updated.
28
+ attr_accessor :ip_refresh
29
+
30
+ # Timeout in seconds for the NBNS hostname resolution request.
31
+ NBNS_TIMEOUT = 30
32
+ # UDP port for the NBNS hostname resolution request.
33
+ NBNS_PORT = 137
34
+ # Buffer size for the NBNS hostname resolution request.
35
+ NBNS_BUFSIZE = 65536
36
+ # NBNS hostname resolution request buffer.
37
+ NBNS_REQUEST = "\x82\x28\x0\x0\x0\x1\x0\x0\x0\x0\x0\x0\x20\x43\x4B\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x0\x0\x21\x0\x1"
38
+
39
+ @@prefixes = nil
40
+
41
+ # Create a +Target+ object given its +ip+ and (optional) +mac+ address.
42
+ # The +ip+ argument could also be a MAC address itself, in this case the
43
+ # ip address will be parsed from the computer ARP cache and updated
44
+ # accordingly.
45
+ def initialize( ip, mac=nil )
46
+ if Network.is_ip?(ip)
47
+ @ip = ip
48
+ @ip_refresh = false
49
+ else
50
+ @ip = nil
51
+ mac = ip
52
+ @ip_refresh = true
40
53
  end
41
54
 
42
- def sortable_ip
43
- @ip.split('.').inject(0) {|total,value| (total << 8 ) + value.to_i}
44
- end
45
-
46
- def mac=(value)
47
- @mac = Target.normalized_mac(value)
48
- @vendor = Target.lookup_vendor(@mac) if not @mac.nil?
49
- end
50
-
51
- def to_s
52
- s = sprintf( '%-15s : %-17s', if @ip.nil? then '???' else @ip end, @mac )
53
- s += " / #{@hostname}" unless @hostname.nil?
54
- s += if @vendor.nil? then " ( ??? )" else " ( #{@vendor} )" end
55
- s
56
- end
57
-
58
- def to_s_compact
59
- if @hostname
60
- "#{@hostname}/#{@ip}"
61
- else
62
- @ip
63
- end
55
+ @mac = Target.normalized_mac(mac) unless mac.nil?
56
+ @vendor = Target.lookup_vendor(@mac) unless mac.nil?
57
+ @hostname = nil
58
+ @resolver = Thread.new { resolve! } unless Context.get.options.no_target_nbns or @ip.nil?
59
+ end
60
+
61
+ # Return the integer representation of the +ip+ attribute which can be
62
+ # used for sorting a list of +Target+ objects+
63
+ def sortable_ip
64
+ @ip.split('.').inject(0) {|total,value| (total << 8 ) + value.to_i}
65
+ end
66
+
67
+ # +mac+ attribute setter, it will normalize the +value+ and perform
68
+ # a vendor lookup.
69
+ def mac=(value)
70
+ @mac = Target.normalized_mac(value)
71
+ @vendor = Target.lookup_vendor(@mac) if not @mac.nil?
72
+ end
73
+
74
+ # Return a verbose string representation of this object.
75
+ def to_s
76
+ s = sprintf( '%-15s : %-17s', if @ip.nil? then '???' else @ip end, @mac )
77
+ s += " / #{@hostname}" unless @hostname.nil?
78
+ s += if @vendor.nil? then " ( ??? )" else " ( #{@vendor} )" end
79
+ s
80
+ end
81
+
82
+ # Return a compact string representation of this object.
83
+ def to_s_compact
84
+ if @hostname
85
+ "#{@hostname}/#{@ip}"
86
+ else
87
+ @ip
64
88
  end
65
-
66
- def equals?(ip, 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
89
+ end
90
+
91
+ # Return true if this +Target+ is equal to the specified +ip+ and +mac+,
92
+ # otherwise return false.
93
+ def equals?(ip, mac)
94
+ # compare by ip
95
+ if mac.nil?
96
+ return ( @ip == ip )
97
+ # compare by mac
98
+ elsif !@mac.nil? and ( @mac == mac )
99
+ Logger.info "Found IP #{ip} for target #{@mac}!" if @ip.nil?
100
+ @ip = ip
101
+ return true
77
102
  end
103
+ false
104
+ end
78
105
 
79
- def self.normalized_mac(v)
80
- v.split(':').map { |e| if e.size == 2 then e.upcase else "0#{e.upcase}" end }.join(':')
81
- end
106
+ def self.normalized_mac(v)
107
+ v.split(':').map { |e| if e.size == 2 then e.upcase else "0#{e.upcase}" end }.join(':')
108
+ end
82
109
 
83
110
  private
84
111
 
85
- def resolve!
86
- resp, sock = nil, nil
87
- begin
88
- sock = UDPSocket.open
89
- sock.send( NBNS_REQUEST, 0, @ip, NBNS_PORT )
90
- resp = if select([sock], nil, nil, NBNS_TIMEOUT)
91
- sock.recvfrom(NBNS_BUFSIZE)
92
- end
93
- if resp
94
- @hostname = parse_nbns_response resp
95
- Logger.info "Found NetBIOS name '#{@hostname}' for address #{@ip}"
96
- end
97
- rescue Exception => e
98
- Logger.debug e
99
- ensure
100
- sock.close if sock
112
+ def resolve!
113
+ resp, sock = nil, nil
114
+ begin
115
+ sock = UDPSocket.open
116
+ sock.send( NBNS_REQUEST, 0, @ip, NBNS_PORT )
117
+ resp = if select([sock], nil, nil, NBNS_TIMEOUT)
118
+ sock.recvfrom(NBNS_BUFSIZE)
101
119
  end
120
+ if resp
121
+ @hostname = parse_nbns_response resp
122
+ Logger.info "Found NetBIOS name '#{@hostname}' for address #{@ip}"
123
+ end
124
+ rescue Exception => e
125
+ Logger.debug e
126
+ ensure
127
+ sock.close if sock
102
128
  end
129
+ end
103
130
 
104
- def parse_nbns_response resp
105
- resp[0][57,15].to_s.strip
106
- end
131
+ def parse_nbns_response resp
132
+ resp[0][57,15].to_s.strip
133
+ end
107
134
 
108
- def self.lookup_vendor( mac )
109
- if @@prefixes == nil
110
- Logger.debug 'Preloading hardware vendor prefixes ...'
135
+ def self.lookup_vendor( mac )
136
+ if @@prefixes == nil
137
+ Logger.debug 'Preloading hardware vendor prefixes ...'
111
138
 
112
- @@prefixes = {}
113
- filename = File.dirname(__FILE__) + '/hw-prefixes'
114
- File.open( filename ).each do |line|
115
- if line =~ /^([A-F0-9]{6})\s(.+)$/
116
- @@prefixes[$1] = $2
117
- end
139
+ @@prefixes = {}
140
+ filename = File.dirname(__FILE__) + '/hw-prefixes'
141
+ File.open( filename ).each do |line|
142
+ if line =~ /^([A-F0-9]{6})\s(.+)$/
143
+ @@prefixes[$1] = $2
118
144
  end
119
145
  end
120
-
121
- @@prefixes[ mac.split(':')[0,3].join('').upcase ]
122
146
  end
147
+
148
+ @@prefixes[ mac.split(':')[0,3].join('').upcase ]
149
+ end
123
150
  end
124
151
  end