aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rwxr-xr-xapt-panopticon.lua105
2 files changed, 76 insertions, 31 deletions
diff --git a/README.md b/README.md
index 94b3c65..5db696e 100644
--- a/README.md
+++ b/README.md
@@ -31,12 +31,12 @@ in someplace like `/usr/local/bin` and make sure it is executable.
31It should run on any recent Linux, you'll need to have the following 31It should run on any recent Linux, you'll need to have the following
32installed - 32installed -
33 33
34* curl
34* dig, part of BIND. On Debian based systems it'll be in the dnsutils package. 35* dig, part of BIND. On Debian based systems it'll be in the dnsutils package.
35* flock, on Debian based systems it'll be in the util-linux package. 36* flock, on Debian based systems it'll be in the util-linux package.
36* ionice, on Debian based systems it'll be in the util-linux package. 37* ionice, on Debian based systems it'll be in the util-linux package.
37* luajit 38* luajit
38* LuaSocket, on Debian based systems it'll be in the lua-socket package. 39* LuaSocket, on Debian based systems it'll be in the lua-socket package.
39* LuaSec, on Debian based systems it'll be in the lua-sec package.
40* wget 40* wget
41 41
42 42
diff --git a/apt-panopticon.lua b/apt-panopticon.lua
index 227bec1..c536990 100755
--- a/apt-panopticon.lua
+++ b/apt-panopticon.lua
@@ -107,7 +107,6 @@ local logFile
107local socket = require 'socket' 107local socket = require 'socket'
108local ftp = require 'socket.ftp' 108local ftp = require 'socket.ftp'
109local http = require 'socket.http' 109local http = require 'socket.http'
110local https = require 'ssl.https' -- See https://github.com/brunoos/luasec/wiki/LuaSec-0.6 for docs.
111local url = require 'socket.url' 110local url = require 'socket.url'
112 111
113 112
@@ -286,42 +285,87 @@ checkHEAD = function (host, URL, r, retry)
286 if not testing(PU.scheme, host) then D("Not testing " .. PU.scheme .. " " .. host .. " -> " .. URL); return end 285 if not testing(PU.scheme, host) then D("Not testing " .. PU.scheme .. " " .. host .. " -> " .. URL); return end
287 -- TODO - Perhaps we should try it anyway, and mark it as a warning if it DOES work? 286 -- TODO - Perhaps we should try it anyway, and mark it as a warning if it DOES work?
288 if "https" == PU.scheme and options.roundRobin.value == host then D("Not testing " .. PU.scheme .. " " .. host .. " -> " .. URL .. " mirrors shouldn't have the correct cert."); return end 287 if "https" == PU.scheme and options.roundRobin.value == host then D("Not testing " .. PU.scheme .. " " .. host .. " -> " .. URL .. " mirrors shouldn't have the correct cert."); return end
289 local hd = {} 288
290 if pu.host ~= PU.host then hd = {Host = host} end 289 --[[ Using curl command line -
291 local htp = http; 290 -I - HEAD
292 if PU.scheme == "https" then htp = https end 291 --connect-to IP - connect to IP, but use SNI from URL.
293 -- NOTE - the docs for lua-sec say that redirect isn't supported is version 0.6, no idea if that means it ignores redirections like we want. 292 -header "" - add extra headers.
294 -- TODO - find out! 293 -L - DO follow redirects.
295 -- The protocol and options are lua-sec specific arguments. 294 --max-redirs n - set maximum redirects, default is 50, -1 = unlimited.
296 local p, c, h, s = htp.request{method = "HEAD", redirect = false, url = URL, headers = hd, protocol = "any", options = "all"} 295 --retry n - maximum retries, default is 0, no retries.
297 if nil == s then s = "" end 296 -o file - write to file instead of stdout.
298 if nil == p then 297 --path-as-is - https://curl.haxx.se/libcurl/c/CURLOPT_PATH_AS_IS.html might be useful for URL_sanity.
299 E(" " .. c .. " " .. s .. "! " .. check .. " " .. host .. " -> " .. URL, PU.scheme, "", host) 298 -s silent - don't output progress or error messages.
300 -- So far the only errors are "timeout", "Network is unreachable", and "closed", and I suspect "closed" is due to saturating my bandwidth. 299 --connect-timeout n - timeout in seconds.
301 if "timeout" == c then timeouts = timeouts + 1 end 300 Should return with error code 28 on a timeout?
302 if ("closed" == c) or ("Network is unreachable" == c) or ("timeout" == c) then checkHEAD(host, URL, r, retry + 1, timeouts) end 301 -D file - write the received headers to a file. This includes the status code and string.
303 else 302 ]]
304 if ("4" == tostring(c):sub(1, 1)) or ("5" == tostring(c):sub(1, 1)) then 303 local fname = host .. "_" .. PU.host .. "_" .. PU.path:gsub("/", "_") .. ".txt"
305 E(" " .. c .. " " .. s .. ". " .. check .. " " .. host .. " -> " .. URL, PU.scheme, "", host) 304 local hdr = ""
306 else 305 local IP = ""
307 I(" " .. c .. " " .. s .. ". " .. check .. " " .. host .. " -> " .. URL) 306 if pu.host ~= PU.host then
308 timeouts = timeouts - 1 -- Backoff the timeouts count if we managed to get through. 307 if "http" == PU.scheme then
308 hdr = '-H "Host: ' .. host .. '"'
309 end 309 end
310 l = h.location 310 IP = '--connect-to ' .. PU.host
311 if nil ~= l then 311 end
312 pu = url.parse(l, defaultURL) 312 local cmd = 'curl -I --retry 0 -s --path-as-is --connect-timeout 30 --max-redirs 0 ' .. IP .. ' ' .. '-o /dev/null -D results/"HEADERS_' .. fname .. '" ' ..
313 hdr .. ' -w "#%{http_code} %{ssl_verify_result} %{url_effective}\\n" ' .. PU.scheme .. '://' .. host .. PU.path .. ' >>results/"STATUS_' .. fname .. '"'
314 local rslt, status = execute(cmd)
315 os.execute("sleep 2")
316 os.execute('cat results/"HEADERS_' .. fname .. '" >>results/"STATUS_' .. fname .. '"; rm results/"HEADERS_' .. fname .. '"')
317 if "exit" == rslt then
318 if 28 == status then
319 E(" TIMEOUT!", PU.scheme, "", host)
320 timeouts = timeouts + 1
321 checkHEAD(host, URL, r, retry + 1, timeouts)
322 return
323 elseif 0 ~= status then
324 E(" The curl command return an error code of " .. status .. ", consult the curl manual for what this means.", PU.scheme, "", host)
325 checkHEAD(host, URL, r, retry + 1, timeouts)
326 return
327 end
328 elseif "signal" == rslt then
329 E(" The curl command was interupted by signal " .. status)
330 return
331 end
332 local rfile, e = io.open("results/STATUS_" .. fname, "r")
333 local code = "000"
334 local cstr = ""
335 local location = nil
336 if nil == rfile then W("opening results/STATUS_" .. fname .. " file - " .. e) else
337 for line in rfile:lines("*l") do
338 if "#" == line:sub(1, 1) then
339 code = line:sub(2, 4)
340 if ("https" == PU.scheme) and ("0" ~= line:sub(6, 6)) then E(" The certificate is invalid.", PU.scheme, "https", host) end
341 elseif "http" == line:sub(1, 4):lower() then
342 -- -2 coz the headers file gets a \r at the end.
343 cstr = line:sub(14, -2)
344 elseif "location" == line:sub(1, 8):lower() then
345 location = line:sub(11, -2)
346 end
347 end
348 end
349 os.execute('rm results/"STATUS_' .. fname .. '"')
350 if ("4" == tostring(code):sub(1, 1)) or ("5" == tostring(code):sub(1, 1)) then
351 E(" " .. code .. " " .. cstr .. ". " .. check .. " " .. host .. " -> " .. URL, PU.scheme, "", host)
352 else
353 I(" " .. code .. " " .. cstr .. ". " .. check .. " " .. host .. " -> " .. URL)
354 timeouts = timeouts - 1 -- Backoff the timeouts count if we managed to get through.
355 if nil ~= location then
356 pu = url.parse(location, defaultURL)
313 if (pu.scheme ~= PU.scheme) then 357 if (pu.scheme ~= PU.scheme) then
314 if testing("Protocol") then W(" protocol changed during redirect! " .. check .. " " .. host .. " -> " .. URL .. " -> " .. l, PU.scheme, "Protocol", host) end 358 if testing("Protocol") then W(" protocol changed during redirect! " .. check .. " " .. host .. " -> " .. URL .. " -> " .. location, PU.scheme, "Protocol", host) end
315 if (pu.host == host) and pu.path == PU.path then D("Not testing protocol change " .. URL .. " -> " .. l); return end 359 if (pu.host == host) and pu.path == PU.path then D("Not testing protocol change " .. URL .. " -> " .. location); return end
316 end 360 end
317 361
318 if l == URL then 362 if location == URL then
319 E(" redirect loop! " .. check .. " " .. host .. " -> " .. URL, PU.scheme, "", host) 363 E(" redirect loop! " .. check .. " " .. host .. " -> " .. URL, PU.scheme, "", host)
320 elseif nil == pu.host then 364 elseif nil == pu.host then
321 I(" relative redirect. " .. check .. " " .. host .. " -> " .. URL .. " -> " .. l) 365 I(" relative redirect. " .. check .. " " .. host .. " -> " .. URL .. " -> " .. location)
322 checkHEAD(host, PU.scheme .. "://" .. PU.host .. l, r + 1) 366 checkHEAD(host, PU.scheme .. "://" .. PU.host .. location, r + 1)
323 elseif (PU.host == pu.host) or (host == pu.host) then 367 elseif (PU.host == pu.host) or (host == pu.host) then
324 checkHEAD(pu.host, l, r + 1) 368 checkHEAD(pu.host, location, r + 1)
325 else 369 else
326 --[[ The hard part here is that we end up throwing ALL of the test files at the redirected location. 370 --[[ The hard part here is that we end up throwing ALL of the test files at the redirected location.
327 Not good for deb.debian.org, which we should only be throwing .debs at. 371 Not good for deb.debian.org, which we should only be throwing .debs at.
@@ -448,6 +492,7 @@ local getMirrors = function ()
448 I("getting mirrors.") 492 I("getting mirrors.")
449 local p, c, h = http.request(URL) 493 local p, c, h = http.request(URL)
450 if nil == p then E(c .. " fetching " .. URL) else 494 if nil == p then E(c .. " fetching " .. URL) else
495
451 for l in p:gmatch("\n*([^\n]+)\n*") do 496 for l in p:gmatch("\n*([^\n]+)\n*") do
452 local t, d = l:match("(%a*):%s*(.*)") 497 local t, d = l:match("(%a*):%s*(.*)")
453 d = string.lower(d) 498 d = string.lower(d)