Следите за руками:
21:23:25 GET "captive.apple.com" /hotspot-detect.html HTTP/1.0 302 0 - "CaptiveNetworkSupport-346 wispr" - - - "http://captive.apple.com/hotspot-1.html"
21:23:25 GET "captive.apple.com" /hotspot-1.html HTTP/1.0 403 86 - "CaptiveNetworkSupport-346 wispr" - - - -
21:23:26 GET "captive.apple.com" /hotspot-detect.html HTTP/1.1 200 467 - "Mozilla/5.0 (iPhone; CPU iPhone OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Mobile/14C92" - - - -
21:23:26 GET "captive.apple.com" /hotspot-detect.html HTTP/1.0 200 68 - "CaptiveNetworkSupport-346 wispr" - - - -
21:23:28 GET "captive.apple.com" /hotspot-2.html HTTP/1.1 302 0 - "Mozilla/5.0 (iPhone; CPU iPhone OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Version/10.0 Mobile/14C92 Safari/602.1" - - - "http://мой-URL"
Первые два ответа — это чтобы показать wispr такую бяку, чтоб он захотел вмешаться. wispr и на Mac OS X, и на iOS заканчивается на " wispr". Сразу 403 или 200 без Success как-то не очень работают, а 302 — вполне, но раз 302, то и по другому адресу надо что-то ответить. Потом запускается CNS (нет Safari), и ему отдаётся страничка, на которой есть Success и скрипт, который через
полсекунды создаст обычную (без _blank) ссылку с абсолютным URL (который с двоечкой) и нажмёт по ней click()'ом. При отдаче страницы на stateful сервере помечается этот комп. Либо переход по ссылке, либо Success и код 200 на странице, либо просто это делается каждый раз после первого открытия, пока не разобрался, что-то из этого заставляет сделать ещё один запрос wispr, и на этот раз помеченному компу отдаётся самый обычный Success, а когда через полсекунды пойдёт переход по ссылке, CNS уже будет в состоянии «Готово» и направит переход в настоящий Safari, а сам закроется. При обнаружении открытия ссылки настоящим Safari пометка с компа сбрасывается.
Бывает, зацикливается в CNA, но после увеличения задержки на секунду вроде нормализовалось.
with AWS.Status;
with AWS.Response;
package Worker_Echoes.Apple.Captive is
function Service (Request : AWS.Status.Data)
return AWS.Response.Data;
end Worker_Echoes.Apple.Captive;
with Ada.Strings.Fixed;
with AWS.URL;
with AWS.Messages;
with Worker_Echoes.Protected_Strings;
with Worker_Echoes.Config;
package body Worker_Echoes.Apple.Captive is
use AWS;
use all type AWS.Messages.Status_Code;
Last_CNA_CNS_Peername : Protected_Strings.Protected_String;
-------------
-- Service --
-------------
function Service (Request : Status.Data) return Response.Data is
URL_Object : constant URL.Object := Status.URI (Request);
URL_String : constant String := URL.URL (URL_Object);
User_Agent : constant String := Status.User_Agent (Request);
begin
if User_Agent'Length >= 6 and then User_Agent (User_Agent'Last - 5 .. User_Agent'Last) = " wispr" then
if Last_CNA_CNS_Peername.Get = Status.Peername (Request) then
return Response.Build
(Status_Code => S200,
Content_Type => "text/html; charset=utf-8",
Message_Body => "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
elsif URL_String = "http://captive.apple.com/hotspot-1.html" then
return Response.Build
(Status_Code => S200,
Content_Type => "text/html; charset=utf-8",
Message_Body => "<HTML><HEAD></HEAD><BODY></BODY></HTML>");
else
return Response.URL ("http://captive.apple.com/hotspot-1.html");
end if;
elsif Ada.Strings.Fixed.Index (User_Agent, "Safari") = 0 then
Last_CNA_CNS_Peername.Set (Status.Peername (Request));
-- 1. Not sure if intact "<BODY>Success</BODY>" matters, but probably yes.
-- 2. There was no delay previously, thus A had to be created before BODY is processed.
-- Now it's possible to click A with id, but touching working code was avoided.
return Response.Build
(Status_Code => S200,
Content_Type => "text/html; charset=utf-8",
Message_Body => "<HTML><HEAD><SCRIPT>" &
"window.setTimeout (function () {" &
"var A = document.createElement (""a"");" &
"A.setAttribute (""href"", ""http://captive.apple.com/hotspot-2.html"");" &
"var Body = document.getElementsByTagName (""body"");" &
"if (Body.length > 0) {" &
"Body = Body [0];" &
"} else {" &
"Body = document.createElement (""body"");" &
"document.getElementsByTagName (""html"") [0].appendChild (Body);" &
"}" &
"Body.appendChild (A);" &
"A.click ();" &
"}, 1000);" &
"</SCRIPT>" &
"<STYLE>body { display: none; }</STYLE>" &
"<TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>");
else
Protected_Strings.Reset (Last_CNA_CNS_Peername, Status.Peername (Request)); -- reset if matches
return Response.URL (Config.Get_Target_URL);
end if;
end Service;
end Worker_Echoes.Apple.Captive;