aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/mirror-checker.lua
diff options
context:
space:
mode:
authoronefang2019-06-25 15:08:34 +1000
committeronefang2019-06-25 15:08:34 +1000
commit22fc09cfd2305850063d2bbdd3af76237caf2e73 (patch)
tree87765825e2c2425ba00bcd26dfeb14919e39d539 /mirror-checker.lua
parentGit ignore the results. (diff)
downloadapt-panopticon-22fc09cfd2305850063d2bbdd3af76237caf2e73.zip
apt-panopticon-22fc09cfd2305850063d2bbdd3af76237caf2e73.tar.gz
apt-panopticon-22fc09cfd2305850063d2bbdd3af76237caf2e73.tar.bz2
apt-panopticon-22fc09cfd2305850063d2bbdd3af76237caf2e73.tar.xz
Add the actual source code, and the basic documentation.
Still need to write the help output.
Diffstat (limited to '')
-rwxr-xr-xmirror-checker.lua420
1 files changed, 420 insertions, 0 deletions
diff --git a/mirror-checker.lua b/mirror-checker.lua
new file mode 100755
index 0000000..34b2ec8
--- /dev/null
+++ b/mirror-checker.lua
@@ -0,0 +1,420 @@
1#!/usr/bin/env luajit
2
3
4local args = {...}
5
6
7verbosity = 0
8keep = false
9options =
10{
11 referenceSite =
12 {
13 typ = "string",
14 help = "",
15 value = "pkgmaster.devuan.org",
16 },
17 tests =
18 {
19 typ = "table",
20 help = "",
21 value =
22 {
23 "IPv4",
24 "IPv6",
25-- "ftp",
26 "http",
27 "https",
28-- "rsync",
29 "DNS-RR",
30 "Protocol",
31-- "URL-Sanity",
32-- "Integrity",
33-- "Updated",
34 },
35 },
36}
37
38local defaultURL = {scheme = "http"}
39local download = "wget -np -N -r -P results "
40local releases = {"jessie", "ascii", "beowulf", "ceres"}
41local releaseFiles =
42{
43 -- Release file.
44 "/Release",
45 "/InRelease",
46 "/main/binary-all/Packages.gz",
47 -- Contents files.
48 "/main/Contents-all.gz",
49 "/main/Contents-amd64.gz",
50 "/main/Contents-arm64.gz",
51 "-security/main/Contents-all.gz",
52 "-security/main/Contents-amd64.gz",
53 "-security/main/Contents-arm64.gz",
54}
55local referenceDebs =
56{
57 -- Devuan package. NOTE this one likely should not get redirected, but that's more a warning than an error.
58 "merged/pool/DEVUAN/main/d/desktop-base/desktop-base_2.0.3_all.deb",
59 -- Debian package.
60 "merged/pool/DEBIAN/main/d/dash/dash_0.5.8-2.4_amd64.deb",
61 -- Debian security package. NOTE this one should always be redirected?
62 "merged/pool/DEBIAN-SECURITY/updates/main/a/apt/apt-transport-https_1.4.9_amd64.deb",
63}
64local arg = {}
65local sendArgs = ""
66local log
67
68
69local socket = require 'socket'
70local ftp = require 'socket.ftp'
71local http = require 'socket.http'
72local url = require 'socket.url'
73
74
75-- Use this to print a table.
76printTable = function (table, space, name)
77 print(space .. name .. ": ")
78 print(space .. "{")
79 printTableSub(table, space .. " ")
80 print(space .. "}")
81end
82printTableSub = function (table, space)
83 for k, v in pairs(table) do
84 if type(v) == "table" then
85 printTable(v, space, k)
86 elseif type(v) == "string" then
87 print(space .. k .. ': "' .. v .. '";')
88 elseif type(v) == "function" then
89 print(space .. "function " .. k .. "();")
90 elseif type(v) == "userdata" then
91 print(space .. "userdata " .. k .. ";")
92 elseif type(v) == "boolean" then
93 if (v) then
94 print(space .. "boolean " .. k .. " TRUE ;")
95 else
96 print(space .. "boolean " .. k .. " FALSE ;")
97 end
98 else
99 print(space .. k .. ": " .. v .. ";")
100 end
101 end
102end
103
104local log = function(v, t, s)
105 if v <= verbosity then
106 if 3 <= verbosity then t = os.date() .. " " .. t end
107 print(t .. ": " .. s)
108 end
109 if nil ~= log then
110 log:write(os.date() .. " " .. t .. ": " .. s .. "/n")
111 log:flush()
112 end
113end
114local D = function(s) log(3, "DEBUG ", s) end
115local I = function(s) log(2, "INFO ", s) end
116local W = function(s) log(1, "WARNING ", s) end
117local E = function(s) log(0, "ERROR ", s) end
118local C = function(s) log(-1, "CRITICAL", s) end
119
120local testing = function(t)
121 for i, v in pairs(options.tests.value) do
122 if t == v then return true end
123 end
124 return false
125end
126
127local checkExes = function (exe)
128 local count = io.popen('ps x | grep "' .. exe .. '" | grep -v " grep " | wc -l'):read("*l")
129 D(count .. " " .. exe .. " commands left.")
130 return tonumber(count)
131end
132
133
134local IP = {}
135local gatherIPs = function (host)
136 if nil == IP[host] then
137 local IPs
138 local dig = io.popen('dig +keepopen +noall +nottlid +answer ' .. host .. ' A ' .. host .. ' AAAA ' .. host .. ' CNAME ' .. host .. ' SRV | sort | uniq')
139 repeat
140 IPs = dig:read("*l")
141 if nil ~= IPs then
142 for k, t, v in IPs:gmatch("([%w_%-%.]*)%.%s*IN%s*(%a*)%s*(.*)") do
143 if "." == v:sub(-1, -1) then v = v:sub(1, -2) end
144 if nil == IP[k] then IP[k] = {} end
145 IP[k][v] = t
146 end
147 end
148 until nil == IPs
149 end
150end
151
152-- Returns FTP directory listing
153local nlst = function (u)
154 local t = {}
155 local p = url.parse(u)
156 p.command = "nlst"
157 p.sink = ltn12.sink.table(t)
158 local r, e = ftp.get(p)
159 return r and table.concat(t), e
160end
161
162checkURL = function (host, URL, r)
163 if nil == r then r = 0 end
164 local check = "Checking file"
165 if 0 < r then
166 check = "Redirecting to"
167-- checkIP(host)
168 end
169 if 10 < r then
170 E("too many redirects! " .. check .. " " .. host .. " -> " .. URL)
171 return
172 end
173 local PU = url.parse(URL, defaultURL)
174 D(" " .. PU.scheme .. " :// " .. check .. " " .. host .. " -> " .. URL)
175 if not testing(PU.scheme) then D("not testing " .. PU.scheme .. " " .. host .. " -> " .. URL); return end
176 local hd = {Host = host}
177 local p, c, h, s = http.request{method = "HEAD", redirect = false, url = URL, headers = hd}
178 if nil == p then E(c .. "! " .. check .. " " .. host .. " -> " .. URL) else
179 l = h.location
180 if nil ~= l then
181 local pu = url.parse(l, defaultURL)
182 if l == URL then
183 E("redirect loop! " .. check .. " " .. host .. " -> " .. URL)
184 else
185 if nil == pu.host then
186 W("no location host! " .. check .. " " .. host .. " -> " .. URL .. " -> " .. l)
187 checkURL(host, PU.scheme .. "://" .. PU.host .. l, r + 1)
188 else
189 if testing("Protocol") and pu.scheme ~= PU.scheme then
190 W("protocol changed during redirect! " .. check .. " " .. host .. " -> " .. URL .. " -> " .. l)
191 end
192 checkURL(pu.host, l, r + 1)
193 end
194 end
195 end
196 end
197end
198
199local checkPaths = function (host, ip, path)
200 I(" Checking IP " .. host .. " -> " .. ip .. " " .. path)
201 for i, s in pairs(referenceDebs) do
202 if testing("http") then checkURL(host, "http://" .. ip .. path .. s) end
203 if testing("https") then checkURL(host, "https://" .. ip .. path .. s) end
204 end
205
206 for i, s in pairs(releases) do
207 for j, k in pairs(releaseFiles) do
208 if testing("http") then checkURL(host, "http://" .. ip .. path .. "merged/dists/" .. s .. k) end
209 if testing("https") then checkURL(host, "https://" .. ip .. path .. "merged/dists/" .. s .. k) end
210 end
211 end
212end
213
214local execute = function (s)
215 D("executing " .. s)
216 os.execute(s)
217end
218
219forkIP = function (orig, host)
220 if nil == host then host = orig end
221 local po = url.parse("http://" .. orig, defaultURL)
222 local ph = url.parse("http://" .. host, defaultURL)
223 gatherIPs(ph.host)
224 for k, v in pairs(IP[ph.host]) do
225 if v == "A" then
226 if testing("IPv4") then execute("sleep 1; ionice -c3 ./mirror-checker.lua " .. sendArgs .. " " .. host .. " " .. k .. " &") end
227 elseif v == "AAAA" then
228 if testing("IPv6") then execute("sleep 1; ionice -c3 ./mirror-checker.lua " .. sendArgs .. " " .. host .. " [" .. k .. "] &") end
229 elseif v == "CNAME" then
230 forkIP(orig, k)
231 end
232 end
233end
234
235checkIP = function (orig, host, path, ip)
236 if nil ~= ip then
237 checkPaths(orig, ip, path)
238 else
239 D("checkIP " .. orig .. " " .. host)
240 gatherIPs(host)
241 for k, v in pairs(IP[host]) do
242 if v == "A" then
243 if testing("IPv4") then checkPaths(orig, k, path) end
244 elseif v == "AAAA" then
245 if testing("IPv6") then checkPaths(orig, "[" .. k .. "]", path) end
246 elseif v == "CNAME" then
247 checkIP(orig, k, path)
248 end
249 end
250 end
251end
252
253local checkHost = function (host, path, ip)
254 if nil == path then path = "/" else
255 if "/" == path:sub(-1, -1) then
256 W("slash at end of BaseURL in mirror_list.txt! " .. host .. " " .. path)
257 else
258 path = path .. "/"
259 end
260 end
261 checkIP(host, host, path, ip)
262end
263
264local downloads = function (host, URL, IP)
265 if nil == URL then URL = "/" end
266 if nil == IP then IP = "" else IP = "-" .. IP end
267 local log = " --rejected-log=results/wget-%s_REJECTS-" .. host .. IP .. ".log -a results/wget-%s-" .. host .. IP ..".log "
268 I("starting file download commands for " .. host .. " " .. URL)
269 local cm = "ionice -c3 " .. download .. log:format("debs", "debs")
270 for i, s in pairs(referenceDebs) do
271 cm = cm .. " https://" .. host .. URL .. "/" .. s
272 end
273 for i, s in pairs(releases) do
274 execute(cm .. " &")
275 cm = "ionice -c3 " .. download .. log:format(s, s)
276 for j, k in pairs(releaseFiles) do
277 cm = cm .. " https://" .. host .. URL .. "/merged/dists/" .. s .. k
278 end
279 end
280 execute(cm .. " &")
281end
282
283local getMirrors = function ()
284 local mirrors = {}
285 local host = ""
286 local m = {}
287 local URL = "https://" .. options.referenceSite.value .. "/mirror_list.txt"
288 I("getting mirrors.")
289 local p, c, h = http.request(URL)
290 if nil == p then E(c .. " fetching " .. URL) else
291 for l in p:gmatch("\n*([^\n]+)\n*") do
292 local t, d = l:match("(%a*):%s*(.*)")
293 if "FQDN" == t then
294 if "" ~= host then
295 mirrors[host] = m
296 m = {}
297 end
298 host = d
299 end
300 m[t] = d
301 end
302 if "" ~= host then
303 mirrors[host] = m
304 end
305 end
306 return mirrors
307end
308
309
310if 0 ~= #args then
311 local option = ""
312 for i, a in pairs(args) do
313 if ("--help" == a) or ("-h" == a) then
314 print("I should write some docs, huh?")
315 elseif "--version" == a then
316 print("mirror-checker-lua version 0.1 alpha")
317 elseif "-v" == a then
318 verbosity = verbosity + 1
319 sendArgs = sendArgs .. a .. " "
320 elseif "-q" == a then
321 verbosity = -1
322 sendArgs = sendArgs .. a .. " "
323 elseif "-k" == a then
324 keep = true
325 elseif "--" == a:sub(1, 2) then
326 local s, e = a:find("=")
327 if nil == s then e = -1 end
328 option = a:sub(3, e - 1)
329 local o = options[option]
330 if nil == o then
331 print("Unknown option --" .. option)
332 option = ""
333 else
334 option = a
335 sendArgs = sendArgs .. a .. " "
336 local s, e = a:find("=")
337 if nil == s then e = 0 end
338 option = a:sub(3, e - 1)
339 if "table" == options[option].typ then
340 local result = {}
341 for t in (a:sub(e + 1) .. ","):gmatch("([+%-]?%w*),") do
342 local f = t:sub(1, 1)
343 local n = t:sub(2, -1)
344 if ("+" ~= f) and ("-" ~= f) then
345 table.insert(result, t)
346 end
347 end
348 if 0 ~= #result then
349 options[option].value = result
350 else
351 for t in (a:sub(e + 1) .. ","):gmatch("([+%-]?%w*),") do
352 local f = t:sub(1, 1)
353 local n = t:sub(2, -1)
354 if "+" == f then
355 table.insert(options[option].value, n)
356 elseif "-" == f then
357 local r = {}
358 for i, k in pairs(options[option].value) do
359 if k ~= n then table.insert(r, k) end
360 end
361 options[option].value = r
362 end
363 end
364 end
365 else
366 options[option].value = a
367 end
368 option = ""
369 end
370 elseif "-" == a:sub(1, 1) then
371 print("Unknown option " .. a)
372 else
373 table.insert(arg, a)
374 end
375 end
376end
377
378--printTable(options.tests.value, "", "tests")
379
380execute("mkdir -p results")
381
382if 0 < #arg then
383 if nil ~= arg[2]
384 log = io.open ("mirror-checker-lua_" .. arg[1] .. "_" .. arg[2] .. ".log", "a+")
385 else
386 log = io.open ("mirror-checker-lua_" .. arg[1] .. ".log", "a+" [, mode])
387 end
388 local pu = url.parse("http://" .. arg[1], defaultURL)
389 I("Starting tests for " ..pu.host .. " with these tests - " .. table.concat(options.tests.value, ", "))
390 if nil ~= arg[2] then I(" Using IP " .. arg[2]) end
391 if testing("Integrity") or testing("Updated") then
392 if not keep then execute("rm -fr results/" .. pu.host) end
393 downloads(pu.host, pu.path, arg[2])
394 checkExes("mirror-checker.lua " .. sendArgs)
395 checkExes(download)
396 end
397 checkHost(pu.host, pu.path, arg[2])
398else
399 if not keep then os.execute("rm -f results/*.log") end
400 log = io.open ("mirror-checker-lua.log", "a+")
401 I("Starting tests " .. table.concat(options.tests.value, ", "))
402 execute("mkdir -p results")
403 local mirrors = getMirrors()
404 mirrors[options.referenceSite.value] = nil
405-- checkHost(options.referenceSite.value)
406 forkIP(options.referenceSite.value)
407-- checkHost("deb.devuan.org")
408 forkIP("deb.devuan.org")
409 for k, m in pairs(mirrors) do
410 local pu = url.parse("http://" .. m.BaseURL, defaultURL)
411-- checkHost(pu.host)
412 forkIP(m.BaseURL)
413 checkExes("mirror-checker.lua " .. sendArgs)
414 if testing("Integrity") or testing("Updated") then checkExes(download) end
415 end
416 while 1 <= checkExes("mirror-checker.lua " .. sendArgs) do os.execute("sleep 30") end
417 if testing("Integrity") or testing("Updated") then
418 while 1 < checkExes(download) do os.execute("sleep 30") end
419 end
420end