bettercap 1.1.10 → 1.2.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.
- 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
|