Skip to content

Commit ebf6bb9

Browse files
authored
bpo-39991: Enhance uuid parser for MAC address (GH-19045)
Reject valid IPv6 addresses which doesn't contain "::" but have a length of 17 characters.
1 parent 5b1ef20 commit ebf6bb9

File tree

2 files changed

+93
-31
lines changed

2 files changed

+93
-31
lines changed

Lib/test/test_uuid.py

+52
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,58 @@ class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase):
679679
class BaseTestInternals:
680680
_uuid = py_uuid
681681

682+
def check_parse_mac(self, aix):
683+
if not aix:
684+
patch = mock.patch.multiple(self.uuid,
685+
_MAC_DELIM=b':',
686+
_MAC_OMITS_LEADING_ZEROES=False)
687+
else:
688+
patch = mock.patch.multiple(self.uuid,
689+
_MAC_DELIM=b'.',
690+
_MAC_OMITS_LEADING_ZEROES=True)
691+
692+
with patch:
693+
# Valid MAC addresses
694+
if not aix:
695+
tests = (
696+
(b'52:54:00:9d:0e:67', 0x5254009d0e67),
697+
(b'12:34:56:78:90:ab', 0x1234567890ab),
698+
)
699+
else:
700+
# AIX format
701+
tests = (
702+
(b'fe.ad.c.1.23.4', 0xfead0c012304),
703+
)
704+
for mac, expected in tests:
705+
self.assertEqual(self.uuid._parse_mac(mac), expected)
706+
707+
# Invalid MAC addresses
708+
for mac in (
709+
b'',
710+
# IPv6 addresses with same length than valid MAC address
711+
# (17 characters)
712+
b'fe80::5054:ff:fe9',
713+
b'123:2:3:4:5:6:7:8',
714+
# empty 5rd field
715+
b'52:54:00:9d::67',
716+
# only 5 fields instead of 6
717+
b'52:54:00:9d:0e'
718+
# invalid character 'x'
719+
b'52:54:00:9d:0e:6x'
720+
# dash separator
721+
b'52-54-00-9d-0e-67',
722+
):
723+
if aix:
724+
mac = mac.replace(b':', b'.')
725+
with self.subTest(mac=mac):
726+
self.assertIsNone(self.uuid._parse_mac(mac))
727+
728+
def test_parse_mac(self):
729+
self.check_parse_mac(False)
730+
731+
def test_parse_mac_aix(self):
732+
self.check_parse_mac(True)
733+
682734
def test_find_under_heading(self):
683735
data = '''\
684736
Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll

Lib/uuid.py

+41-31
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,34 @@ def _find_mac_near_keyword(command, args, keywords, get_word_index):
434434
return first_local_mac or None
435435

436436

437+
def _parse_mac(word):
438+
# Accept 'HH:HH:HH:HH:HH:HH' MAC address (ex: '52:54:00:9d:0e:67'),
439+
# but reject IPv6 address (ex: 'fe80::5054:ff:fe9' or '123:2:3:4:5:6:7:8').
440+
#
441+
# Virtual interfaces, such as those provided by VPNs, do not have a
442+
# colon-delimited MAC address as expected, but a 16-byte HWAddr separated
443+
# by dashes. These should be ignored in favor of a real MAC address
444+
parts = word.split(_MAC_DELIM)
445+
if len(parts) != 6:
446+
return
447+
if _MAC_OMITS_LEADING_ZEROES:
448+
# (Only) on AIX the macaddr value given is not prefixed by 0, e.g.
449+
# en0 1500 link#2 fa.bc.de.f7.62.4 110854824 0 160133733 0 0
450+
# not
451+
# en0 1500 link#2 fa.bc.de.f7.62.04 110854824 0 160133733 0 0
452+
if not all(1 <= len(part) <= 2 for part in parts):
453+
return
454+
hexstr = b''.join(part.rjust(2, b'0') for part in parts)
455+
else:
456+
if not all(len(part) == 2 for part in parts):
457+
return
458+
hexstr = b''.join(parts)
459+
try:
460+
return int(hexstr, 16)
461+
except ValueError:
462+
return
463+
464+
437465
def _find_mac_under_heading(command, args, heading):
438466
"""Looks for a MAC address under a heading in a command's output.
439467
@@ -453,39 +481,21 @@ def _find_mac_under_heading(command, args, heading):
453481

454482
first_local_mac = None
455483
for line in stdout:
484+
words = line.rstrip().split()
456485
try:
457-
words = line.rstrip().split()
458486
word = words[column_index]
459-
# Accept 'HH:HH:HH:HH:HH:HH' MAC address (ex: '52:54:00:9d:0e:67'),
460-
# but reject IPv6 address (ex: 'fe80::5054:ff:fe9') detected
461-
# by '::' pattern.
462-
if len(word) == 17 and b'::' not in word:
463-
mac = int(word.replace(_MAC_DELIM, b''), 16)
464-
elif _MAC_OMITS_LEADING_ZEROES:
465-
# (Only) on AIX the macaddr value given is not prefixed by 0, e.g.
466-
# en0 1500 link#2 fa.bc.de.f7.62.4 110854824 0 160133733 0 0
467-
# not
468-
# en0 1500 link#2 fa.bc.de.f7.62.04 110854824 0 160133733 0 0
469-
parts = word.split(_MAC_DELIM)
470-
if len(parts) == 6 and all(0 < len(p) <= 2 for p in parts):
471-
hexstr = b''.join(p.rjust(2, b'0') for p in parts)
472-
mac = int(hexstr, 16)
473-
else:
474-
continue
475-
else:
476-
continue
477-
except (ValueError, IndexError):
478-
# Virtual interfaces, such as those provided by
479-
# VPNs, do not have a colon-delimited MAC address
480-
# as expected, but a 16-byte HWAddr separated by
481-
# dashes. These should be ignored in favor of a
482-
# real MAC address
483-
pass
484-
else:
485-
if _is_universal(mac):
486-
return mac
487-
first_local_mac = first_local_mac or mac
488-
return first_local_mac or None
487+
except IndexError:
488+
continue
489+
490+
mac = _parse_mac(word)
491+
if mac is None:
492+
continue
493+
if _is_universal(mac):
494+
return mac
495+
if first_local_mac is None:
496+
first_local_mac = mac
497+
498+
return first_local_mac
489499

490500

491501
# The following functions call external programs to 'get' a macaddr value to

0 commit comments

Comments
 (0)