File CVE-2024-49769.patch of Package python-waitress.36515

From 03cc640fe7106902899f82115c26e37002bca7f1 Mon Sep 17 00:00:00 2001
From: Delta Regeer <bertjw@regeer.org>
Date: Sun, 3 Mar 2024 16:15:51 -0700
Subject: [PATCH 1/6] HTTPChannel is always created from accept, explicitly set
 self.connected to True

---
 waitress/channel.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

Index: waitress-1.4.3/waitress/channel.py
===================================================================
--- waitress-1.4.3.orig/waitress/channel.py
+++ waitress-1.4.3/waitress/channel.py
@@ -77,8 +77,7 @@ class HTTPChannel(wasyncore.dispatcher,
         self.outbuf_lock = threading.Condition()
 
         wasyncore.dispatcher.__init__(self, sock, map=map)
-
-        # Don't let wasyncore.dispatcher throttle self.addr on us.
+        self.connected = True
         self.addr = addr
 
     def writable(self):
@@ -90,9 +89,6 @@ class HTTPChannel(wasyncore.dispatcher,
     def handle_write(self):
         # Precondition: there's data in the out buffer to be sent, or
         # there's a pending will_close request
-        if not self.connected:
-            # we dont want to close the channel twice
-            return
 
         # try to flush any pending output
         if not self.requests:
Index: waitress-1.4.3/waitress/wasyncore.py
===================================================================
--- waitress-1.4.3.orig/waitress/wasyncore.py
+++ waitress-1.4.3/waitress/wasyncore.py
@@ -300,22 +300,6 @@ class dispatcher:
             # get a socket from a blocking source.
             sock.setblocking(0)
             self.set_socket(sock, map)
-            self.connected = True
-            # The constructor no longer requires that the socket
-            # passed be connected.
-            try:
-                self.addr = sock.getpeername()
-            except socket.error as err:
-                if err.args[0] in (ENOTCONN, EINVAL):
-                    # To handle the case where we got an unconnected
-                    # socket.
-                    self.connected = False
-                else:
-                    # The socket is broken in some unknown way, alert
-                    # the user and remove it from the map (to prevent
-                    # polling of broken sockets).
-                    self.del_channel(map)
-                    raise
         else:
             self.socket = None
 
@@ -397,23 +381,6 @@ class dispatcher:
         self.addr = addr
         return self.socket.bind(addr)
 
-    def connect(self, address):
-        self.connected = False
-        self.connecting = True
-        err = self.socket.connect_ex(address)
-        if (
-            err in (EINPROGRESS, EALREADY, EWOULDBLOCK)
-            or err == EINVAL
-            and os.name == "nt"
-        ):  # pragma: no cover
-            self.addr = address
-            return
-        if err in (0, EISCONN):
-            self.addr = address
-            self.handle_connect_event()
-        else:
-            raise socket.error(err, errorcode[err])
-
     def accept(self):
         # XXX can return either an address pair or None
         try:
@@ -471,6 +438,8 @@ class dispatcher:
                 if why.args[0] not in (ENOTCONN, EBADF):
                     raise
 
+            self.socket = None
+
     # log and log_info may be overridden to provide more sophisticated
     # logging and warning methods. In general, log is for 'hit' logging
     # and 'log_info' is for informational, warning and error logging.
@@ -521,7 +490,11 @@ class dispatcher:
         # handle_expt_event() is called if there might be an error on the
         # socket, or if there is OOB data
         # check for the error condition first
-        err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+        err = (
+            self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+            if self.socket is not None
+            else 1
+        )
         if err != 0:
             # we can get here when select.select() says that there is an
             # exceptional condition on the socket
@@ -574,34 +547,6 @@ class dispatcher:
         self.close()
 
 
-# ---------------------------------------------------------------------------
-# adds simple buffered output capability, useful for simple clients.
-# [for more sophisticated usage use asynchat.async_chat]
-# ---------------------------------------------------------------------------
-
-
-class dispatcher_with_send(dispatcher):
-    def __init__(self, sock=None, map=None):
-        dispatcher.__init__(self, sock, map)
-        self.out_buffer = b""
-
-    def initiate_send(self):
-        num_sent = 0
-        num_sent = dispatcher.send(self, self.out_buffer[:65536])
-        self.out_buffer = self.out_buffer[num_sent:]
-
-    handle_write = initiate_send
-
-    def writable(self):
-        return (not self.connected) or len(self.out_buffer)
-
-    def send(self, data):
-        if self.debug:  # pragma: no cover
-            self.log_info("sending %s" % repr(data))
-        self.out_buffer = self.out_buffer + data
-        self.initiate_send()
-
-
 def close_all(map=None, ignore_all=False):
     if map is None:  # pragma: no cover
         map = socket_map
Index: waitress-1.4.3/waitress/tests/test_wasyncore.py
===================================================================
--- waitress-1.4.3.orig/waitress/tests/test_wasyncore.py
+++ waitress-1.4.3/waitress/tests/test_wasyncore.py
@@ -10,6 +10,7 @@ import socket
 import sys
 import time
 import errno
+from errno import EALREADY, EINPROGRESS, EINVAL, EISCONN, EWOULDBLOCK, errorcode
 import re
 import struct
 import threading
@@ -612,61 +613,6 @@ class DispatcherTests(unittest.TestCase)
         self.assertTrue(err != "")
 
 
-class dispatcherwithsend_noread(asyncore.dispatcher_with_send):  # pragma: no cover
-    def readable(self):
-        return False
-
-    def handle_connect(self):
-        pass
-
-
-class DispatcherWithSendTests(unittest.TestCase):
-    def setUp(self):
-        pass
-
-    def tearDown(self):
-        asyncore.close_all()
-
-    @reap_threads
-    def test_send(self):
-        evt = threading.Event()
-        sock = socket.socket()
-        sock.settimeout(3)
-        port = bind_port(sock)
-
-        cap = BytesIO()
-        args = (evt, cap, sock)
-        t = threading.Thread(target=capture_server, args=args)
-        t.start()
-        try:
-            # wait a little longer for the server to initialize (it sometimes
-            # refuses connections on slow machines without this wait)
-            time.sleep(0.2)
-
-            data = b"Suppose there isn't a 16-ton weight?"
-            d = dispatcherwithsend_noread()
-            d.create_socket()
-            d.connect((HOST, port))
-
-            # give time for socket to connect
-            time.sleep(0.1)
-
-            d.send(data)
-            d.send(data)
-            d.send(b"\n")
-
-            n = 1000
-            while d.out_buffer and n > 0:  # pragma: no cover
-                asyncore.poll()
-                n -= 1
-
-            evt.wait()
-
-            self.assertEqual(cap.getvalue(), data * 2)
-        finally:
-            join_thread(t, timeout=TIMEOUT)
-
-
 @unittest.skipUnless(
     hasattr(asyncore, "file_wrapper"), "asyncore.file_wrapper required"
 )
@@ -808,6 +754,23 @@ class BaseClient(BaseTestHandler):
         self.create_socket(family)
         self.connect(address)
 
+    def connect(self, address):
+        self.connected = False
+        self.connecting = True
+        err = self.socket.connect_ex(address)
+        if (
+            err in (EINPROGRESS, EALREADY, EWOULDBLOCK)
+            or err == EINVAL
+            and os.name == "nt"
+        ):  # pragma: no cover
+            self.addr = address
+            return
+        if err in (0, EISCONN):
+            self.addr = address
+            self.handle_connect_event()
+        else:
+            raise OSError(err, errorcode[err])
+
     def handle_connect(self):
         pass
 
@@ -1416,17 +1379,6 @@ class Test_dispatcher(unittest.TestCase)
 
         return dispatcher(sock=sock, map=map)
 
-    def test_unexpected_getpeername_exc(self):
-        sock = dummysocket()
-
-        def getpeername():
-            raise socket.error(errno.EBADF)
-
-        map = {}
-        sock.getpeername = getpeername
-        self.assertRaises(socket.error, self._makeOne, sock=sock, map=map)
-        self.assertEqual(map, {})
-
     def test___repr__accepting(self):
         sock = dummysocket()
         map = {}
@@ -1462,13 +1414,6 @@ class Test_dispatcher(unittest.TestCase)
         inst.set_reuse_addr()
         self.assertTrue(sock.errored)
 
-    def test_connect_raise_socket_error(self):
-        sock = dummysocket()
-        map = {}
-        sock.connect_ex = lambda *arg: 1
-        inst = self._makeOne(sock=sock, map=map)
-        self.assertRaises(socket.error, inst.connect, 0)
-
     def test_accept_raise_TypeError(self):
         sock = dummysocket()
         map = {}
@@ -1637,21 +1582,6 @@ class Test_dispatcher(unittest.TestCase)
         self.assertTrue(sock.closed)
 
 
-class Test_dispatcher_with_send(unittest.TestCase):
-    def _makeOne(self, sock=None, map=None):
-        from waitress.wasyncore import dispatcher_with_send
-
-        return dispatcher_with_send(sock=sock, map=map)
-
-    def test_writable(self):
-        sock = dummysocket()
-        map = {}
-        inst = self._makeOne(sock=sock, map=map)
-        inst.out_buffer = b"123"
-        inst.connected = True
-        self.assertTrue(inst.writable())
-
-
 class Test_close_all(unittest.TestCase):
     def _callFUT(self, map=None, ignore_all=False):
         from waitress.wasyncore import close_all
openSUSE Build Service is sponsored by