gh-74895: getaddrinfo no longer raises OverflowError (#2435)

`socket.getaddrinfo()` no longer raises `OverflowError` based on the **port** argument. Error reporting (or not) for its value is left up to the underlying C library `getaddrinfo()` implementation.
This commit is contained in:
Radek Smejkal 2023-02-14 02:37:34 +01:00 committed by GitHub
parent 0c6fe81dce
commit 928752ce4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 6 deletions

View File

@ -1600,6 +1600,54 @@ class GeneralModuleTests(unittest.TestCase):
except socket.gaierror: except socket.gaierror:
pass pass
def test_getaddrinfo_int_port_overflow(self):
# gh-74895: Test that getaddrinfo does not raise OverflowError on port.
#
# POSIX getaddrinfo() never specify the valid range for "service"
# decimal port number values. For IPv4 and IPv6 they are technically
# unsigned 16-bit values, but the API is protocol agnostic. Which values
# trigger an error from the C library function varies by platform as
# they do not all perform validation.
# The key here is that we don't want to produce OverflowError as Python
# prior to 3.12 did for ints outside of a [LONG_MIN, LONG_MAX] range.
# Leave the error up to the underlying string based platform C API.
from _testcapi import ULONG_MAX, LONG_MAX, LONG_MIN
try:
socket.getaddrinfo(None, ULONG_MAX + 1)
except OverflowError:
# Platforms differ as to what values consitute a getaddrinfo() error
# return. Some fail for LONG_MAX+1, others ULONG_MAX+1, and Windows
# silently accepts such huge "port" aka "service" numeric values.
self.fail("Either no error or socket.gaierror expected.")
except socket.gaierror:
pass
try:
socket.getaddrinfo(None, LONG_MAX + 1)
except OverflowError:
self.fail("Either no error or socket.gaierror expected.")
except socket.gaierror:
pass
try:
socket.getaddrinfo(None, LONG_MAX - 0xffff + 1)
except OverflowError:
self.fail("Either no error or socket.gaierror expected.")
except socket.gaierror:
pass
try:
socket.getaddrinfo(None, LONG_MIN - 1)
except OverflowError:
self.fail("Either no error or socket.gaierror expected.")
except socket.gaierror:
pass
socket.getaddrinfo(None, 0) # No error expected.
socket.getaddrinfo(None, 0xffff) # No error expected.
def test_getnameinfo(self): def test_getnameinfo(self):
# only IP addresses are allowed # only IP addresses are allowed
self.assertRaises(OSError, socket.getnameinfo, ('mail.python.org',0), 0) self.assertRaises(OSError, socket.getnameinfo, ('mail.python.org',0), 0)

View File

@ -1688,6 +1688,7 @@ Roman Skurikhin
Ville Skyttä Ville Skyttä
Michael Sloan Michael Sloan
Nick Sloan Nick Sloan
Radek Smejkal
Václav Šmilauer Václav Šmilauer
Casper W. Smet Casper W. Smet
Allen W. Smith Allen W. Smith

View File

@ -0,0 +1,5 @@
:mod:`socket.getaddrinfo` no longer raises :class:`OverflowError` for
:class:`int` **port** values outside of the C long range. Out of range values
are left up to the underlying string based C library API to report. A
:class:`socket.gaierror` ``SAI_SERVICE`` may occur instead, or no error at all
as not all platform C libraries generate an error.

View File

@ -342,7 +342,11 @@ getaddrinfo(const char*hostname, const char*servname,
pai->ai_socktype = SOCK_DGRAM; pai->ai_socktype = SOCK_DGRAM;
pai->ai_protocol = IPPROTO_UDP; pai->ai_protocol = IPPROTO_UDP;
} }
port = htons((u_short)atoi(servname)); long maybe_port = strtol(servname, NULL, 10);
if (maybe_port < 0 || maybe_port > 0xffff) {
ERR(EAI_SERVICE);
}
port = htons((u_short)maybe_port);
} else { } else {
struct servent *sp; struct servent *sp;
const char *proto; const char *proto;

View File

@ -6650,7 +6650,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
struct addrinfo *res0 = NULL; struct addrinfo *res0 = NULL;
PyObject *hobj = NULL; PyObject *hobj = NULL;
PyObject *pobj = (PyObject *)NULL; PyObject *pobj = (PyObject *)NULL;
char pbuf[30]; PyObject *pstr = NULL;
const char *hptr, *pptr; const char *hptr, *pptr;
int family, socktype, protocol, flags; int family, socktype, protocol, flags;
int error; int error;
@ -6680,11 +6680,13 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
return NULL; return NULL;
} }
if (PyLong_CheckExact(pobj)) { if (PyLong_CheckExact(pobj)) {
long value = PyLong_AsLong(pobj); pstr = PyObject_Str(pobj);
if (value == -1 && PyErr_Occurred()) if (pstr == NULL)
goto err;
assert(PyUnicode_Check(pstr));
pptr = PyUnicode_AsUTF8(pstr);
if (pptr == NULL)
goto err; goto err;
PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", value);
pptr = pbuf;
} else if (PyUnicode_Check(pobj)) { } else if (PyUnicode_Check(pobj)) {
pptr = PyUnicode_AsUTF8(pobj); pptr = PyUnicode_AsUTF8(pobj);
if (pptr == NULL) if (pptr == NULL)
@ -6750,12 +6752,14 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
Py_DECREF(single); Py_DECREF(single);
} }
Py_XDECREF(idna); Py_XDECREF(idna);
Py_XDECREF(pstr);
if (res0) if (res0)
freeaddrinfo(res0); freeaddrinfo(res0);
return all; return all;
err: err:
Py_XDECREF(all); Py_XDECREF(all);
Py_XDECREF(idna); Py_XDECREF(idna);
Py_XDECREF(pstr);
if (res0) if (res0)
freeaddrinfo(res0); freeaddrinfo(res0);
return (PyObject *)NULL; return (PyObject *)NULL;