From e2c55c1ddc56d331ffe45d96b221a937b8da87b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Wed, 8 Jul 2026 11:14:34 +0200 Subject: [PATCH] Minor patch --- lib/core/settings.py | 2 +- lib/request/connect.py | 11 +++++++++++ lib/request/keepalive.py | 24 ++++++++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/core/settings.py b/lib/core/settings.py index c7165b0d3..01edfd279 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.7.38" +VERSION = "1.10.7.39" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/request/connect.py b/lib/request/connect.py index f148d9358..ce39c8f29 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -760,6 +760,17 @@ class Connect(object): warnMsg = "problem occurred during connection closing ('%s')" % getSafeExString(ex) logger.warning(warnMsg) + # Keep-alive: dispose the response explicitly. Its wrapped close() hands the socket + # back to the pool when the body was fully drained, otherwise drops it (a size-capped + # partial read must not be reused). This avoids leaning on GC to reclaim it (delayed on + # non-refcounting runtimes like PyPy). Guarded by the handler's marker so the HTTP/2 + # reuse pool is left untouched. + elif conn is not None and getattr(conn, "_keepaliveManaged", False): + try: + conn.close() + except Exception: + pass + except SqlmapConnectionException as ex: if conf.proxyList and not kb.threadException: warnMsg = "unable to connect to the target URL ('%s')" % getSafeExString(ex) diff --git a/lib/request/keepalive.py b/lib/request/keepalive.py index e3f192643..1195ff477 100644 --- a/lib/request/keepalive.py +++ b/lib/request/keepalive.py @@ -185,7 +185,11 @@ class _KeepAliveHandler(object): a connection is never reused. """ - state = {"handled": False} + # 'eof' is raised only on a genuine end-of-body (observed while reading), never + # merely because close() nulled the file object; the socket is handed back to the + # pool solely on that signal, so a half-read response can never be reused (which + # would splice its leftover bytes onto the next request - response desynchronization) + state = {"handled": False, "reading": False, "eof": False} _read = response.read _close = response.close @@ -200,7 +204,7 @@ class _KeepAliveHandler(object): def settle(): # Once (and only once) the body is fully drained, decide the socket's fate - if state["handled"] or not drained(): + if state["handled"] or not state["eof"]: return state["handled"] = True if keep: @@ -212,12 +216,23 @@ class _KeepAliveHandler(object): pass def read(*args, **kwargs): - data = _read(*args, **kwargs) + state["reading"] = True + try: + data = _read(*args, **kwargs) + finally: + state["reading"] = False + if drained(): + state["eof"] = True settle() return data def close(): - # Note: on Python 2 httplib.read() calls close() itself upon EOF + # Note: on Python 2 httplib.read() calls close() itself upon EOF, i.e. from inside + # our read(); such an in-read close marks a real end-of-body. An external close() + # on a not-yet-drained body means the caller abandoned it (e.g. sqlmap hitting a + # size cap), so the socket must be dropped rather than returned to the pool. + if state["reading"]: + state["eof"] = True _close() settle() if not state["handled"]: @@ -228,6 +243,7 @@ class _KeepAliveHandler(object): except Exception: pass + response._keepaliveManaged = True response.read = read response.close = close