zoukankan      html  css  js  c++  java
  • 网页如何获取往返数据包的 TTL

    前言

    Web 前端思考题:如何获取往返数据包的 TTL

    注意,这里说的是「往返」,即去程和返程,也就是「服务端」收到「客户端数据包」的 TTL,和「客户端」收到「服务端数据包」的 TTL。

    注意,这里说的是「接收」,毕竟发送时的 TTL 毫无意义,服务端固定已知,客户端通常是 64 或 128(和操作系统相关)。

    这个问题的初衷,是想通过 JS 检测用户和服务器之间的往返路由数。虽然大多情况下往返链路相近,但也存在差异较大的情况。例如访问某些香港服务器,去程电信直连,而返程则会到日本绕一圈,链路差异非常大。

    去程

    去程 TTL 很容易获取,直接读取 Web 服务器的 socket 即可(例如通过 getsockopt)。或者用 raw socket/libpcap 抓包也不难实现。

    返程

    返程 TTL 就没那么容易获取了。

    也许你会说,可通过服务器 traceroute 反查。这种方案虽然可行,但并不准确。如今用户几乎都在内网中,反查只能到用户的公网路由器,内网的路由数仍无法获取。而且反查效率很低,需要发不少数据包。

    但用 JS 读取数据包 TTL 更不可行,毕竟浏览器功能十分有限。抓包这种操作,想都不用想;而 getsockopt 这类高权限 API,浏览器显然不可能提供。因此我们得另辟蹊径。

    思路

    设想下,假如客户端能把某个「收到的数据包」封装在「另一种数据包」的内容里回传给服务器,那我们就可以在服务器上获取返程信息了。

    事实上,这种情况是存在的!「ICMP 端口不可到达」协议专做这事。

    那么,客户端在什么情况下会触发这种 ICMP?只要浏览器在收到服务器数据包之前断开连接即可。之后数据包达到时,由于操作系统找不到目标端口对应的连接,只能丢弃该包,同时回复一个「ICMP 端口不可到达」告知服务器。

    该 ICMP 的内容部分,正是被丢弃包的网络层和传输层头,包含了我们想要的返程信息。

    因此我们的核心思路:

    1. JS 向服务器发包

    2. 服务器稍作延迟,回复任意内容

    3. JS 在收到包之前释放连接

    4. 服务器抓取 ICMP (type=3, code=3) 类型的包

    由于 TCP 连接是操作系统维护的,JS 难以精确控制释放时间,因此我们使用更简单的 UDP。通过 WebRTC 即可实现 UDP 连接的创建和关闭。

    并且 TCP 是有状态的,连接断开后 NAT 会删除条目,服务器返回的数据包可能进不了内网。而 UDP 是无状态的,本地关闭连接对 NAT 毫无感知,此后一段时间里数据仍可进入内网。

    实现

    实现比思考简单得多,这里就不讲解了。

    需要注意的是,JS 向服务器发送 UDP 包时需携带一个 uuid 数据,用于之后获取数据时的关联。

    在线演示:https://www.etherdream.com/test/stunex.html

    局限

    不过并非所有情况下都能成功获取,例如有些操作系统禁用了端口不可到达的响应,有些运营商会丢弃这类 ICMP 包。

    如果演示页面显示 fail,那就是无法获取了。(如果是 network err 可能测试服务关了~)

    当然,本文纯属开脑洞而已,顺便分享一点点网络小知识~

  • 相关阅读:
    剑指offer51-正则表达式匹配
    剑指offer50-构建乘积数组
    剑指offer49-数组中的重复
    PHP系列笔记——Zend_Controller工作流程
    多态与重载
    读取文件数据的大概流程
    基于HTTP协议下载文件的实现
    C++中的面向对象笔记
    firebreath注册接口
    python读取excelxlsx,写入excel
  • 原文地址:https://www.cnblogs.com/index-html/p/js-get-round-trip-ttl.html
Copyright © 2011-2022 走看看