]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-74895: getaddrinfo no longer raises OverflowError (#2435)
authorRadek Smejkal <radek.smejkal@atrak.cz>
Tue, 14 Feb 2023 01:37:34 +0000 (02:37 +0100)
committerGitHub <noreply@github.com>
Tue, 14 Feb 2023 01:37:34 +0000 (17:37 -0800)
`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.

Lib/test/test_socket.py
Misc/ACKS
Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst [new file with mode: 0644]
Modules/getaddrinfo.c
Modules/socketmodule.c

index f1b4018c265e1816896bc72605332aeef386f1ba..a313da29b4a4fd93db6dbfad2afec8ceccac94f5 100644 (file)
@@ -1600,6 +1600,54 @@ class GeneralModuleTests(unittest.TestCase):
             except socket.gaierror:
                 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):
         # only IP addresses are allowed
         self.assertRaises(OSError, socket.getnameinfo, ('mail.python.org',0), 0)
index e12cbea0ebd6ed2650ca199f93beed80626a53fb..ca92608868f23f682ec316186418a14b2b16b7bc 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1688,6 +1688,7 @@ Roman Skurikhin
 Ville Skyttä
 Michael Sloan
 Nick Sloan
+Radek Smejkal
 Václav Šmilauer
 Casper W. Smet
 Allen W. Smith
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-13-22-21-58.gh-issue-74895.esMNtq.rst
new file mode 100644 (file)
index 0000000..adbbb60
--- /dev/null
@@ -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.
index 0b4620ed683de9d63c5af77cf4d13bb977616d83..f1c28d7d9312ac639209dd61372f9deb3788dbf3 100644 (file)
@@ -342,7 +342,11 @@ getaddrinfo(const char*hostname, const char*servname,
                 pai->ai_socktype = SOCK_DGRAM;
                 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 {
             struct servent *sp;
             const char *proto;
index 0a9e46512b157b03e693f376ba290f41febb9f54..2d300f19436b1a08e364aae641666cabf707cc2a 100644 (file)
@@ -6650,7 +6650,7 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
     struct addrinfo *res0 = NULL;
     PyObject *hobj = NULL;
     PyObject *pobj = (PyObject *)NULL;
-    char pbuf[30];
+    PyObject *pstr = NULL;
     const char *hptr, *pptr;
     int family, socktype, protocol, flags;
     int error;
@@ -6680,11 +6680,13 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
         return NULL;
     }
     if (PyLong_CheckExact(pobj)) {
-        long value = PyLong_AsLong(pobj);
-        if (value == -1 && PyErr_Occurred())
+        pstr = PyObject_Str(pobj);
+        if (pstr == NULL)
+            goto err;
+        assert(PyUnicode_Check(pstr));
+        pptr = PyUnicode_AsUTF8(pstr);
+        if (pptr == NULL)
             goto err;
-        PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", value);
-        pptr = pbuf;
     } else if (PyUnicode_Check(pobj)) {
         pptr = PyUnicode_AsUTF8(pobj);
         if (pptr == NULL)
@@ -6750,12 +6752,14 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
         Py_DECREF(single);
     }
     Py_XDECREF(idna);
+    Py_XDECREF(pstr);
     if (res0)
         freeaddrinfo(res0);
     return all;
  err:
     Py_XDECREF(all);
     Py_XDECREF(idna);
+    Py_XDECREF(pstr);
     if (res0)
         freeaddrinfo(res0);
     return (PyObject *)NULL;