This is a small tool using Tinyscript for finding the organization associated with a given OUI or MAC address.
$ pip install tinyscript
$ tsm install oui
This is a small tool using Tinyscript for finding the organization associated with a given OUI or MAC address.
$ pip install tinyscript
$ tsm install oui
#!/usr/bin/python3 | |
from tinyscript import * | |
from tinyscript.helpers import Path, TempPath | |
__script__ = "OUI/MAC Organization Finder" | |
__author__ = "Alexandre D'Hondt" | |
__email__ = "[email protected]" | |
__version__ = "1.2" | |
__copyright__ = ("A. D'Hondt", 2021) | |
__license__ = "gpl-3.0" | |
__doc__ = """ | |
This tool returns the organization name associated to an input OUI or MAC address. | |
""" | |
__examples__ = ["50:e0:85:01:23:45", "9C-3E-53-01-23-45 -u"] | |
PATH_OUI_DB = Path(__file__).dirname.joinpath("oui.tsv") | |
PATH_XTRA_OUI_DB = Path(__file__).dirname.joinpath("oui-extra.tsv") | |
TEMP_OUI_DB = TempPath().tempfile() | |
URL_OUI_DB = "https://standards-oui.ieee.org/" | |
def add_extra_oui(entry): | |
new_oui, new_org = list(map(lambda x: x.strip(), entry.split("=", 1))) | |
with PATH_XTRA_OUI_DB.open('a+') as f: | |
f.seek(0) | |
for l in f: | |
oui, org = l.strip().split("\t", 1) | |
if new_oui == oui: | |
logger.warning("OUI '%s' already exists (%s)" % (oui, org)) | |
return | |
f.write("%s\t%s" % (new_oui, new_org)) | |
logger.info("OUI '%s' added (%s)" % (new_oui, new_org)) | |
def eui_or_oui(oui): | |
if re.match("(?i)[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", oui) or \ | |
re.match("(?i)[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2})$", oui): | |
oui = re.sub(r"[-_:]", "", oui.upper())[:6] | |
logger.debug(f"Target OUI: {oui}") | |
org = open_db().get(oui) | |
if org: | |
print(org) | |
else: | |
logger.failure("OUI not found") | |
return | |
raise ValueError("Not an OUI or MAC address") | |
def open_db(): | |
if not PATH_OUI_DB.exists(): | |
update_db(True) | |
logger.debug("Opening OUI database...") | |
db = {} | |
with PATH_OUI_DB.open('rt') as f: | |
for l in f: | |
oui, org = l.split("\t", 1) | |
db[oui] = org.rstrip("\n") | |
if PATH_XTRA_OUI_DB.exists(): | |
with PATH_XTRA_OUI_DB.open('rt') as f: | |
for l in f: | |
if l.strip().startswith("#") or l.strip() == "": | |
continue | |
oui, org = l.split("\t", 1) | |
db[oui] = org.rstrip("\n") | |
return db | |
def search_ouis(pattern): | |
ouis = [] | |
for oui, org in open_db().items(): | |
if re.search(pattern, org, re.I): | |
ouis.append((oui, org)) | |
for oui in sorted(ouis): | |
print("%s\t%s" % oui) | |
def update_db(new=False): | |
logger.debug("%s OUI database..." % ["Updating", "Downloading"][new]) | |
data = "" | |
with requests.get(URL_OUI_DB, headers={'User-Agent': "OUI Lookup Tool"}, stream=True) as resp: | |
resp.raise_for_status() | |
with TEMP_OUI_DB.open('wb') as f: | |
for chunk in resp.iter_content(chunk_size=8192): | |
f.write(chunk) | |
with TEMP_OUI_DB.open('rt') as f: | |
for l in f: | |
l = l.strip() | |
m = re.match(r"([0-9A-F]{6})\s+\(base 16\)\t+(.*)$", l) | |
if m: | |
data += "%s\t%s\n" % (m.group(1), m.group(2)) | |
with PATH_OUI_DB.open('wt') as f: | |
f.write(data.rstrip("\n")) | |
if __name__ == '__main__': | |
parser.add_argument("oui", nargs="?", type=eui_or_oui, help="OUI or complete MAC address") | |
parser.add_argument("-a", "--add", type=add_extra_oui, help="add a custom OUI with its organization name", | |
note="format is \"XXXXXX=orgname\" (with XXXXXX the OUI without separator)") | |
parser.add_argument("-s", "--search", type=search_ouis, help="search for OUIs given a pattern of organization") | |
parser.add_argument("-u", "--update", dest="update", action="store_true", help="update the OUI database", | |
note="DB source is %s" % URL_OUI_DB) | |
initialize(noargs_action="usage") | |
if args.update is True: | |
update_db() |