oui-offline 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f0186bcd1fbaf8902a189bfa41da73427e0b7df7
4
+ data.tar.gz: b553fb418502ad5d9028eafdd24d308c64814b4e
5
+ SHA512:
6
+ metadata.gz: 687a5cf5e2e9bc2d6de4613be2d44444bab75cf4535bb7072997fed38fb1dac71fbd8970918457dbec2c6973dba32423e7d7193a54a8429e0393abaf042cf5d4
7
+ data.tar.gz: 09313a9cf06a123afa7e5bba35ea40968f8761dc0255ff8d1d51462edc0372b502bc6f75ae13601c94e71a5fa40efddc81a047afa2bf86c82c736862444ea878
checksums.yaml.gz.sig ADDED
Binary file
data.tar.gz.sig ADDED
Binary file
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # OUI (Organizationally Unique Identifiers)
2
+ ## Usage
3
+
4
+ >OUI.find 'AA-BB-CC'
5
+ => nil
6
+ > OUI.find '00:0c:85'
7
+ => {
8
+ :id => 3205,
9
+ :organization => "CISCO SYSTEMS, INC.",
10
+ :address1 => "170 W. TASMAN DRIVE",
11
+ :address2 => "M/S SJA-2",
12
+ :address3 => "SAN JOSE CA 95134-1706",
13
+ :country => "UNITED STATES"
14
+ }
15
+
16
+ ## Installation
17
+ ### Bundler Installation
18
+
19
+ ```ruby
20
+ gem 'oui', git: 'https://github.com/steakknife/oui.git'
21
+ ```
22
+
23
+ ### Manual Installation
24
+
25
+ cd ${TMP_DIR-/tmp}
26
+ git clone https://github.com/steakknife/oui
27
+ cd oui
28
+ gem build *.gemspec
29
+ gem install *.gem
30
+
31
+
32
+ ## Data
33
+
34
+ Database sourced from the public IEEE list, but it can be rebuilt anytime by running `bin/update_db` or `OUI.update_db`
35
+
36
+ ## License
37
+
38
+ MIT
data/bin/oui ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ lib_dir = File.expand_path('../../lib', __FILE__)
3
+ $:.unshift lib_dir unless $:.include? lib_dir
4
+ require 'oui'
5
+
6
+ VERBOSE = ARGV.delete '-v'
7
+ case ARGV.shift
8
+ when 'lookup'
9
+ ARGV.map do |mac|
10
+ if r = OUI.find(mac)
11
+ puts (VERBOSE) ? r : r[:organization]
12
+ end
13
+ end
14
+
15
+ when 'update'
16
+ OUI.update_db
17
+
18
+ else
19
+ $stderr.puts <<-EOS
20
+ Usage: oui lookup oui+ # get corp name, oui in 24-bit oui in hex format
21
+ oui update # update oui db from ieee.org
22
+ EOS
23
+ exit 1
24
+ end
25
+
@@ -0,0 +1,7 @@
1
+ [
2
+ {"id": "00:D3:10", "organization": "Dell, Inc., for Dell Compellent Storage products"},
3
+ {"id": "0A:BB:CF", "organization": "Apple, Inc., Bluetooth PAN"},
4
+ {"id": "6A:5B:35", "organization": "Apple, Inc., Bridge and bond devices (virtual)"},
5
+ {"id": "CA:6F:1D", "organization": "Apple, Inc., iPhone USB"},
6
+ {"id": "D2:00:1A", "organization": "Apple, Inc., Thunderbolt"}
7
+ ]
data/db/oui.sqlite3 ADDED
Binary file
data/lib/oui.rb ADDED
@@ -0,0 +1,238 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+ require 'open-uri'
4
+ require 'sequel'
5
+
6
+ # Organizationally Unique Identifier
7
+ module OUI
8
+ extend self
9
+
10
+ private
11
+
12
+ TABLE = :ouis
13
+ # import data/oui.txt instead of fetching remotely
14
+ IMPORT_LOCAL_TXT_FILE = false
15
+ # use in-memory instead of persistent file
16
+ IN_MEMORY_ONLY = false
17
+ LOCAL_DB = File.expand_path('../../db/oui.sqlite3', __FILE__)
18
+ LOCAL_MANUAL_FILE = File.expand_path('../../data/oui-manual.json', __FILE__)
19
+ if IMPORT_LOCAL_TXT_FILE
20
+ OUI_URL = File.join('data', 'oui.txt')
21
+ else
22
+ OUI_URL = 'http://standards.ieee.org/develop/regauth/oui/oui.txt'
23
+ end
24
+ FIRST_LINE_INDEX = 7
25
+ EXPECTED_DUPLICATES = [0x0001C8, 0x080030]
26
+ LINE_LENGTH = 22
27
+
28
+ public
29
+
30
+ # @param oui [String,Integer] hex or numeric OUI to find
31
+ # @return [Hash,nil]
32
+ def find(oui)
33
+ update_db unless table? && table.count > 0
34
+ table.where(id: oui_to_i(oui)).first
35
+ end
36
+
37
+ # Converts an OUI string to an integer of equal value
38
+ # @param oui [String,Integer] MAC OUI in hexadecimal formats
39
+ # hhhh.hh, hh:hh:hh, hh-hh-hh or hhhhhh
40
+ # @return [Integer] numeric representation of oui
41
+ def oui_to_i(oui)
42
+ return oui if oui.is_a? Integer
43
+ oui = oui.strip.gsub(/[:\- .]/, '')
44
+ return unless oui =~ /[[:xdigit:]]{6}/
45
+ oui.to_i(16)
46
+ end
47
+
48
+ # Release backend resources
49
+ def close_db
50
+ @db = nil
51
+ end
52
+
53
+ # Update database from fetched URL
54
+ # @return [Integer] number of unique records loaded
55
+ def update_db
56
+ ## Sequel
57
+ drop_table
58
+ create_table
59
+ db.transaction do
60
+ table.delete_sql
61
+ install_manual
62
+ install_updates
63
+ end
64
+ ## AR
65
+ # self.transaction do
66
+ # self.delete_all
67
+ # self.install_manual
68
+ # self.install_updates
69
+ # end
70
+ end
71
+
72
+
73
+ private
74
+
75
+ HEX_BEGINNING_REGEX = /\A[[:space:]]{2}[[:xdigit:]]{2}-/
76
+ ERASE_LINE = "\b" * LINE_LENGTH
77
+ BLANK_LINE = ' ' * LINE_LENGTH
78
+
79
+ def connect_file_db(f)
80
+ FileUtils.mkdir_p(File.dirname(f))
81
+ Sequel.sqlite(f)
82
+ end
83
+
84
+ def connect_db
85
+ if IN_MEMORY_ONLY
86
+ Sequel.sqlite # in-memory sqlite database
87
+ else
88
+ $stderr.puts "Connecting to db file #{LOCAL_DB}"
89
+ connect_file_db LOCAL_DB
90
+ end
91
+ end
92
+
93
+ def db
94
+ @db ||= connect_db
95
+ end
96
+
97
+ def table?
98
+ db.tables.include? TABLE
99
+ end
100
+
101
+ def table
102
+ db[TABLE]
103
+ end
104
+
105
+ def drop_table
106
+ db.drop_table(TABLE) if table?
107
+ end
108
+
109
+ def create_table
110
+ db.create_table TABLE do
111
+ primary_key :id
112
+ String :organization, null: false
113
+ String :address1
114
+ String :address2
115
+ String :address3
116
+ String :country
117
+ index :id
118
+ end
119
+ end
120
+
121
+ # @param lines [Array<String>]
122
+ # @return [Array<Array<String>>]
123
+ def parse_lines_into_groups(lines)
124
+ grps, curgrp = [], []
125
+ lines[FIRST_LINE_INDEX..-1].each do |line|
126
+ if !curgrp.empty? && line =~ HEX_BEGINNING_REGEX
127
+ grps << curgrp
128
+ curgrp = []
129
+ end
130
+ line.strip!
131
+ next if line.empty?
132
+ curgrp << line
133
+ end
134
+ grps << curgrp # add last group and return
135
+ end
136
+
137
+ # @param g [Array<String>]
138
+ def parse_org(g)
139
+ g[0].split("\t").last
140
+ end
141
+
142
+ # @param g [Array<String>]
143
+ def parse_id(g)
144
+ g[1].split(' ')[0].to_i(16)
145
+ end
146
+
147
+ MISSING_COUNTRIES = [
148
+ 0x000052,
149
+ 0x002142,
150
+ 0x684CA8
151
+ ]
152
+
153
+ COUNTRY_OVERRIDES = {
154
+ 0x000052 => 'UNITED STATES',
155
+ 0x002142 => 'SERBIA',
156
+ 0x684CA8 => 'CHINA'
157
+ }
158
+
159
+ def parse_address1(g)
160
+ g[2] if g.length >= 4
161
+ end
162
+
163
+ def parse_address2(g, id)
164
+ g[3] if g.length >= 5 || MISSING_COUNTRIES.include?(id)
165
+ end
166
+
167
+ def parse_address3(g)
168
+ g[4] if g.length == 6
169
+ end
170
+
171
+ # @param g [Array<String>]
172
+ # @param id [Integer]
173
+ def parse_country(g, id)
174
+ c = COUNTRY_OVERRIDES[id] || g[-1]
175
+ c if c !~ /\A\h/
176
+ end
177
+
178
+ # @param g [Array<String>]
179
+ def create_from_line_group(g)
180
+ n = g.length
181
+ raise ArgumentError, "Parse error lines: #{n}" unless (2..6).include? n
182
+ id = parse_id(g)
183
+ create_unless_present(id: id, organization: parse_org(g),
184
+ address1: parse_address1(g),
185
+ address2: parse_address2(g, id),
186
+ address3: parse_address3(g),
187
+ country: parse_country(g, id))
188
+ end
189
+
190
+ def fetch
191
+ $stderr.puts "Fetching #{OUI_URL}"
192
+ open(OUI_URL).read
193
+ end
194
+
195
+ def install_manual
196
+ JSON.load(File.read(LOCAL_MANUAL_FILE)).each do |g|
197
+ # convert keys to symbols
198
+ g = g.map { |k, v| [k.to_sym, v] }
199
+ g = Hash[g]
200
+ # convert OUI octets to integers
201
+ g[:id] = oui_to_i(g[:id])
202
+ create_unless_present(g)
203
+ end
204
+ rescue Errno::ENOENT
205
+ end
206
+
207
+ def install_updates
208
+ lines = fetch.split("\n").map { |x| x.sub(/\r$/, '') }
209
+ parse_lines_into_groups(lines).each_with_index do |group, idx|
210
+ create_from_line_group(group)
211
+ $stderr.print "#{ERASE_LINE}Created records #{idx}" if idx % 1000 == 0
212
+ end.count
213
+ $stderr.puts "#{ERASE_LINE}#{BLANK_LINE}"
214
+ end
215
+
216
+ # Expected duplicates are 00-01-C8 (2x) and 08-00-30 (3x)
217
+ def expected_duplicate?(id)
218
+ EXPECTED_DUPLICATES.include? id
219
+ end
220
+
221
+ # Has a particular id been added yet?
222
+ def added
223
+ @added ||= {}
224
+ end
225
+
226
+ def create_unless_present(opts)
227
+ id = opts[:id]
228
+ if added[id]
229
+ unless expected_duplicate? id
230
+ $stderr.puts "OUI unexpected duplicate #{opts}"
231
+ end
232
+ else
233
+ table.insert(opts)
234
+ # self.create! opts
235
+ added[id] = true
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,31 @@
1
+ Gem::Specification::new do |s|
2
+ s.name = 'oui-offline'
3
+ s.version = '1.0.0'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.summary = 'Organizationally Unique Idenitfiers (OUI)'
6
+ s.description = 'Organizationally Unique Idenitfiers (OUI) offline database'
7
+ s.license = 'MIT'
8
+
9
+ s.files =
10
+ ['Gemfile',
11
+ 'README.md',
12
+ 'bin/oui',
13
+ 'data/oui-manual.json',
14
+ 'db/oui.sqlite3',
15
+ 'lib/oui.rb',
16
+ 'oui-offline.gemspec',
17
+ ]
18
+ s.required_ruby_version = '>= 1.9.3'
19
+
20
+ s.require_path = 'lib'
21
+ s.executables << 'oui'
22
+
23
+ s.author = 'Barry Allard'
24
+ s.email = 'barry.allard@gmail.com'
25
+ s.homepage = 'https://github.com/steakknife/oui'
26
+ s.post_install_message = 'Oui!'
27
+
28
+ s.add_dependency 'sqlite3', '>= 1.3', '< 2'
29
+ s.add_dependency 'sequel', '>= 4', '< 5'
30
+ end
31
+ .tap {|gem| pk = File.expand_path(File.join('~/.keys', 'gem-private_key.pem')); gem.signing_key = pk if File.exist? pk; gem.cert_chain = ['gem-public_cert.pem']} # pressed firmly by waxseal
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oui-offline
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Barry Allard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ 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
42
+ t/HWh1yhriCUe/y8+De8M87btOs=
43
+ -----END CERTIFICATE-----
44
+ date: 2015-01-23 00:00:00.000000000 Z
45
+ dependencies:
46
+ - !ruby/object:Gem::Dependency
47
+ name: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '1.3'
53
+ - - "<"
54
+ - !ruby/object:Gem::Version
55
+ version: '2'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '1.3'
63
+ - - "<"
64
+ - !ruby/object:Gem::Version
65
+ version: '2'
66
+ - !ruby/object:Gem::Dependency
67
+ name: sequel
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '4'
73
+ - - "<"
74
+ - !ruby/object:Gem::Version
75
+ version: '5'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '4'
83
+ - - "<"
84
+ - !ruby/object:Gem::Version
85
+ version: '5'
86
+ description: Organizationally Unique Idenitfiers (OUI) offline database
87
+ email: barry.allard@gmail.com
88
+ executables:
89
+ - oui
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - Gemfile
94
+ - README.md
95
+ - bin/oui
96
+ - data/oui-manual.json
97
+ - db/oui.sqlite3
98
+ - lib/oui.rb
99
+ - oui-offline.gemspec
100
+ homepage: https://github.com/steakknife/oui
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message: Oui!
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.9.3
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.4.5
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Organizationally Unique Idenitfiers (OUI)
124
+ test_files: []
125
+ has_rdoc:
metadata.gz.sig ADDED
Binary file