@@ -210,6 +210,15 @@ socks_hook = r'''
210210 /* SOCKS peer negotiation: after TCP connect, before HTTP dispatch */
211211 if (const auto sp = serverConnection()->getPeer()) {
212212 if (sp->socks_type) {
213+ /* The SOCKS tunnel is bound to (request->url.host():port).
214+ * The pconn pool is keyed by peer address, NOT target, so a
215+ * pooled SOCKS-negotiated connection would silently route the
216+ * next request to the WRONG destination. Force the upstream
217+ * connection to close after this request to keep one tunnel
218+ * per target, and to guarantee the next dispatch() runs on a
219+ * freshly-connected fd that has not been SOCKS-negotiated yet. */
220+ request->flags.proxyKeepalive = false;
221+
213222 const auto targetPort = static_cast<uint16_t>(request->url.port());
214223 debugs(17, 3, "SOCKS" << sp->socks_type
215224 << " negotiation with peer " << sp->host
@@ -290,6 +299,11 @@ socks_tunnel_hook = r'''
290299 /* SOCKS peer: negotiate tunnel right after TCP connect */
291300 if (conn->getPeer() && conn->getPeer()->socks_type) {
292301 const auto sp = conn->getPeer();
302+ /* Same rationale as FwdState::dispatch(): the SOCKS tunnel is
303+ * bound to one target host, so prevent this connection from being
304+ * returned to the pconn pool where another request could pick it
305+ * up and silently send data into the previous target's tunnel. */
306+ request->flags.proxyKeepalive = false;
293307 const auto targetPort = static_cast<uint16_t>(request->url.port());
294308 debugs(26, 3, "SOCKS" << sp->socks_type
295309 << " tunnel negotiation with peer " << sp->host
0 commit comments