diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index b76c60e1ce2..3b013a8ef5e 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -23,6 +23,12 @@ def importable(name): return False +def mock_get_command_stdout(data): + def get_command_stdout(command, args): + return io.BytesIO(data.encode()) + return get_command_stdout + + class BaseTestUUID: uuid = None @@ -673,7 +679,6 @@ class TestUUIDWithExtModule(BaseTestUUID, unittest.TestCase): class BaseTestInternals: _uuid = py_uuid - def test_find_under_heading(self): data = '''\ Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll @@ -685,15 +690,12 @@ en0 1500 192.168.90 x071 1714807956 0 711348489 0 0 224.0.0.1 ''' - def mock_get_command_stdout(command, args): - return io.BytesIO(data.encode()) - # The above data is from AIX - with '.' as _MAC_DELIM and strings # shorter than 17 bytes (no leading 0). (_MAC_OMITS_LEADING_ZEROES=True) with mock.patch.multiple(self.uuid, _MAC_DELIM=b'.', _MAC_OMITS_LEADING_ZEROES=True, - _get_command_stdout=mock_get_command_stdout): + _get_command_stdout=mock_get_command_stdout(data)): mac = self.uuid._find_mac_under_heading( command='netstat', args='-ian', @@ -702,6 +704,43 @@ en0 1500 192.168.90 x071 1714807956 0 711348489 0 0 self.assertEqual(mac, 0xfead0c012304) + def test_find_under_heading_ipv6(self): + # bpo-39991: IPv6 address "fe80::5054:ff:fe9" looks like a MAC address + # (same string length) but must be skipped + data = '''\ +Name Mtu Network Address Ipkts Ierrs Idrop Opkts Oerrs Coll +vtnet 1500 52:54:00:9d:0e:67 10017 0 0 8174 0 0 +vtnet - fe80::%vtnet0 fe80::5054:ff:fe9 0 - - 4 - - +vtnet - 192.168.122.0 192.168.122.45 8844 - - 8171 - - +lo0 16384 lo0 260148 0 0 260148 0 0 +lo0 - ::1/128 ::1 193 - - 193 - - + ff01::1%lo0 + ff02::2:2eb7:74fa + ff02::2:ff2e:b774 + ff02::1%lo0 + ff02::1:ff00:1%lo +lo0 - fe80::%lo0/64 fe80::1%lo0 0 - - 0 - - + ff01::1%lo0 + ff02::2:2eb7:74fa + ff02::2:ff2e:b774 + ff02::1%lo0 + ff02::1:ff00:1%lo +lo0 - 127.0.0.0/8 127.0.0.1 259955 - - 259955 - - + 224.0.0.1 +''' + + with mock.patch.multiple(self.uuid, + _MAC_DELIM=b':', + _MAC_OMITS_LEADING_ZEROES=False, + _get_command_stdout=mock_get_command_stdout(data)): + mac = self.uuid._find_mac_under_heading( + command='netstat', + args='-ian', + heading=b'Address', + ) + + self.assertEqual(mac, 0x5254009d0e67) + def test_find_mac_near_keyword(self): # key and value are on the same line data = ''' @@ -710,14 +749,11 @@ cscotun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab ''' - def mock_get_command_stdout(command, args): - return io.BytesIO(data.encode()) - # The above data will only be parsed properly on non-AIX unixes. with mock.patch.multiple(self.uuid, _MAC_DELIM=b':', _MAC_OMITS_LEADING_ZEROES=False, - _get_command_stdout=mock_get_command_stdout): + _get_command_stdout=mock_get_command_stdout(data)): mac = self.uuid._find_mac_near_keyword( command='ifconfig', args='', diff --git a/Lib/uuid.py b/Lib/uuid.py index 224a766ff22..3b3abc2a455 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -456,7 +456,10 @@ def _find_mac_under_heading(command, args, heading): try: words = line.rstrip().split() word = words[column_index] - if len(word) == 17: + # Accept 'HH:HH:HH:HH:HH:HH' MAC address (ex: '52:54:00:9d:0e:67'), + # but reject IPv6 address (ex: 'fe80::5054:ff:fe9') detected + # by '::' pattern. + if len(word) == 17 and b'::' not in word: mac = int(word.replace(_MAC_DELIM, b''), 16) elif _MAC_OMITS_LEADING_ZEROES: # (Only) on AIX the macaddr value given is not prefixed by 0, e.g. diff --git a/Misc/NEWS.d/next/Library/2020-03-17-12-40-38.bpo-39991.hLPPs4.rst b/Misc/NEWS.d/next/Library/2020-03-17-12-40-38.bpo-39991.hLPPs4.rst new file mode 100644 index 00000000000..ef5a9e4d673 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-17-12-40-38.bpo-39991.hLPPs4.rst @@ -0,0 +1,2 @@ +:func:`uuid.getnode` now skips IPv6 addresses with the same string length +than a MAC address (17 characters): only use MAC addresses.