Commit 4535b111 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #17293: uuid.getnode() now determines MAC address on AIX using netstat.

Based on patch by Aivars Kalvāns.
parent 7a025823
...@@ -307,6 +307,24 @@ class TestUUID(unittest.TestCase): ...@@ -307,6 +307,24 @@ class TestUUID(unittest.TestCase):
if node is not None: if node is not None:
self.check_node(node, 'ifconfig') self.check_node(node, 'ifconfig')
@unittest.skipUnless(os.name == 'posix', 'requires Posix')
def test_arp_getnode(self):
node = uuid._arp_getnode()
if node is not None:
self.check_node(node, 'arp')
@unittest.skipUnless(os.name == 'posix', 'requires Posix')
def test_lanscan_getnode(self):
node = uuid._lanscan_getnode()
if node is not None:
self.check_node(node, 'lanscan')
@unittest.skipUnless(os.name == 'posix', 'requires Posix')
def test_netstat_getnode(self):
node = uuid._netstat_getnode()
if node is not None:
self.check_node(node, 'netstat')
@unittest.skipUnless(os.name == 'nt', 'requires Windows') @unittest.skipUnless(os.name == 'nt', 'requires Windows')
def test_ipconfig_getnode(self): def test_ipconfig_getnode(self):
node = uuid._ipconfig_getnode() node = uuid._ipconfig_getnode()
......
...@@ -291,7 +291,7 @@ class UUID(object): ...@@ -291,7 +291,7 @@ class UUID(object):
version = property(get_version) version = property(get_version)
def _find_mac(command, args, hw_identifiers, get_index): def _popen(command, args):
import os import os
path = os.environ.get("PATH", os.defpath).split(os.pathsep) path = os.environ.get("PATH", os.defpath).split(os.pathsep)
path.extend(('/sbin', '/usr/sbin')) path.extend(('/sbin', '/usr/sbin'))
...@@ -303,19 +303,27 @@ def _find_mac(command, args, hw_identifiers, get_index): ...@@ -303,19 +303,27 @@ def _find_mac(command, args, hw_identifiers, get_index):
break break
else: else:
return None return None
# LC_ALL to ensure English output, 2>/dev/null to prevent output on
# stderr (Note: we don't have an example where the words we search for
# are actually localized, but in theory some system could do so.)
cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args)
return os.popen(cmd)
def _find_mac(command, args, hw_identifiers, get_index):
try: try:
# LC_ALL to ensure English output, 2>/dev/null to pipe = _popen(command, args)
# prevent output on stderr if not pipe:
cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args) return
with os.popen(cmd) as pipe: with pipe:
for line in pipe: for line in pipe:
words = line.lower().split() words = line.lower().rstrip().split()
for i in range(len(words)): for i in range(len(words)):
if words[i] in hw_identifiers: if words[i] in hw_identifiers:
try: try:
return int( word = words[get_index(i)]
words[get_index(i)].replace(':', ''), 16) mac = int(word.replace(':', ''), 16)
if mac:
return mac
except (ValueError, IndexError): except (ValueError, IndexError):
# Virtual interfaces, such as those provided by # Virtual interfaces, such as those provided by
# VPNs, do not have a colon-delimited MAC address # VPNs, do not have a colon-delimited MAC address
...@@ -328,27 +336,50 @@ def _find_mac(command, args, hw_identifiers, get_index): ...@@ -328,27 +336,50 @@ def _find_mac(command, args, hw_identifiers, get_index):
def _ifconfig_getnode(): def _ifconfig_getnode():
"""Get the hardware address on Unix by running ifconfig.""" """Get the hardware address on Unix by running ifconfig."""
# This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes. # This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.
for args in ('', '-a', '-av'): for args in ('', '-a', '-av'):
mac = _find_mac('ifconfig', args, ['hwaddr', 'ether'], lambda i: i+1) mac = _find_mac('ifconfig', args, ['hwaddr', 'ether'], lambda i: i+1)
if mac: if mac:
return mac return mac
import socket def _arp_getnode():
"""Get the hardware address on Unix by running arp."""
import os, socket
ip_addr = socket.gethostbyname(socket.gethostname()) ip_addr = socket.gethostbyname(socket.gethostname())
# Try getting the MAC addr from arp based on our IP address (Solaris). # Try getting the MAC addr from arp based on our IP address (Solaris).
mac = _find_mac('arp', '-an', [ip_addr], lambda i: -1) return _find_mac('arp', '-an', [ip_addr], lambda i: -1)
if mac:
return mac
def _lanscan_getnode():
"""Get the hardware address on Unix by running lanscan."""
# This might work on HP-UX. # This might work on HP-UX.
mac = _find_mac('lanscan', '-ai', ['lan0'], lambda i: 0) return _find_mac('lanscan', '-ai', ['lan0'], lambda i: 0)
def _netstat_getnode():
"""Get the hardware address on Unix by running netstat."""
# This might work on AIX, Tru64 UNIX and presumably on IRIX.
try:
pipe = _popen('netstat', '-ia')
if not pipe:
return
with pipe:
words = pipe.readline().rstrip().split()
try:
i = words.index('Address')
except ValueError:
return
for line in pipe:
try:
words = line.rstrip().split()
word = words[i]
if len(word) == 17 and word.count(':') == 5:
mac = int(word.replace(':', ''), 16)
if mac: if mac:
return mac return mac
except (ValueError, IndexError):
return None pass
except OSError:
pass
def _ipconfig_getnode(): def _ipconfig_getnode():
"""Get the hardware address on Windows by running ipconfig.exe.""" """Get the hardware address on Windows by running ipconfig.exe."""
...@@ -488,7 +519,8 @@ def getnode(): ...@@ -488,7 +519,8 @@ def getnode():
if sys.platform == 'win32': if sys.platform == 'win32':
getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
else: else:
getters = [_unixdll_getnode, _ifconfig_getnode] getters = [_unixdll_getnode, _ifconfig_getnode, _arp_getnode,
_lanscan_getnode, _netstat_getnode]
for getter in getters + [_random_getnode]: for getter in getters + [_random_getnode]:
try: try:
......
...@@ -37,6 +37,9 @@ Core and Builtins ...@@ -37,6 +37,9 @@ Core and Builtins
Library Library
------- -------
- Issue #17293: uuid.getnode() now determines MAC address on AIX using netstat.
Based on patch by Aivars Kalvāns.
- Issue #22769: Fixed ttk.Treeview.tag_has() when called without arguments. - Issue #22769: Fixed ttk.Treeview.tag_has() when called without arguments.
- Issue #22787: Allow the keyfile argument of SSLContext.load_cert_chain to be - Issue #22787: Allow the keyfile argument of SSLContext.load_cert_chain to be
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment