Skip to content

Commit 928752c

Browse files
authored
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.
1 parent 0c6fe81 commit 928752c

File tree

5 files changed

+68
-6
lines changed

5 files changed

+68
-6
lines changed

Lib/test/test_socket.py

+48
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,54 @@ def testGetaddrinfo(self):
16001600
except socket.gaierror:
16011601
pass
16021602

1603+
def test_getaddrinfo_int_port_overflow(self):
1604+
# gh-74895: Test that getaddrinfo does not raise OverflowError on port.
1605+
#
1606+
# POSIX getaddrinfo() never specify the valid range for "service"
1607+
# decimal port number values. For IPv4 and IPv6 they are technically
1608+
# unsigned 16-bit values, but the API is protocol agnostic. Which values
1609+
# trigger an error from the C library function varies by platform as
1610+
# they do not all perform validation.
1611+
1612+
# The key here is that we don't want to produce OverflowError as Python
1613+
# prior to 3.12 did for ints outside of a [LONG_MIN, LONG_MAX] range.
1614+
# Leave the error up to the underlying string based platform C API.
1615+
1616+
from _testcapi import ULONG_MAX, LONG_MAX, LONG_MIN
1617+
try:
1618+
socket.getaddrinfo(None, ULONG_MAX + 1)
1619+
except OverflowError:
1620+
# Platforms differ as to what values consitute a getaddrinfo() error
1621+
# return. Some fail for LONG_MAX+1, others ULONG_MAX+1, and Windows
1622+
# silently accepts such huge "port" aka "service" numeric values.
1623+
self.fail("Either no error or socket.gaierror expected.")
1624+
except socket.gaierror:
1625+
pass
1626+
1627+
try:
1628+
socket.getaddrinfo(None, LONG_MAX + 1)
1629+
except OverflowError:
1630+
self.fail("Either no error or socket.gaierror expected.")
1631+
except socket.gaierror:
1632+
pass
1633+
1634+
try:
1635+
socket.getaddrinfo(None, LONG_MAX - 0xffff + 1)
1636+
except OverflowError:
1637+
self.fail("Either no error or socket.gaierror expected.")
1638+
except socket.gaierror:
1639+
pass
1640+
1641+
try:
1642+
socket.getaddrinfo(None, LONG_MIN - 1)
1643+
except OverflowError:
1644+
self.fail("Either no error or socket.gaierror expected.")
1645+
except socket.gaierror:
1646+
pass
1647+
1648+
socket.getaddrinfo(None, 0) # No error expected.
1649+
socket.getaddrinfo(None, 0xffff) # No error expected.
1650+
16031651
def test_getnameinfo(self):
16041652
# only IP addresses are allowed
16051653
self.assertRaises(OSError, socket.getnameinfo, ('mail.python.org',0), 0)

Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ Roman Skurikhin
16881688
Ville Skyttä
16891689
Michael Sloan
16901690
Nick Sloan
1691+
Radek Smejkal
16911692
Václav Šmilauer
16921693
Casper W. Smet
16931694
Allen W. Smith
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
:mod:`socket.getaddrinfo` no longer raises :class:`OverflowError` for
2+
:class:`int` **port** values outside of the C long range. Out of range values
3+
are left up to the underlying string based C library API to report. A
4+
:class:`socket.gaierror` ``SAI_SERVICE`` may occur instead, or no error at all
5+
as not all platform C libraries generate an error.

Modules/getaddrinfo.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,11 @@ getaddrinfo(const char*hostname, const char*servname,
342342
pai->ai_socktype = SOCK_DGRAM;
343343
pai->ai_protocol = IPPROTO_UDP;
344344
}
345-
port = htons((u_short)atoi(servname));
345+
long maybe_port = strtol(servname, NULL, 10);
346+
if (maybe_port < 0 || maybe_port > 0xffff) {
347+
ERR(EAI_SERVICE);
348+
}
349+
port = htons((u_short)maybe_port);
346350
} else {
347351
struct servent *sp;
348352
const char *proto;

Modules/socketmodule.c

+9-5
Original file line numberDiff line numberDiff line change
@@ -6650,7 +6650,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
66506650
struct addrinfo *res0 = NULL;
66516651
PyObject *hobj = NULL;
66526652
PyObject *pobj = (PyObject *)NULL;
6653-
char pbuf[30];
6653+
PyObject *pstr = NULL;
66546654
const char *hptr, *pptr;
66556655
int family, socktype, protocol, flags;
66566656
int error;
@@ -6680,11 +6680,13 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
66806680
return NULL;
66816681
}
66826682
if (PyLong_CheckExact(pobj)) {
6683-
long value = PyLong_AsLong(pobj);
6684-
if (value == -1 && PyErr_Occurred())
6683+
pstr = PyObject_Str(pobj);
6684+
if (pstr == NULL)
6685+
goto err;
6686+
assert(PyUnicode_Check(pstr));
6687+
pptr = PyUnicode_AsUTF8(pstr);
6688+
if (pptr == NULL)
66856689
goto err;
6686-
PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", value);
6687-
pptr = pbuf;
66886690
} else if (PyUnicode_Check(pobj)) {
66896691
pptr = PyUnicode_AsUTF8(pobj);
66906692
if (pptr == NULL)
@@ -6750,12 +6752,14 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
67506752
Py_DECREF(single);
67516753
}
67526754
Py_XDECREF(idna);
6755+
Py_XDECREF(pstr);
67536756
if (res0)
67546757
freeaddrinfo(res0);
67556758
return all;
67566759
err:
67576760
Py_XDECREF(all);
67586761
Py_XDECREF(idna);
6762+
Py_XDECREF(pstr);
67596763
if (res0)
67606764
freeaddrinfo(res0);
67616765
return (PyObject *)NULL;

0 commit comments

Comments
 (0)