Skip to content

gh-101531: Handle omitted leading zeroes in mac address on Darwin (#1… #105004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 50 additions & 24 deletions Lib/test/test_uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,29 +781,14 @@ class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase):
class BaseTestInternals:
_uuid = py_uuid

def check_parse_mac(self, aix):
if not aix:
patch = mock.patch.multiple(self.uuid,
_MAC_DELIM=b':',
_MAC_OMITS_LEADING_ZEROES=False)
else:
patch = mock.patch.multiple(self.uuid,
_MAC_DELIM=b'.',
_MAC_OMITS_LEADING_ZEROES=True)
def check_parse_mac(self, valid_macs, delim=b':', omits_leading_zeros=False):
patch = mock.patch.multiple(self.uuid,
_MAC_DELIM=delim,
_MAC_OMITS_LEADING_ZEROES=omits_leading_zeros)

with patch:
# Valid MAC addresses
if not aix:
tests = (
(b'52:54:00:9d:0e:67', 0x5254009d0e67),
(b'12:34:56:78:90:ab', 0x1234567890ab),
)
else:
# AIX format
tests = (
(b'fe.ad.c.1.23.4', 0xfead0c012304),
)
for mac, expected in tests:
for mac, expected in valid_macs:
self.assertEqual(self.uuid._parse_mac(mac), expected)

# Invalid MAC addresses
Expand All @@ -822,16 +807,38 @@ def check_parse_mac(self, aix):
# dash separator
b'52-54-00-9d-0e-67',
):
if aix:
mac = mac.replace(b':', b'.')
if delim != b':':
mac = mac.replace(b':', delim)
with self.subTest(mac=mac):
self.assertIsNone(self.uuid._parse_mac(mac))

def test_parse_mac(self):
self.check_parse_mac(False)
self.check_parse_mac(
valid_macs=(
(b'52:54:00:9d:0e:67', 0x5254009d0e67),
(b'12:34:56:78:90:ab', 0x1234567890ab),
),
delim=b':',
omits_leading_zeros=False,
)

def test_parse_mac_aix(self):
self.check_parse_mac(True)
self.check_parse_mac(
valid_macs=(
(b'fe.ad.c.1.23.4', 0xfead0c012304),
),
delim=b'.',
omits_leading_zeros=True,
)

def test_parse_mac_macos(self):
self.check_parse_mac(
valid_macs=(
(b'1:0:5e:0:c:fb', 0x01005e000cfb),
),
delim=b':',
omits_leading_zeros=True,
)

def test_find_under_heading(self):
data = '''\
Expand Down Expand Up @@ -917,6 +924,25 @@ def test_find_mac_near_keyword(self):

self.assertEqual(mac, 0x1234567890ab)

def test_find_mac_near_keyword_macos(self):
data = '''
? (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet]
'''

with mock.patch.multiple(self.uuid,
_MAC_DELIM=b':',
_MAC_OMITS_LEADING_ZEROES=True,
_get_command_stdout=mock_get_command_stdout(data)):

mac = self.uuid._find_mac_near_keyword(
command='arp',
args='-an',
keywords=[os.fsencode('(%s)' % '224.0.0.251')],
get_word_index=lambda x: x + 2,
)

self.assertEqual(mac, 0x01005e0000fb)

def check_node(self, node, requires=None):
if requires and node is None:
self.skipTest('requires ' + requires)
Expand Down
12 changes: 8 additions & 4 deletions Lib/uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
if _AIX:
_MAC_DELIM = b'.'
_MAC_OMITS_LEADING_ZEROES = True
if sys.platform == 'darwin':
_MAC_OMITS_LEADING_ZEROES = True

RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
'reserved for NCS compatibility', 'specified in RFC 4122',
Expand Down Expand Up @@ -430,16 +432,16 @@ def _find_mac_near_keyword(command, args, keywords, get_word_index):
if words[i] in keywords:
try:
word = words[get_word_index(i)]
mac = int(word.replace(_MAC_DELIM, b''), 16)
except (ValueError, IndexError):
mac = _parse_mac(word)
except IndexError:
# Virtual interfaces, such as those provided by
# VPNs, do not have a colon-delimited MAC address
# as expected, but a 16-byte HWAddr separated by
# dashes. These should be ignored in favor of a
# real MAC address
pass
else:
if _is_universal(mac):
if mac and _is_universal(mac):
return mac
first_local_mac = first_local_mac or mac
return first_local_mac or None
Expand All @@ -456,10 +458,12 @@ def _parse_mac(word):
if len(parts) != 6:
return
if _MAC_OMITS_LEADING_ZEROES:
# (Only) on AIX the macaddr value given is not prefixed by 0, e.g.
# on AIX and darwin the macaddr value given is not prefixed by 0, e.g. on AIX
# en0 1500 link#2 fa.bc.de.f7.62.4 110854824 0 160133733 0 0
# not
# en0 1500 link#2 fa.bc.de.f7.62.04 110854824 0 160133733 0 0
# and on darwin
# ? (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet]
if not all(1 <= len(part) <= 2 for part in parts):
return
hexstr = b''.join(part.rjust(2, b'0') for part in parts)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Handle omitted leading zeroes in mac address on Darwin
Loading