louis 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/.yardopts +1 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +661 -0
- data/README.md +58 -0
- data/Rakefile +14 -0
- data/data/mac_oui_manuf.txt +25491 -0
- data/lib/louis/version.rb +3 -0
- data/lib/louis.rb +90 -0
- data/louis.gemspec +30 -0
- data/spec/louis_spec.rb +7 -0
- data/spec/spec_helper.rb +40 -0
- metadata +163 -0
data/lib/louis.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require "louis/version"
|
2
|
+
|
3
|
+
module Louis
|
4
|
+
OUI_FILE = File.expand_path(File.join(File.dirname(__FILE__), '..', 'data',
|
5
|
+
'mac_oui_manuf.txt'))
|
6
|
+
|
7
|
+
OUI_FORMAT_REGEX = /^(?<prefix>[0-9a-fA-F:\-]+)(\/(?<mask>(\d+)))?\s+(?<short_vendor>\S+)(\s+# (?<long_vendor>.+))?$/
|
8
|
+
|
9
|
+
# Calculate the bit mask for testing whether or not a mac_prefix matches.
|
10
|
+
# This returns an integer with the upper X bits set where X is the mask
|
11
|
+
# length.
|
12
|
+
#
|
13
|
+
# @param [String] prefix
|
14
|
+
# @param [String] mask
|
15
|
+
# @return [Fixnum]
|
16
|
+
def self.calculate_mask(prefix, mask)
|
17
|
+
mask_base = mask.nil? ? (clean_mac(prefix).length * 4) : mask.to_i
|
18
|
+
(2 ** 48 - 1) - (2 ** (48 - mask_base) - 1)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the hex representing a full or partial MAC address with the
|
22
|
+
# 'connecting' characters removed. Does nothing to ensure length.
|
23
|
+
#
|
24
|
+
# @param [String] mac
|
25
|
+
# @return [String]
|
26
|
+
def self.clean_mac(mac)
|
27
|
+
mac.gsub(/[:-]/, '')
|
28
|
+
end
|
29
|
+
|
30
|
+
# Search through the OUI lookup table and return all the entries in the
|
31
|
+
# lookup table that match the provided MAC.
|
32
|
+
#
|
33
|
+
# @param [String] mac
|
34
|
+
# @return [Array<Hash<String=>Object>>]
|
35
|
+
def self.find_matches(mac)
|
36
|
+
@lookup_table.select { |m| mac_matches_prefix?(mac, m['prefix'], m['mask']) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Loads the lookup table, parsing out the uncommented non-blank lines into
|
40
|
+
# objects we can compare MACs against to find their vendor.
|
41
|
+
def self.load_lookup_table
|
42
|
+
return if @lookup_table
|
43
|
+
@lookup_table = []
|
44
|
+
|
45
|
+
File.open(OUI_FILE).each_line do |line|
|
46
|
+
if (matches = OUI_FORMAT_REGEX.match(line))
|
47
|
+
result = Hash[matches.names.zip(matches.captures)]
|
48
|
+
|
49
|
+
@lookup_table.push({
|
50
|
+
'mask' => calculate_mask(result['prefix'], result['mask']),
|
51
|
+
'prefix' => mac_to_num(result['prefix']),
|
52
|
+
'short_vendor' => result['short_vendor'],
|
53
|
+
'long_vendor' => result['long_vendor']
|
54
|
+
})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the name of the vendor that has the most specific prefix
|
60
|
+
# available in the OUI table or failing any matches will return "Unknown".
|
61
|
+
#
|
62
|
+
# @param [String] mac
|
63
|
+
# @return [String]
|
64
|
+
def self.lookup(mac)
|
65
|
+
load_lookup_table
|
66
|
+
|
67
|
+
o = find_matches(mac).sort_by { |m| m['prefix'] }.first
|
68
|
+
o ||= {'long_vendor' => 'Unknown', 'short_vendor' => 'Unknown'}
|
69
|
+
|
70
|
+
o.select { |k,_| %w(long_vendor short_vendor).include?(k) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Checks to see whether or not the MAC address has the provided prefix.
|
74
|
+
#
|
75
|
+
# @param [String] mac
|
76
|
+
# @param [Fixnum] prefix
|
77
|
+
# @param [Fixnum] mask
|
78
|
+
# @return [Boolean]
|
79
|
+
def self.mac_matches_prefix?(mac, prefix, mask)
|
80
|
+
(mac_to_num(mac) & mask) == prefix
|
81
|
+
end
|
82
|
+
|
83
|
+
# Converts a hexidecimal version of a full or partial (prefix) MAC address
|
84
|
+
# into it's integer representation.
|
85
|
+
#
|
86
|
+
# @param [String] mac
|
87
|
+
def self.mac_to_num(mac)
|
88
|
+
clean_mac(mac).ljust(12, '0').to_i(16)
|
89
|
+
end
|
90
|
+
end
|
data/louis.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'louis/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = "louis"
|
10
|
+
spec.version = Louis::VERSION
|
11
|
+
spec.authors = ["Sam Stelfox"]
|
12
|
+
spec.email = ["sstelfox@bedroomprogrammers.net"]
|
13
|
+
spec.summary = %q{Library for looking up the the vendor associated with a MAC address.}
|
14
|
+
spec.description = %q{There is a public registry maintained by the IANA that is required to be used by all vendors operating in certains spaces. Ethernet, Bluetooth, and Wireless device manufacturers are all assigned unique prefixes. This database is available publicly online and can be used to identify the manufacturer of these devices. This library provides an easy mechanism to perform these lookups.}
|
15
|
+
spec.homepage = ""
|
16
|
+
spec.license = "AGPLv3"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rdoc"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "simplecov"
|
29
|
+
spec.add_development_dependency "yard"
|
30
|
+
end
|
data/spec/louis_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
base_path = File.expand_path(File.join(File.basename(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(base_path) unless $LOAD_PATH.include?(base_path)
|
3
|
+
|
4
|
+
require 'rspec'
|
5
|
+
require 'simplecov'
|
6
|
+
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
8
|
+
SimpleCov.add_filter "/spec/"
|
9
|
+
SimpleCov.add_filter do |source_file|
|
10
|
+
source_file.lines.count < 3
|
11
|
+
end
|
12
|
+
|
13
|
+
SimpleCov.start
|
14
|
+
|
15
|
+
require 'louis'
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
config.expect_with :rspec do |expectations|
|
19
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
20
|
+
end
|
21
|
+
|
22
|
+
config.mock_with :rspec do |mocks|
|
23
|
+
mocks.verify_partial_doubles = true
|
24
|
+
mocks.verify_doubled_constant_names = true
|
25
|
+
end
|
26
|
+
|
27
|
+
config.filter_run :focus
|
28
|
+
config.run_all_when_everything_filtered = true
|
29
|
+
config.disable_monkey_patching!
|
30
|
+
config.warnings = true
|
31
|
+
|
32
|
+
if config.files_to_run.one?
|
33
|
+
config.default_formatter = 'doc'
|
34
|
+
end
|
35
|
+
|
36
|
+
config.profile_examples = 3
|
37
|
+
config.order = :random
|
38
|
+
|
39
|
+
Kernel.srand(config.seed)
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: louis
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Stelfox
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rdoc
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: There is a public registry maintained by the IANA that is required to
|
112
|
+
be used by all vendors operating in certains spaces. Ethernet, Bluetooth, and Wireless
|
113
|
+
device manufacturers are all assigned unique prefixes. This database is available
|
114
|
+
publicly online and can be used to identify the manufacturer of these devices. This
|
115
|
+
library provides an easy mechanism to perform these lookups.
|
116
|
+
email:
|
117
|
+
- sstelfox@bedroomprogrammers.net
|
118
|
+
executables: []
|
119
|
+
extensions: []
|
120
|
+
extra_rdoc_files: []
|
121
|
+
files:
|
122
|
+
- ".gitignore"
|
123
|
+
- ".rspec"
|
124
|
+
- ".travis.yml"
|
125
|
+
- ".yardopts"
|
126
|
+
- Gemfile
|
127
|
+
- LICENSE.txt
|
128
|
+
- README.md
|
129
|
+
- Rakefile
|
130
|
+
- data/mac_oui_manuf.txt
|
131
|
+
- lib/louis.rb
|
132
|
+
- lib/louis/version.rb
|
133
|
+
- louis.gemspec
|
134
|
+
- spec/louis_spec.rb
|
135
|
+
- spec/spec_helper.rb
|
136
|
+
homepage: ''
|
137
|
+
licenses:
|
138
|
+
- AGPLv3
|
139
|
+
metadata: {}
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubyforge_project:
|
156
|
+
rubygems_version: 2.4.3
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: Library for looking up the the vendor associated with a MAC address.
|
160
|
+
test_files:
|
161
|
+
- spec/louis_spec.rb
|
162
|
+
- spec/spec_helper.rb
|
163
|
+
has_rdoc:
|