-
Notifications
You must be signed in to change notification settings - Fork 3
/
auto-apt-proxy
executable file
·202 lines (183 loc) · 5.88 KB
/
auto-apt-proxy
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
#!/bin/sh
# auto-apt-proxy - automatic detector of common APT proxy settings
# Copyright (C) 2016-2020 Antonio Terceiro
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set -e
uid=$(id -u)
cache_dir=${TMPDIR:-/tmp}/.auto-apt-proxy-${uid}
if [ -d "${cache_dir}" ]; then
# require existing cache dir to be owned by the current user and have the
# correct permissions
owner_and_mode="$(stat --format=%u:%f "${cache_dir}")"
if [ "${owner_and_mode}" != "${uid}:41c0" ]; then
echo "E: insecure cache dir ${cache_dir}. Must be owned by UID ${uid} and have permissions 700" >&2
exit 1
fi
else
mkdir -m 0700 "${cache_dir}"
fi
output="${cache_dir}/output"
cleanup() {
rm -f "$output"
}
trap cleanup INT EXIT TERM
hit() {
timeout 5 /usr/lib/apt/apt-helper \
-o Acquire::http::Proxy=DIRECT -o Acquire::Retries=0 \
download-file "$@" "$output" 2>&1
}
cache_ttl=60 # seconds
cache() {
local cache_file="${cache_dir}/cache"
local lock_file="${cache_dir}/lock"
local cache_age
(
flock 9
# invalidate stale cache
if [ -f "${cache_file}" ]; then
ts=$(stat --format=%Y "${cache_file}")
now=$(date +%s)
cache_age=$((now - ts))
if [ "${cache_age}" -gt "${cache_ttl}" ]; then
rm -f "${cache_file}"
fi
fi
if [ -f "${cache_file}" ]; then
# read cache
if [ -s "${cache_file}" ]; then
cat "${cache_file}"
fi
else
# update cache
"$@" > "$cache_file" || true
cat "${cache_file}"
fi
) 9> "${lock_file}"
}
detect_apt_cacher() {
local ip="$1"
local proxy=http://$ip:3142
hit -o "Acquire::http::Proxy::${ip}=DIRECT" "$proxy" >/dev/null 2>&1 || true;
if [ -s "$output" ] && grep -q -i '<title>Apt-cacher' "$output"; then
echo "$proxy"
return 0
fi
return 1
}
detect_apt_cacher_ng() {
local ip="$1"
local proxy=http://$ip:3142
if hit -o "Acquire::http::Proxy::${ip}=DIRECT" "$proxy" | grep -q -i '406.*usage.information'; then
echo "$proxy"
return 0
fi
return 1
}
detect_approx() {
local ip="$1"
local proxy=http://$ip:9999
hit -o "Acquire::http::Proxy::${ip}=DIRECT" "$proxy" >/dev/null 2>&1 || true;
if [ -s "$output" ] && grep -q -i '<title>approx\s*server</title>' "$output"; then
echo "$proxy"
return 0
fi
return 1
}
# NOTE: This does NOT check MDNS/DNS-SD (avahi/zeroconf/bonjour) records.
# If you want that, use squid-deb-proxy-client, which depends on avahi.
detect_squid_deb_proxy() {
local ip="$1"
local proxy=http://$ip:8000
if hit -oDebug::acquire::http=1 -o "Acquire::http::Proxy::${ip}=DIRECT" "$proxy" 2>&1 | grep -q 'Via: .*squid-deb-proxy'; then
echo "$proxy"
return 0
fi
return 1
}
# NOTE: This does NOT check MDNS/DNS-SD (avahi/zeroconf/bonjour) records.
# If you want that, use squid-deb-proxy-client, which depends on avahi.
#
# FIXME: if there are multiple matching SRV records, we should make a
# weighted random choice from the one(s) with the highest priority.
# For now, we make a uniformly random choice from all records (shuf + exit).
#
# NOTE: We don't check that it "looks like" a known apt proxy (hit + grep -q).
# This is because
# 1) the other detectors are just GUESSING hosts and ports.
# You might accidentally run a non-apt-proxy on 127.0.0.1:9999, but
# you can't accidentally create an _apt_proxy SRV record!
# 2) refactoring the grep -q's out of detect_* is tedious and boring.
# 3) there's no grep -q for squid, which I want to use. ;-)
#
# NOTE: no need for if/then/else and return 0/1 because:
# * if awk matches something, it prints it and exits zero.
# * if hostname or apt-helper fail, awk matches nothing, so exits non-zero.
# * set -e ignores errors from apt-helper (no pipefail) and hostname (no ???).
detect_DNS_SRV_record() {
local domain
domain="$(hostname --domain 2>/dev/null)"
if [ -n "${domain}" ]; then
/usr/lib/apt/apt-helper srv-lookup _apt_proxy._tcp."${domain}" 2>/dev/null |
shuf |
awk '/^[^#]/{print "http://" $1 ":" $4;found=1;exit}END{exit !found}'
else
return 1
fi
}
__detect__() {
# If a SRV record is found, use it and guess no further.
detect_DNS_SRV_record && return 0
if command -v ip >/dev/null; then
gateway=$(ip route | awk '/default/ { print($3) }')
elif busybox ip --help >/dev/null 2>&1; then
gateway=$(busybox ip route | awk '/default/ { print($3) }')
else
gateway=''
fi
# consider a user-defined host as well, quick check whether it's configured
explicit_proxy=$(timeout 5 getent hosts apt-proxy | awk '/[:blank:]/ { print($1) }' )
for ip in $explicit_proxy 127.0.0.1 $gateway; do
detect_apt_cacher_ng "$ip" && return 0
detect_approx "$ip" && return 0
detect_apt_cacher "$ip" && return 0
detect_squid_deb_proxy "$ip" && return 0
done
return 0
}
detect() {
if [ -z "${AUTO_APT_PROXY_NO_CACHE:-}" ]; then
cache __detect__
else
__detect__
fi
}
if [ $# -eq 0 ]; then
detect
else
case "$1" in
ftp://*|http://*|https://*|file://*)
# APT mode: first argument is an URI
detect
;;
*)
# wrapper mode: execute command using the detected proxy
proxy=$(detect || true)
if [ -n "$proxy" ]; then
export http_proxy="$proxy"
export HTTP_PROXY="$proxy"
fi
exec "$@"
esac
fi