Skip to content

Commit b9723f9

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.14] gh-93251: Decode localized socket error messages from the locale encoding (GH-154683) (GH-154724)
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 7908848 commit b9723f9

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
@@ -1741,6 +1741,23 @@ def testGetaddrinfo(self):
17411741
except socket.gaierror:
17421742
pass
17431743

1744+
@unittest.skipUnless(hasattr(socket, 'AI_NUMERICSERV'),
1745+
'needs socket.AI_NUMERICSERV')
1746+
@support.thread_unsafe('setlocale is not thread-safe')
1747+
@support.run_with_locales('LC_ALL',
1748+
'uk_UA.KOI8-U', 'uk_UA', 'ja_JP.eucJP', 'ja_JP.SJIS', 'ja_JP',
1749+
'ko_KR.eucKR', 'zh_CN.GB18030', 'el_GR.ISO8859-7',
1750+
'de_DE.ISO8859-1', 'ja_JP.UTF-8',
1751+
'')
1752+
def test_getaddrinfo_localized_error(self):
1753+
# gh-93251: the localized gai_strerror() message could fail to be
1754+
# decoded as UTF-8, so UnicodeDecodeError was raised
1755+
# instead of gaierror.
1756+
with self.assertRaises(socket.gaierror) as cm:
1757+
socket.getaddrinfo("localhost", "http",
1758+
flags=socket.AI_NUMERICSERV)
1759+
str(cm.exception)
1760+
17441761
@unittest.skipIf(_testcapi is None, "requires _testcapi")
17451762
def test_getaddrinfo_int_port_overflow(self):
17461763
# 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
@@ -735,14 +735,24 @@ set_error(void)
735735
}
736736

737737

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

744754
#ifdef HAVE_HSTRERROR
745-
v = Py_BuildValue("(is)", h_error, hstrerror(h_error));
755+
v = Py_BuildValue("(iN)", h_error, decode_error_message(hstrerror(h_error)));
746756
#else
747757
v = Py_BuildValue("(is)", h_error, "host not found");
748758
#endif
@@ -769,7 +779,7 @@ set_gaierror(socket_state *state, int error)
769779
#endif
770780

771781
#ifdef HAVE_GAI_STRERROR
772-
v = Py_BuildValue("(is)", error, gai_strerror(error));
782+
v = Py_BuildValue("(iN)", error, decode_error_message(gai_strerror(error)));
773783
#else
774784
v = Py_BuildValue("(is)", error, "getaddrinfo failed");
775785
#endif

0 commit comments

Comments
 (0)