Skip to content

Commit 352446e

Browse files
serhiy-storchakaclaude
authored andcommitted
gh-93251: Decode localized socket error messages from the locale encoding (GH-154683)
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 ba8584d commit 352446e

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
@@ -1732,6 +1732,23 @@ def testGetaddrinfo(self):
17321732
except socket.gaierror:
17331733
pass
17341734

1735+
@unittest.skipUnless(hasattr(socket, 'AI_NUMERICSERV'),
1736+
'needs socket.AI_NUMERICSERV')
1737+
@support.thread_unsafe('setlocale is not thread-safe')
1738+
@support.run_with_locales('LC_ALL',
1739+
'uk_UA.KOI8-U', 'uk_UA', 'ja_JP.eucJP', 'ja_JP.SJIS', 'ja_JP',
1740+
'ko_KR.eucKR', 'zh_CN.GB18030', 'el_GR.ISO8859-7',
1741+
'de_DE.ISO8859-1', 'ja_JP.UTF-8',
1742+
'')
1743+
def test_getaddrinfo_localized_error(self):
1744+
# gh-93251: the localized gai_strerror() message could fail to be
1745+
# decoded as UTF-8, so UnicodeDecodeError was raised
1746+
# instead of gaierror.
1747+
with self.assertRaises(socket.gaierror) as cm:
1748+
socket.getaddrinfo("localhost", "http",
1749+
flags=socket.AI_NUMERICSERV)
1750+
str(cm.exception)
1751+
17351752
@unittest.skipIf(_testcapi is None, "requires _testcapi")
17361753
def test_getaddrinfo_int_port_overflow(self):
17371754
# 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
@@ -676,14 +676,24 @@ set_error(void)
676676
}
677677

678678

679+
#if defined(HAVE_HSTRERROR) || defined(HAVE_GAI_STRERROR)
680+
/* Decode a locale-encoded error message from the C library.
681+
It can be localized and use a non-UTF-8 encoding. */
682+
static PyObject *
683+
decode_error_message(const char *str)
684+
{
685+
return PyUnicode_DecodeLocale(str, "surrogateescape");
686+
}
687+
#endif
688+
679689
#if defined(HAVE_GETHOSTBYNAME_R) || defined (HAVE_GETHOSTBYNAME) || defined (HAVE_GETHOSTBYADDR)
680690
static PyObject *
681691
set_herror(socket_state *state, int h_error)
682692
{
683693
PyObject *v;
684694

685695
#ifdef HAVE_HSTRERROR
686-
v = Py_BuildValue("(is)", h_error, hstrerror(h_error));
696+
v = Py_BuildValue("(iN)", h_error, decode_error_message(hstrerror(h_error)));
687697
#else
688698
v = Py_BuildValue("(is)", h_error, "host not found");
689699
#endif
@@ -710,7 +720,7 @@ set_gaierror(socket_state *state, int error)
710720
#endif
711721

712722
#ifdef HAVE_GAI_STRERROR
713-
v = Py_BuildValue("(is)", error, gai_strerror(error));
723+
v = Py_BuildValue("(iN)", error, decode_error_message(gai_strerror(error)));
714724
#else
715725
v = Py_BuildValue("(is)", error, "getaddrinfo failed");
716726
#endif

0 commit comments

Comments
 (0)