bettercap 1.1.10 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/TODO.md +8 -7
- data/bin/bettercap +8 -2
- data/lib/bettercap.rb +5 -4
- data/lib/bettercap/context.rb +54 -8
- data/lib/bettercap/discovery/agents/arp.rb +17 -4
- data/lib/bettercap/discovery/agents/base.rb +16 -52
- data/lib/bettercap/discovery/agents/icmp.rb +25 -17
- data/lib/bettercap/discovery/agents/udp.rb +9 -20
- data/lib/bettercap/discovery/{discovery.rb → thread.rb} +10 -9
- data/lib/bettercap/error.rb +1 -2
- data/lib/bettercap/factories/{firewall_factory.rb → firewall.rb} +11 -7
- data/lib/bettercap/factories/{parser_factory.rb → parser.rb} +13 -3
- data/lib/bettercap/factories/{spoofer_factory.rb → spoofer.rb} +10 -3
- data/lib/bettercap/firewalls/base.rb +76 -0
- data/lib/bettercap/firewalls/linux.rb +26 -16
- data/lib/bettercap/firewalls/osx.rb +22 -13
- data/lib/bettercap/firewalls/redirection.rb +15 -1
- data/lib/bettercap/httpd/server.rb +5 -0
- data/lib/bettercap/logger.rb +29 -8
- data/lib/bettercap/network.rb +105 -105
- data/lib/bettercap/options.rb +99 -41
- data/lib/bettercap/packet_queue.rb +92 -0
- data/lib/bettercap/proxy/certstore.rb +49 -43
- data/lib/bettercap/proxy/module.rb +4 -2
- data/lib/bettercap/proxy/proxy.rb +7 -2
- data/lib/bettercap/proxy/request.rb +28 -16
- data/lib/bettercap/proxy/response.rb +23 -2
- data/lib/bettercap/proxy/stream_logger.rb +6 -0
- data/lib/bettercap/proxy/streamer.rb +13 -5
- data/lib/bettercap/proxy/thread_pool.rb +6 -14
- data/lib/bettercap/shell.rb +5 -3
- data/lib/bettercap/sniffer/parsers/base.rb +7 -1
- data/lib/bettercap/sniffer/parsers/custom.rb +6 -1
- data/lib/bettercap/sniffer/parsers/ftp.rb +8 -5
- data/lib/bettercap/sniffer/parsers/httpauth.rb +4 -1
- data/lib/bettercap/sniffer/parsers/https.rb +4 -1
- data/lib/bettercap/sniffer/parsers/irc.rb +8 -5
- data/lib/bettercap/sniffer/parsers/mail.rb +8 -5
- data/lib/bettercap/sniffer/parsers/ntlmss.rb +21 -18
- data/lib/bettercap/sniffer/parsers/post.rb +4 -1
- data/lib/bettercap/sniffer/parsers/url.rb +4 -1
- data/lib/bettercap/sniffer/sniffer.rb +7 -3
- data/lib/bettercap/spoofers/arp.rb +69 -94
- data/lib/bettercap/spoofers/base.rb +132 -0
- data/lib/bettercap/spoofers/icmp.rb +200 -0
- data/lib/bettercap/spoofers/none.rb +8 -2
- data/lib/bettercap/target.rb +117 -90
- data/lib/bettercap/update_checker.rb +6 -0
- data/lib/bettercap/version.rb +3 -1
- metadata +24 -8
- data/lib/bettercap/base/ifirewall.rb +0 -46
- 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
|
12
|
+
require 'bettercap/spoofers/base'
|
13
13
|
require 'bettercap/logger'
|
14
14
|
|
15
15
|
module BetterCap
|
16
|
-
|
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
|
data/lib/bettercap/target.rb
CHANGED
@@ -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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
131
|
+
def parse_nbns_response resp
|
132
|
+
resp[0][57,15].to_s.strip
|
133
|
+
end
|
107
134
|
|
108
|
-
|
109
|
-
|
110
|
-
|
135
|
+
def self.lookup_vendor( mac )
|
136
|
+
if @@prefixes == nil
|
137
|
+
Logger.debug 'Preloading hardware vendor prefixes ...'
|
111
138
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|