• 需要更新的 localhost 知识


     

    之前回答了知乎上的一个问题 以子域名的形式使用localhost,有什么坑吗?,本文是该回答的备份。

    一开始的回答:

    localhost 本身只是一个主机名,约定俗成指向回环地址 127.0.0.1 和 ::1,如果你新配的主机名不叫 localhost,那其实叫什么都无所谓,可以是 a.localhostb.localhost,也可以是 a.localb.local,具体取决于你本机的 hosts 和 DNS 配置。

    后来发现,我在没有修改 hosts 的情况下,竟然也能用 Chrome 访问 foo.localhost,事情并没有想象中简单。

    排查 DNS

    首先借助 dig 命令验证是不是 DNS 做了处理。

    C:\Users\keqingrong>dig foo.localhost +nocomments +nocmd
    
    ; <<>> DiG 9.14.0 <<>> foo.localhost +nocomments +nocmd
    ;; global options: +cmd
    ;foo.localhost.                   IN      A
    foo.localhost.            9247    IN      A       127.0.0.1
    ;; Query time: 5 msec
    ;; SERVER: 192.168.199.1#53(192.168.199.1)
    ;; WHEN: Tue Sep 28 22:47:51 ;; MSG SIZE  rcvd: 56

    192.168.199.1 是极路由的 DNS 地址,确实返回了 127.0.0.1。不过因为路由器会自动获取运营商分配的 DNS,所以还需要进一步验证,比如江苏电信的 DNS。

    C:\Users\keqingrong>dig @218.2.2.2 foo.localhost +nocomments +nocmd
    
    ; <<>> DiG 9.14.0 <<>> @218.2.2.2 foo.localhost +nocomments +nocmd
    ; (1 server found)
    ;; global options: +cmd
    ;foo.localhost.                   IN      A
    foo.localhost.            9967    IN      A       127.0.0.1
    ;; Query time: 5 msec
    ;; SERVER: 218.2.2.2#53(218.2.2.2)
    ;; WHEN: Tue Sep 28 22:51:14 ;; MSG SIZE  rcvd: 45

    由此可见是江苏电信的 DNS 对 foo.localhost 做了解析,所有的 DNS 都会这样吗?让我们再验证其他公共 DNS 的行为,比如 Cloudflare DNS。

    C:\Users\keqingrong>dig @1.1.1.1 foo.localhost +nocomments +nocmd
    
    ; <<>> DiG 9.14.0 <<>> @1.1.1.1 foo.localhost +nocomments +nocmd
    ; (1 server found)
    ;; global options: +cmd
    ;foo.localhost.                   IN      A
    .                       86400   IN      SOA     a.root-servers.net. nstld.verisign-grs.com. 2021092800 1800 900 604800 86400
    ;; Query time: 174 msec
    ;; SERVER: 1.1.1.1#53(1.1.1.1)
    ;; WHEN: Tue Sep 28 22:54:51 ;; MSG SIZE  rcvd: 115

    Cloudflare DNS 只返回了一条 SOA 记录,看来并不是所有 DNS 都会将 foo.localhost 解析到 127.0.0.1

    SOA 记录: 起始授权机构(Start Of Authority),用于标识 NS 记录中的主服务器

    在 macOS 上测试的结果和 Windows 一致。

    $ dig foo.localhost +nocomments +nocmd
    ;foo.localhost.			IN	A
    foo.localhost.		8473	IN	A	127.0.0.1
    ;; Query time: 2 msec
    ;; SERVER: 192.168.199.1#53(192.168.199.1)
    ;; WHEN: 二  9 28 23:00:42 CST 2021
    ;; MSG SIZE  rcvd: 56

    如果断网,本机将无法顺利解析 foo.localhost,使用 dig 或 ping 命令获取不到回环地址 IP,因为依赖上游 DNS。但是此时浏览器依然顽强,可以正常访问 http://foo.localhost/,一定是浏览器做了特殊处理。

    像 curl 只处理了 localhost,其他域名依赖 DNS 解析,因此不支持 foo.localhost,见 document the new 'localhost' treatment

    排查浏览器

    查阅 Chromium 代码,找到了定义于 net/base/url_util.cc 的 IsLocalhost() 函数,localhost 和以 .localhost 结尾的 host 都视作本地回环地址的主机名:

    // https://github.com/chromium/chromium/blob/ef3c0b7e3f9387e57570cdfd6c7e65ee5add4ec9/net/base/url_util.cc#L388
    bool IsLocalhost(const GURL& url) {
      return HostStringIsLocalhost(url.HostNoBracketsPiece());
    }
    
    bool HostStringIsLocalhost(base::StringPiece host) {
      IPAddress ip_address;
      if (ip_address.AssignFromIPLiteral(host))
        return ip_address.IsLoopback();
      return IsLocalHostname(host);
    }
    
    bool IsLocalHostname(base::StringPiece host) {
      std::string normalized_host = base::ToLowerASCII(host);
      // Remove any trailing '.'.
      if (!normalized_host.empty() && *normalized_host.rbegin() == '.')
        normalized_host.resize(normalized_host.size() - 1);
    
      return normalized_host == "localhost" ||
             IsNormalizedLocalhostTLD(normalized_host);
    }
    
    bool IsNormalizedLocalhostTLD(const std::string& host) {
      return base::EndsWith(host, ".localhost");
    }

    相应的的单元测试:

    // https://github.com/chromium/chromium/blob/ef3c0b7e3f9387e57570cdfd6c7e65ee5add4ec9/net/base/url_util_unittest.cc#L447
    TEST(UrlUtilTest, IsLocalhost) {
      EXPECT_TRUE(HostStringIsLocalhost("localhost"));
      EXPECT_TRUE(HostStringIsLocalhost("localHosT"));
      EXPECT_TRUE(HostStringIsLocalhost("localhost."));
      EXPECT_TRUE(HostStringIsLocalhost("localHost."));
      EXPECT_TRUE(HostStringIsLocalhost("127.0.0.1"));
      EXPECT_TRUE(HostStringIsLocalhost("127.0.1.0"));
      EXPECT_TRUE(HostStringIsLocalhost("127.1.0.0"));
      EXPECT_TRUE(HostStringIsLocalhost("127.0.0.255"));
      EXPECT_TRUE(HostStringIsLocalhost("127.0.255.0"));
      EXPECT_TRUE(HostStringIsLocalhost("127.255.0.0"));
      EXPECT_TRUE(HostStringIsLocalhost("::1"));
      EXPECT_TRUE(HostStringIsLocalhost("0:0:0:0:0:0:0:1"));
      EXPECT_TRUE(HostStringIsLocalhost("foo.localhost"));
      EXPECT_TRUE(HostStringIsLocalhost("foo.localhost."));
      EXPECT_TRUE(HostStringIsLocalhost("foo.localhoST"));
      EXPECT_TRUE(HostStringIsLocalhost("foo.localhoST."));
    }

    该特性最早是在 2015 年 6 月的一次提交中引入,见 Resolve RFC 6761 localhost names to loopback

    Gecko 相关的代码位于 netwerk/dns/DNS.cpp

    // https://github.com/mozilla/gecko-dev/blob/a8f7e8c66bedda0b3cfbd52494cc2e9fc12f606a/netwerk/dns/DNS.cpp#L168
    bool IsLoopbackHostname(const nsACString& aAsciiHost) {
      // If the user has configured to proxy localhost addresses don't consider them
      // to be secure
      if (StaticPrefs::network_proxy_allow_hijacking_localhost()) {
        return false;
      }
    
      nsAutoCString host;
      nsContentUtils::ASCIIToLower(aAsciiHost, host);
    
      return host.EqualsLiteral("localhost") ||
             StringEndsWith(host, ".localhost"_ns);
    }

    是在 2020 年 10 月的一次提交中引入,见 Bug 1220810 - Hardcode localhost to loopback

    从 Chromium 的提交信息中可以看到两个 IETF 链接:

    其中提到将如下域名作为特殊用途的保留域名:

    • 私有地址: *.in-addr.arpa.
    • 部分顶级域名: "test.", "localhost.", "invalid."
    • 示例域名: "example.", "example.com.", "example.net.", "example.org."

    在域名分配机构 IANA(Internet Assigned Numbers Authority, 互联网数字分配机构)的网站上也可以查到相关信息,见 IANA-managed Reserved Domains,他们还提供了一份更详细的域名数据 Special-Use Domain Names,包含了所有用于特殊用途的域名,可以在其中找到顶级域名 local. 和 localhost.

    经验证,在不改本机 hosts 的前提下,Chromium 系的 Chrome、Edge,Firefox,Safari,以及 Windows 10 21H1 上的 IE,打开 foo.localhost 都能访问到本地的 nginx 服务,唯一无法识别的是一台 Windows 10 1904 上的 IE。

    相关链接

  • 相关阅读:
    web第一章(html)
    面向对象第七章,内存的管理,面向对象三大特征封装、继承、多态
    面向对象第六章(接口、强转、多态)
    面向对象第五章(封装、抽象类、匿名内部类)
    面向对象第四章(封装、static)
    面向对象第二章(引用类型数组、继承)
    python 生成随机数
    python 生成测试报告
    python 邮件发送
    python 日志打印
  • 原文地址:https://www.cnblogs.com/mouseleo/p/16154248.html
Copyright © 2020-2023  润新知