module Macker
Macker
namespace
Macker
namespace
Macker
namespace
Constants
- VERSION
Macker
version
Attributes
Get the timestamp of vendor list in memory @return [Time] time object or nil
Proc timestamp accessor to set external timestamp @param value [Time] proc timestamp @return [Time] proc timestamp
Public Instance Methods
Fetch new vendor list if cached list is expired @return [Boolean] true if vendor list is expired and updated from remote
# File lib/macker.rb, line 172 def expire! if expired? update(true) true else false end end
Check if vendor list is expired @return [Boolean] true if vendor list is expired
# File lib/macker.rb, line 194 def expired? Time.now > vendors_expiration end
Generate a MAC address.
-
No options for random MAC.
-
Vendor option to get a valid OUI MAC.
-
Vendor Name option to get a random MAC from vendor.
@example
generate generate(vendor: true) generate(vendor: 'IEEE Registration Authority')
@param opts [Hash] options for the method @return [Address] MAC address with data
# File lib/macker.rb, line 102 def generate(opts = {}) lapsed! return generate_by_iso_code(opts.delete(:iso_code).upcase, opts) if opts[:iso_code] vendor = opts.delete(:vendor) case vendor when nil, false Address.new(rand(2**48)) when true generate_by_vendor(prefix_table[prefix_table.keys.shuffle.sample][:name], opts) when String generate_by_vendor(vendor, opts) else raise(InvalidOptions, "Incompatible option vendor for generate: #{vendor.class}") end end
Generate a MAC address.
-
No options for random MAC address.
-
Vendor option to get a valid OUI MAC address.
-
Vendor name option to get a random MAC address from vendor.
Raises an error if an error occurs.
@example
generate generate(vendor: true) generate(vendor: 'No vendor')
@param opts [Hash] options for the method @return [Address] MAC address with vendor data
# File lib/macker.rb, line 131 def generate!(opts = {}) generate(opts.merge(raising: true)) end
Vendor table with all country iso codes as keys @return [Hash] vendor iso codes table
# File lib/macker.rb, line 144 def iso_code_table update unless @iso_code_table @iso_code_table end
Fetch new vendor list if cached list is lapsed (expired or stale) @return [Boolean] true if vendor list is lapsed and updated
# File lib/macker.rb, line 158 def lapsed! if config.auto_expire && expired? update(true) true elsif config.auto_stale && stale? update true else false end end
Lookup for a vendor with given MAC address.
@example
lookup('00:04:A9:6D:B8:AC') lookup(20022409388)
@param mac [Address,Integer,String] MAC address @param opts [Hash] options for the method @return [Address] MAC address with vendor data
# File lib/macker.rb, line 68 def lookup(mac, opts = {}) lapsed! data = prefix_table[Address.new(mac).prefix] if data.nil? opts[:raising] ? raise(NotFoundOuiVendor, "OUI not found for MAC: #{mac}") : (return nil) end Address.new(mac, data) end
Lookup for a vendor with given MAC address. Raises an error if no vendor found.
@example
lookup!('00:04:A9:6D:B8:AC') lookup!('80:47:FB:B2:9E:D6')
@param mac [Address,Integer,String] MAC address @return [Address] MAC address with vendor data
# File lib/macker.rb, line 86 def lookup!(mac) lookup(mac, raising: true) end
Vendor table with all base16 MAC prefixes as keys @return [Hash] vendor prefixes table
# File lib/macker.rb, line 137 def prefix_table update unless @prefix_table @prefix_table end
Fetch new vendor list if cached list is stale @return [Boolean] true if vendor list is stale and updated from cache
# File lib/macker.rb, line 183 def stale! if stale? update true else false end end
Check if vendor list is stale Stale is true if vendor list is updated straight by another thread. The actual thread has always old vendor list in memory store. @return [Boolean] true if vendor list is stale
# File lib/macker.rb, line 202 def stale? if config.cache.is_a?(Proc) proc_timestamp != mem_timestamp else file_timestamp != mem_timestamp end end
Update all OUI tables @param straight [Boolean] true for straight, default is careful @return [Time] timestamp of the update
# File lib/macker.rb, line 47 def update(straight = false) @prefix_table = {} @iso_code_table = {} @vendor_table = {} vendor_list(straight) @mem_timestamp = if config.cache.is_a?(Proc) proc_timestamp else file_timestamp end end
Vendor table with all country vendor names as keys @return [Hash] vendor names table
# File lib/macker.rb, line 151 def vendor_table update unless @vendor_table @vendor_table end
Get vendor list expiration time based on ttl @return [Time] vendor list expiration time
# File lib/macker.rb, line 212 def vendors_expiration if config.cache.is_a?(Proc) proc_timestamp + config.ttl_in_seconds else file_timestamp + config.ttl_in_seconds end end
Protected Instance Methods
Get file name with timestamp @return [String] file name
# File lib/macker.rb, line 383 def file_name File.basename(file_path) end
Get file path with timestamp @return [String] file path
# File lib/macker.rb, line 376 def file_path Dir.glob(config.cache).first || File.join(File.dirname(config.cache), File.basename(config.cache).gsub(/_.+\.txt/, '_0.txt')) end
Get file timestamp @return [Time] file timestamp
# File lib/macker.rb, line 389 def file_timestamp timestamp = file_name.scan(/\d+/).first timestamp ? Time.at(timestamp.to_i) : Time.at(0) end
Update file name timestamp @return [Integer] normally 0
# File lib/macker.rb, line 396 def file_update File.rename(file_path, File.join(File.dirname(file_path), File.basename(file_path).gsub(/_\d+\.txt/, "_#{Time.now.to_i}.txt"))) end
Generate a MAC address by iso code.
@param iso_code [String] iso code @param opts [Hash] options for the method @return [Address] MAC address with vendor data
# File lib/macker.rb, line 247 def generate_by_iso_code(iso_code, opts = {}) ouis = iso_code_table[iso_code] if ouis.nil? || ouis.empty? opts[:raising] ? raise(NotFoundOuiVendor, "OUI not found for iso code #{iso_code}") : (return nil) end oui = ouis[rand(ouis.size)] m1 = Address.new(oui[:prefix]).to_i m2 = rand(2**24) mac = m1 + m2 Address.new(mac, name: oui[:name], address: oui[:address], iso_code: iso_code) end
Generate a MAC address by vendor.
@param vendor [String] name of vendor @param opts [Hash] options for the method @return [Address] MAC address with vendor data
# File lib/macker.rb, line 227 def generate_by_vendor(vendor, opts = {}) ouis = vendor_table[vendor] if ouis.nil? || ouis.empty? opts[:raising] ? raise(NotFoundOuiVendor, "OUI not found for vendor: #{vendor}") : (return nil) end oui = ouis[rand(ouis.size)] m1 = Address.new(oui[:prefix]).to_i m2 = rand(2**24) mac = m1 + m2 Address.new(mac, name: vendor, address: oui[:address], iso_code: oui[:iso_code]) end
Opens an URL and reads the content @return [String] text content
# File lib/macker.rb, line 366 def open_url opts = [config.oui_full_url] opts << { 'User-Agent' => config.user_agent } if config.user_agent open(*opts).read rescue OpenURI::HTTPError '' end
Get raw vendor list from cache and then from url @param rescue_straight [Boolean] true for rescue straight, default true @return [String] text content
# File lib/macker.rb, line 294 def raw_vendor_list_careful(rescue_straight = true) res = read_from_cache raise if res.to_s.empty? res rescue rescue_straight ? raw_vendor_list_straight : '' end
Get raw vendor list from url @return [String] text content
# File lib/macker.rb, line 304 def raw_vendor_list_straight res = read_from_url raise if res.to_s.empty? res rescue raw_vendor_list_careful(false) end
Read from cache when exist @return [Proc,String] text content
# File lib/macker.rb, line 347 def read_from_cache if config.cache.is_a?(Proc) config.cache.call(nil) elsif (config.cache.is_a?(String) || config.cache.is_a?(Pathname)) && File.exist?(file_path) open(file_path).read end end
Get remote content and store in cache @return [String] text content
# File lib/macker.rb, line 358 def read_from_url text = open_url.force_encoding(Encoding::UTF_8) store_in_cache(text) if text && config.cache text end
Store the provided text data by calling the proc method provided for the cache, or write to the cache file.
@example
store_in_cache("E0-43-DB (hex) Shenzhen ViewAt Technology Co.,Ltd. E043DB (base 16) Shenzhen ViewAt Technology Co.,Ltd. 9A,Microprofit,6th Gaoxin South... shenzhen guangdong 518057 CN ")
@param text [String] text content @return [Integer] normally 0
# File lib/macker.rb, line 325 def store_in_cache(text) if config.cache.is_a?(Proc) config.cache.call(text) elsif config.cache.is_a?(String) || config.cache.is_a?(Pathname) write_to_file(text) end end
Get vendor list with different strategies. Parse and read in the content.
@example
vendor_list(true) vendor_list
@param straight [Boolean] true for straight, default is careful @return [Hash] vendor list with all base16 MAC prefixes as keys
# File lib/macker.rb, line 271 def vendor_list(straight = false) raw_vendor_list = if straight raw_vendor_list_straight else raw_vendor_list_careful end vendor_list = raw_vendor_list.gsub(/\r\n/, "\n").gsub(/\t+/, "\t").split(/\n\n/) vendor_list[1..-1].each do |vendor| base16_fields = vendor.strip.split("\n")[1].split("\t") mac_prefix = Address.new(base16_fields[0].strip[0..5]).prefix address = vendor.strip.delete("\t").split("\n") iso_code = address[-1].strip next unless @prefix_table[mac_prefix].nil? @prefix_table[mac_prefix] = add_vendor(base16_fields, address, iso_code) end @iso_code_table = hash_invert(@prefix_table, :iso_code) @vendor_table = hash_invert(@prefix_table, :name) @prefix_table end
Writes content to file cache @param text [String] text content @return [Integer] normally 0
# File lib/macker.rb, line 336 def write_to_file(text) open(file_path, 'w') do |f| f.write(text) end file_update rescue Errno::ENOENT raise InvalidCache end
Private Instance Methods
Perform vendor hash @param base16_fields [Array] preifx and name field line @param address [Array] address lines @param iso_code [String] last line, iso code @return [Hash] new vendor
# File lib/macker.rb, line 417 def add_vendor(base16_fields, address, iso_code) { name: base16_fields[-1] .strip .gsub(/\s+/, ' ') .gsub(/[,;](?![\s])/, ', ') .gsub(/[,;]+$/, '') .sub(/^./, &:upcase), address: address[2..-1].map { |a| a .strip .gsub(/\s+/, ' ') .gsub(/[,;](?![\s])/, ', ') .gsub(/[,;]+$/, '') .sub(/^./, &:upcase) }, iso_code: iso_code.length == 2 ? iso_code.upcase : nil } end
Invert hash with given key to use. @param hash [Hash] source hash @param new_key [Symbol] key for the inverted hash @return [Hash] inverted hash
# File lib/macker.rb, line 408 def hash_invert(hash, new_key) hash.each_with_object({}) { |(key, value), out| (out[value[new_key]] ||= []) << value.merge(prefix: key) } end