-
Notifications
You must be signed in to change notification settings - Fork 47
Expand file tree
/
Copy pathpatch_apply.sh
More file actions
executable file
·363 lines (312 loc) · 13.9 KB
/
patch_apply.sh
File metadata and controls
executable file
·363 lines (312 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#!/bin/bash
#
# patch_apply.sh - Apply SOCKS cache_peer support patches to Squid source
#
# Usage: patch_apply.sh <patch_src_dir> <squid_src_dir>
#
# Modifies the Squid 6.x source tree to add SOCKS4/SOCKS5 support for
# cache_peer directives. Uses pattern-based modifications (sed + Python)
# so that the script is tolerant of minor changes across 6.x point releases.
#
set -euo pipefail
PATCH_SRC="${1:?Usage: $0 <patch_src_dir> <squid_src_dir>}"
SQUID_SRC="${2:?Usage: $0 <patch_src_dir> <squid_src_dir>}"
die() { echo "PATCH ERROR: $*" >&2; exit 1; }
echo "==> Copying SocksPeerConnector.h into ${SQUID_SRC}/src/"
cp "${PATCH_SRC}/SocksPeerConnector.h" "${SQUID_SRC}/src/SocksPeerConnector.h" \
|| die "Failed to copy SocksPeerConnector.h"
# ---------------------------------------------------------------------------
# 1. CachePeer.h – add socks_type / socks_user / socks_pass fields
# ---------------------------------------------------------------------------
CACHE_PEER_H="${SQUID_SRC}/src/CachePeer.h"
echo "==> Patching ${CACHE_PEER_H}"
[ -f "${CACHE_PEER_H}" ] || die "CachePeer.h not found"
grep -q 'class CachePeer' "${CACHE_PEER_H}" || die "CachePeer class not found"
# Do NOT include SocksPeerConnector.h here – it pulls in POSIX headers
# that must come after squid.h. Use plain int/char* for the fields.
if ! grep -q 'socks_type' "${CACHE_PEER_H}"; then
if grep -q '} options;' "${CACHE_PEER_H}" 2>/dev/null; then
sed -i '/} options;/a\
\
/* SOCKS proxy support for cache_peer (0=none, 4=SOCKS4, 5=SOCKS5) */\
int socks_type = 0;\
char *socks_user = nullptr;\
char *socks_pass = nullptr;' "${CACHE_PEER_H}"
else
sed -i '/^};/i\
\
/* SOCKS proxy support for cache_peer (0=none, 4=SOCKS4, 5=SOCKS5) */\
int socks_type = 0;\
char *socks_user = nullptr;\
char *socks_pass = nullptr;\
' "${CACHE_PEER_H}"
fi
fi
echo " CachePeer.h patched OK"
# ---------------------------------------------------------------------------
# 1b. CachePeer.cc – free socks_user / socks_pass in destructor
# ---------------------------------------------------------------------------
CACHE_PEER_CC="${SQUID_SRC}/src/CachePeer.cc"
echo "==> Patching ${CACHE_PEER_CC}"
[ -f "${CACHE_PEER_CC}" ] || die "CachePeer.cc not found"
if ! grep -q 'socks_user' "${CACHE_PEER_CC}"; then
# Insert xfree calls next to existing xfree(login) in the destructor
sed -i '/xfree(login);/a\
\
xfree(socks_user);\
xfree(socks_pass);' "${CACHE_PEER_CC}"
grep -q 'socks_user' "${CACHE_PEER_CC}" || die "Failed to patch CachePeer.cc destructor"
fi
echo " CachePeer.cc patched OK"
# ---------------------------------------------------------------------------
# 2. cache_cf.cc – parse socks4 / socks5 / socks-user= / socks-pass=
# ---------------------------------------------------------------------------
CACHE_CF="${SQUID_SRC}/src/cache_cf.cc"
echo "==> Patching ${CACHE_CF}"
[ -f "${CACHE_CF}" ] || die "cache_cf.cc not found"
if ! grep -q 'socks_type' "${CACHE_CF}"; then
ANCHOR=""
for pattern in 'proxy-only' 'no-digest' 'no-query' 'round-robin' 'originserver'; do
if grep -q "\"${pattern}\"" "${CACHE_CF}"; then
ANCHOR="${pattern}"
break
fi
done
[ -n "${ANCHOR}" ] || die "Could not find peer option parsing anchor in cache_cf.cc"
echo " Using anchor: '${ANCHOR}'"
python3 - "${CACHE_CF}" "${ANCHOR}" << 'PYEOF'
import sys, re
filepath = sys.argv[1]
anchor = sys.argv[2]
with open(filepath, 'r') as f:
content = f.read()
# The code to insert. Starts with " else if" (no leading "}") and closes
# the final branch with "}". The insertion point is right after the "}"
# that closes the anchor's if-block, so " else if" continues the chain.
socks_code = ''' else if (!strcmp(token, "socks4")) {
p->socks_type = 4;
} else if (!strcmp(token, "socks5")) {
p->socks_type = 5;
} else if (!strncmp(token, "socks-user=", 11)) {
safe_free(p->socks_user);
p->socks_user = xstrdup(token + 11);
} else if (!strncmp(token, "socks-pass=", 11)) {
safe_free(p->socks_pass);
p->socks_pass = xstrdup(token + 11);
}'''
# Find the anchor in a strcmp/strncmp context
# Try matching "else if" variant first (most options), then plain "if" (first option)
for pat_template in [
r'else\s+if\s*\(!(?:strcmp|strncmp)\(token,\s*"' + re.escape(anchor) + r'"',
r'if\s*\(!(?:strcmp|strncmp)\(token,\s*"' + re.escape(anchor) + r'"',
]:
pat = re.compile(pat_template)
match = pat.search(content)
if match:
break
if not match:
print(f"ERROR: Could not find '{anchor}' in cache_cf.cc", file=sys.stderr)
sys.exit(1)
# From the match position, find the opening brace and count to the closing brace
idx = match.start()
brace_start = content.find('{', idx)
if brace_start < 0:
print("ERROR: Could not find opening brace", file=sys.stderr)
sys.exit(1)
depth = 1
pos = brace_start + 1
while pos < len(content) and depth > 0:
if content[pos] == '{': depth += 1
elif content[pos] == '}': depth -= 1
pos += 1
# pos is now right after the closing "}" of the anchor block
content = content[:pos] + socks_code + content[pos:]
# Also add a post-parse validation: socks4/socks5 requires originserver.
# Options can appear in any order, so we validate after the while loop ends.
# findCachePeerByName is the first check after the option-parsing loop.
validation = '''
/* Validate: SOCKS peers must use originserver */
if (p->socks_type && !p->options.originserver)
throw TextException(ToSBuf("cache_peer ", *p, ": socks4/socks5 requires the originserver option"), Here());
/* Validate: socks-user/socks-pass only valid with socks5 and must be set together */
if (p->socks_type != 5 && (p->socks_user || p->socks_pass))
throw TextException(ToSBuf("cache_peer ", *p, ": socks-user/socks-pass options require socks5"), Here());
if (p->socks_type == 5 && ((!p->socks_user) != (!p->socks_pass)))
throw TextException(ToSBuf("cache_peer ", *p, ": socks-user and socks-pass must both be set or both omitted"), Here());
'''
marker = 'findCachePeerByName'
marker_idx = content.find(marker, pos)
if marker_idx > pos:
line_start = content.rfind('\n', 0, marker_idx)
if line_start > 0:
content = content[:line_start] + validation + content[line_start:]
print(" Inserted SOCKS+originserver validation after option parsing loop")
else:
print("ERROR: Could not insert originserver validation", file=sys.stderr)
sys.exit(1)
with open(filepath, 'w') as f:
f.write(content)
print(f" Inserted SOCKS parsing after '{anchor}' block")
PYEOF
fi
echo " cache_cf.cc patched OK"
# ---------------------------------------------------------------------------
# 3. FwdState.cc – SOCKS negotiation at the top of dispatch()
# ---------------------------------------------------------------------------
FWD_STATE="${SQUID_SRC}/src/FwdState.cc"
echo "==> Patching ${FWD_STATE}"
[ -f "${FWD_STATE}" ] || die "FwdState.cc not found"
# Add include AFTER squid.h (squid.h MUST be the first include in every .cc)
if ! grep -q 'SocksPeerConnector.h' "${FWD_STATE}"; then
sed -i '/#include "squid.h"/a\
#include "SocksPeerConnector.h"' "${FWD_STATE}"
grep -q 'SocksPeerConnector.h' "${FWD_STATE}" || die "Failed to add include to FwdState.cc"
fi
if ! grep -q 'socks_type' "${FWD_STATE}"; then
python3 - "${FWD_STATE}" << 'PYEOF'
import sys, re
filepath = sys.argv[1]
with open(filepath, 'r') as f:
content = f.read()
# Squid 6.10 API:
# serverConnection() returns Comm::ConnectionPointer const &
# ->getPeer() returns CachePeer*
# ->fd is int (public member of Comm::Connection)
# request->url.host() returns const char*
# request->url.port() returns unsigned short
# retryOrBail() is a private method of FwdState
socks_hook = r'''
/* SOCKS peer negotiation: after TCP connect, before HTTP dispatch */
if (const auto sp = serverConnection()->getPeer()) {
if (sp->socks_type) {
/* The SOCKS tunnel is bound to (request->url.host():port).
* The pconn pool is keyed by peer address, NOT target, so a
* pooled SOCKS-negotiated connection would silently route the
* next request to the WRONG destination. Force the upstream
* connection to close after this request to keep one tunnel
* per target, and to guarantee the next dispatch() runs on a
* freshly-connected fd that has not been SOCKS-negotiated yet. */
request->flags.proxyKeepalive = false;
const auto targetPort = static_cast<uint16_t>(request->url.port());
debugs(17, 3, "SOCKS" << sp->socks_type
<< " negotiation with peer " << sp->host
<< " for " << request->url.host() << ":" << targetPort);
if (!SocksPeerConnector::negotiate(
serverConnection()->fd,
static_cast<SocksPeerType>(sp->socks_type),
std::string(request->url.host()),
targetPort,
sp->socks_user ? std::string(sp->socks_user) : std::string(),
sp->socks_pass ? std::string(sp->socks_pass) : std::string())) {
debugs(17, 2, "SOCKS negotiation FAILED for peer " << sp->host);
retryOrBail();
return;
}
debugs(17, 3, "SOCKS negotiation OK for peer " << sp->host);
}
}
'''
inserted = False
for pat in [
r'(void\s+FwdState::dispatch\s*\(\s*\)\s*\{)',
r'(FwdState::dispatch\s*\(\s*\)\s*\n?\s*\{)',
]:
match = re.search(pat, content)
if match:
insert_pos = match.end()
content = content[:insert_pos] + socks_hook + content[insert_pos:]
inserted = True
print(" Inserted SOCKS hook at top of FwdState::dispatch()")
break
if not inserted:
print("ERROR: Could not find dispatch() insertion point in FwdState.cc", file=sys.stderr)
print(" SOCKS support for HTTP requests will not work", file=sys.stderr)
sys.exit(1)
with open(filepath, 'w') as f:
f.write(content)
PYEOF
fi
echo " FwdState.cc patched OK"
# ---------------------------------------------------------------------------
# 4. tunnel.cc – SOCKS negotiation in connectDone() for CONNECT/HTTPS
# ---------------------------------------------------------------------------
TUNNEL_CC="${SQUID_SRC}/src/tunnel.cc"
echo "==> Patching ${TUNNEL_CC}"
[ -f "${TUNNEL_CC}" ] || die "tunnel.cc not found"
# Add include AFTER squid.h
if ! grep -q 'SocksPeerConnector.h' "${TUNNEL_CC}"; then
sed -i '/#include "squid.h"/a\
#include "SocksPeerConnector.h"' "${TUNNEL_CC}"
grep -q 'SocksPeerConnector.h' "${TUNNEL_CC}" || die "Failed to add include to tunnel.cc"
fi
if ! grep -q 'socks_type' "${TUNNEL_CC}"; then
python3 - "${TUNNEL_CC}" << 'PYEOF'
import sys, re
filepath = sys.argv[1]
with open(filepath, 'r') as f:
content = f.read()
# tunnel.cc API (Squid 6.10):
# TunnelStateData has: server.conn, request (HttpRequestPointer)
# connectDone(const Comm::ConnectionPointer &conn, ...) - after TCP connect
# conn->getPeer() returns CachePeer*
# conn->fd is int
# request->url.host() returns const char*
socks_tunnel_hook = r'''
/* SOCKS peer: negotiate tunnel right after TCP connect */
if (conn->getPeer() && conn->getPeer()->socks_type) {
const auto sp = conn->getPeer();
/* Same rationale as FwdState::dispatch(): the SOCKS tunnel is
* bound to one target host, so prevent this connection from being
* returned to the pconn pool where another request could pick it
* up and silently send data into the previous target's tunnel. */
request->flags.proxyKeepalive = false;
const auto targetPort = static_cast<uint16_t>(request->url.port());
debugs(26, 3, "SOCKS" << sp->socks_type
<< " tunnel negotiation with peer " << sp->host
<< " for " << request->url.host() << ":" << targetPort);
if (!SocksPeerConnector::negotiate(
conn->fd,
static_cast<SocksPeerType>(sp->socks_type),
std::string(request->url.host()),
targetPort,
sp->socks_user ? std::string(sp->socks_user) : std::string(),
sp->socks_pass ? std::string(sp->socks_pass) : std::string())) {
debugs(26, 2, "SOCKS tunnel negotiation FAILED for " << sp->host);
saveError(new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw(), al));
retryOrBail("SOCKS negotiation failed");
return;
}
debugs(26, 3, "SOCKS tunnel negotiation OK for " << sp->host);
}
'''
inserted = False
for pat in [
r'(void\s+TunnelStateData::connectDone\s*\([^)]*\)\s*\{)',
r'(TunnelStateData::connectDone\s*\([^)]*\)\s*\n?\s*\{)',
r'(void\s+tunnelConnectDone\s*\([^)]*\)\s*\{)',
]:
match = re.search(pat, content)
if match:
insert_pos = match.end()
content = content[:insert_pos] + socks_tunnel_hook + content[insert_pos:]
inserted = True
print(f" Inserted SOCKS tunnel hook in {match.group(0).strip()[:70]}...")
break
if not inserted:
print("ERROR: Could not patch tunnel.cc - HTTPS tunneling through SOCKS peers will not work", file=sys.stderr)
sys.exit(1)
with open(filepath, 'w') as f:
f.write(content)
PYEOF
fi
echo " tunnel.cc patched OK"
echo ""
echo "==> All patches applied successfully"
echo ""
echo "Modified files:"
echo " - src/CachePeer.h (added socks_type/user/pass fields)"
echo " - src/CachePeer.cc (added socks_user/pass cleanup in destructor)"
echo " - src/cache_cf.cc (added socks4/socks5 option parsing)"
echo " - src/FwdState.cc (SOCKS negotiation in dispatch())"
echo " - src/tunnel.cc (SOCKS negotiation in connectDone())"
echo " - src/SocksPeerConnector.h (new: SOCKS4/5 protocol implementation)"