Skip to content

Commit 6e6884b

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.15] gh-93251: Decode localized socket error messages from the locale encoding (GH-154683) (GH-154723)
The gai_strerror() and hstrerror() messages were decoded as UTF-8, so gaierror and herror could be replaced with UnicodeDecodeError if the message is localized and the locale encoding is not UTF-8. (cherry picked from commit 5afbb60) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 5be0125 commit 6e6884b

3 files changed

Lines changed: 33 additions & 2 deletions

File tree

Lib/test/test_socket.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,23 @@ def testGetaddrinfo(self):
17871787
except socket.gaierror:
17881788
pass
17891789

1790+
@unittest.skipUnless(hasattr(socket, 'AI_NUMERICSERV'),
1791+
'needs socket.AI_NUMERICSERV')
1792+
@support.thread_unsafe('setlocale is not thread-safe')
1793+
@support.run_with_locales('LC_ALL',
1794+
'uk_UA.KOI8-U', 'uk_UA', 'ja_JP.eucJP', 'ja_JP.SJIS', 'ja_JP',
1795+
'ko_KR.eucKR', 'zh_CN.GB18030', 'el_GR.ISO8859-7',
1796+
'de_DE.ISO8859-1', 'ja_JP.UTF-8',
1797+
'')
1798+
def test_getaddrinfo_localized_error(self):
1799+
# gh-93251: the localized gai_strerror() message could fail to be
1800+
# decoded as UTF-8, so UnicodeDecodeError was raised
1801+
# instead of gaierror.
1802+
with self.assertRaises(socket.gaierror) as cm:
1803+
socket.getaddrinfo("localhost", "http",
1804+
flags=socket.AI_NUMERICSERV)
1805+
str(cm.exception)
1806+
17901807
@unittest.skipIf(_testcapi is None, "requires _testcapi")
17911808
def test_getaddrinfo_int_port_overflow(self):
17921809
# gh-74895: Test that getaddrinfo does not raise OverflowError on port.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :exc:`UnicodeDecodeError` in :mod:`socket` functions
2+
(such as :func:`~socket.getaddrinfo` and :func:`~socket.gethostbyaddr`)
3+
when the localized error message of the C library is not UTF-8:
4+
decode it from the locale encoding.

Modules/socketmodule.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,14 +737,24 @@ set_error(void)
737737
}
738738

739739

740+
#if defined(HAVE_HSTRERROR) || defined(HAVE_GAI_STRERROR)
741+
/* Decode a locale-encoded error message from the C library.
742+
It can be localized and use a non-UTF-8 encoding. */
743+
static PyObject *
744+
decode_error_message(const char *str)
745+
{
746+
return PyUnicode_DecodeLocale(str, "surrogateescape");
747+
}
748+
#endif
749+
740750
#if defined(HAVE_GETHOSTBYNAME_R) || defined (HAVE_GETHOSTBYNAME) || defined (HAVE_GETHOSTBYADDR)
741751
static PyObject *
742752
set_herror(socket_state *state, int h_error)
743753
{
744754
PyObject *v;
745755

746756
#ifdef HAVE_HSTRERROR
747-
v = Py_BuildValue("(is)", h_error, hstrerror(h_error));
757+
v = Py_BuildValue("(iN)", h_error, decode_error_message(hstrerror(h_error)));
748758
#else
749759
v = Py_BuildValue("(is)", h_error, "host not found");
750760
#endif
@@ -771,7 +781,7 @@ set_gaierror(socket_state *state, int error)
771781
#endif
772782

773783
#ifdef HAVE_GAI_STRERROR
774-
v = Py_BuildValue("(is)", error, gai_strerror(error));
784+
v = Py_BuildValue("(iN)", error, decode_error_message(gai_strerror(error)));
775785
#else
776786
v = Py_BuildValue("(is)", error, "getaddrinfo failed");
777787
#endif

0 commit comments

Comments
 (0)