为什么 Android 的强制门户检测不触发浏览器窗口?

为什么 Android 的强制门户检测不触发浏览器窗口?

我有一个 Raspberry Pi,它使用 nginx 托管一个简单的网站。 RPi 充当无线接入点 - 用户可以连接到其无线网络,RPi 为他们提供一个 IP(它运行 DHCP 服务器),然后他们可以访问该站点。

因为 RPi 实际上并不为用户提供互联网(仅此一个站点),所以我让用户更容易找到该站点。我不知道该站点的确切 URL,而是告诉 DHCP 服务器告诉客户端使用的 dns 服务器 (dnsmasq),以解析对我的 RPi (192.168.30.1) 的 LAN IP 的所有查询。

此时,我的 nginx 配置中有一个条目:

  • 如果用户请求的host字段不是MyRPiServer.com,则发送302重定向到MyRPiServer.com
  • 如果主机是MyRPiServer.com,则为本地网站提供服务

这效果棒极了。

我想更进一步。当 Android 连接到无线网络时,它会尝试连接到http://connectivitycheck.gstatic.com/generate_204(或其他类似的谷歌页面之一)专门检查请求是否被重定向。如果收到 204 代码,则认为一切正常。如果没有,则假定它位于强制门户后面,并弹出一个浏览器窗口,打开强制门户登录。

出于某种原因,当我告诉 nginx 使用 302 重定向甚至 200(带有一些文本)响应对generate_204 页面的请求时,Android 不会弹出浏览器。

我有一个带有内置热点功能的 mikrotik 路由器,它确实让 android 弹出浏览器并使用强制门户登录(在同一台测试手机上)。当我查看它发送给我的客户端的流量时,它是一个简单的 HTTP 200,带有一些文本,就像我的一样。

似乎确实有效的一件事是,如果我禁用 DNS 服务器将所有内容解析为 192.168.30.1,并使用 iptables 将端口 80 重定向到 RPi 上的本地主机。

有谁知道为什么在 Android 的 Captive Portal 检测中重定向端口 80 有效,但配置 DNS 服务器将所有内容解析到 RPi 本地 IP 却不起作用?

查看此处找到的代码https://stackoverflow.com/a/14030276/4258196,看来 Android 唯一关心的是它是否可以连接到主机,以及是否返回 HTTP 204。就我而言,它肯定正在连接,并且绝对不会收到 204 返回(nginx 日志显示它发送 HTTP 302 和 HTTP 200)。

我的手机运行的是 Android 8,所以我想现在链接的代码可能有所不同。

答案1

我找到的解决方案是将我的 dnsmasq 设置为继续将所有内容解析为 192.168.30.1,但对于强制门户测试服务器有一些例外:

10.45.12.1 clients3.google.com
10.45.12.1 clients.l.google.com
10.45.12.1 connectivitycheck.android.com
10.45.12.1 connectivitycheck.gstatic.com
10.45.12.1 play.googleapis.com

基本上,如果有任何尝试使用我们的 DNS 服务器解析上述域,他们会得到 10.45.12.1 的回复。

10.45.12.1是一个随机IP,不属于任何东西。它不必是 192.168.30.1。

域列表来自这里

完成此操作后,只要您连接到 RPi 的 WiFi,它就会弹出显示我的站点的浏览器页面。

这是一个解决方案,但并不能真正回答为什么会发生这种情况。如果有人能解释一下,我将不胜感激。

编辑:

使用此解决方案,如果我多次连接和断开设备上的 WiFi,Android 有时会弹出登录页面,有时则不会。对于任何做类似事情的人,最后,为了更好的解决方案,我选择了这个:

  • 让 DNSmasq 将所有内容解析为 10.45.12.1(或 192.168.30.0/24 子网之外的任何内容)
    • 它必须位于 192.168.30.0/24 子网(或任何 LAN 子网)之外,否则客户端将尝试使用 ARP 来找出给定设备的 MAC 地址,并且会失败,因为该设备实际上并不存在
  • 让 iptables 将来自 wifi 接口的端口 80 转发到本地主机

这适用于 Android、OS X 和 Windows。我没有 iOS 设备来测试这个。根据,iOS 设备可能需要一些额外的工作。

我仍然很好奇为什么这是必要的,以及为什么将所有内容解析为 192.168.30.1 最初不起作用。

答案2

我花了很多时间试图弄清楚这一点并最终做到了。你可以在这里看到代码https://github.com/tretos53/Captive-Portal

它是指向公共 IP、iptables 和 nginx 重定向的域的组合。

答案3

造成这种情况的原因有很多,也可能是您的特定硬件手机制造商造成的。我目前为 Mongoose OS 构建了我的 Captive Portal 库,用于响应所有带有设备 IP 的 DNS 查询,并且除了三星设备之外的设备没有任何问题,这些设备似乎需要 200 文本响应。

这是库堆栈: https://github.com/tripflex/captive-portal-wifi-stack

具体来说,这里是强制门户处理,以及我使用的端点的详细信息,以及自述文件中有关如何处理的详细信息:

https://github.com/tripflex/captive-portal

您将看到我用于三星设备的一种解决方案是使用元刷新标记返回 200 以及生成的 HTML 文件:

https://github.com/tripflex/captive-portal#cportalredirect_file-setting

<html>
   <head>
      <title>Redirecting to Captive Portal</title>
      <meta http-equiv='refresh' content='0; url=PORTAL_URL'>
   </head>
   <body>
      <p>Please wait, refreshing.  If page does not refresh, click <a href='PORTAL_URL'>here</a> to login.</p>
   </body>
</html>

但实际上最重要的是为每个特定设备设置端点处理:

https://github.com/tripflex/captive-portal#known-endpoints

  • /mobile/status.php安卓 8.0(三星 s9+)
  • /generate_204安卓
  • /gen_204安卓
  • /ncsi.txt视窗
  • /hotspot-detect.htmliOS/OSX
  • /hotspotdetect.htmliOS/OSX
  • /library/test/success.htmliOS系统
  • /success.txt操作系统X
  • /kindle-wifi/wifiredirect.html根据请求使用 com.android.captiveportallogin 进行 Kindle
  • /kindle-wifi/wifistub.htmlKindle 在使用强制门户登录窗口请求之前(也许是为了检测?)

相关内容