终焉!Xray 系列教程 DNS 请求逻辑剖析—代理软件教程完结篇

本篇作为代理软件使用教程的最终章!

本人写这些系列教程并不是为了写教程而写教程,只是想把这些融会贯通,所以采用写出来的方式,边思考边记录,分享只是最后副产品。

这个教程有点难度,并不推荐新手强行理解。不过如果之前使用过 Xray 系列内核,对于该软件有过了解的话,这篇文章会帮你彻底理解 Xray 的运行逻辑,比如 DNS 检测网站出现的各种 DNS 服务商,到底是什么环节请求导致的等等。

客户端使用教程的话,写了 sing-box 系列教程,从零开始到入门阶段。如果不只是为了可以使用,而是想深入了解的话。看完该系列教程,其实你就可以真正入门代理软件了。之后又写了一篇 mihomo 的教程,彻底讲清了 Clash 系列 DNS 请求的逻辑。如果你看懂了这篇,对于 Clash系列 DNS 泄露问题,应该不会再有疑惑了。以及该篇还完整讲解了 Clash 系列包括 mihomo 的操作,如果你想入门使用 mihomo ,可以完整看完这篇教程,搭配官方文档的参数,应该不会有疑惑了。

这篇教程是 xray 教程,会彻底讲清 xray 的工作逻辑以及 DNS 如何请求,不过因为 xrayDNS 功能确实比 mihomo 的复杂很多,现在看不懂的话,等到使用时遇到问题再逐一对比,应该没什么问题。在教程中我不厌其烦的讲解不同场景下的请求逻辑,以及不同参数是否会对该请求产生影响,虽然有点啰嗦,但是的确把各种场景测试完成了。当然你也可以自己设计不同的测试环境,来佐证你的观点。

代理客户端使用教程本篇完结,以后也不会再更新了,或许等之后空闲有时间了,可以研究一下不同的底层传输协议。

[!NOTE]

小声逼逼:如果想自己掌握客户端使用方法,建议先研究一个代理客户端,不要跨平台研究。因为不同的客户端请求逻辑和处理数据的方式不同,前面跨平台会导致观点相互冲突,会让你停滞很久。等研究透一个客户端后,再去学习其他的客户端,就很容易入门并掌握了。

阅读指南!

  • xray 工作逻辑与 DNS 请求逻辑
  • 新手 xray 配置一份
  • 如何使用
  • 自定义 geoip 和 geosite 数据库
  • 小工具分享
  • 复活 Clash for Windows

检测网站为什么会出现 DNS 服务器的 IP 地址

分为两种情况:

1、DNS 检测时候,发起了对该服务器的请求,所以被记录后反馈给检测网站,因此被记录。

2、DNS 检测时候,没有发起对该服务器的请求,但是最终选择了直连出站。客户端将域名交给直连策略,直连 directdomainStrategy 决定了检测网站出现的服务器列表。

​ 如果使用 UseIP 规则,则出站发出的请求, 会先将域名通过内置服务器解析成 IP, 然后进行连接。所以检测网站域名列表会出现各种服务器的 IP,最基础的是代理服务器的 DNS 解析服务 IP,以及本地 IP 的请求记录。如果排在上面的 DNS 服务器默认的 disableFallbackfalse 的话,还会出现一些即便没有满足 expectIPs 的条件,但是还是会出现该服务器的 IP

​ 如果使用默认的规则,则出站请求直接交给系统 DNS 解析,检测网站当然也就无法检测到客户端配置的 DNS 列表的服务器,只能出现你本地的 DNS 服务商。

[!NOTE]

UseIP 在出站发出请求时,先将域名通过内置服务器解析成 IP, 然后进行连接。这个功能只对 freedom 类型出站才生效。如果是其他协议出站,比如 shadowsocksvless 等等,则无效参数,并不会使用内置服务器解析成 IP。(当然 UseIP 还对 sockopt 有效,在之前的版本中,当 Xray 尝试使用域名建立系统连接时,域名的解析由系统完成,不受 Xray 控制。这导致了在非标准 Linux 环境中无法解析域名等问题。为此,Xray 1.3.1Sockopt 引入了 Freedom 中的 domainStrategy,解决了此问题。)

domainStrategy: "AsIs"
"UseIP" | "UseIPv6v4" | "UseIPv6" | "UseIPv4v6" | "UseIPv4"
"ForceIP" | "ForceIPv6v4" | "ForceIPv6" | "ForceIPv4v6" | "ForceIPv4"

路由规则中,routing 下的 domainStrategy 值的匹配逻辑。

AsIs / IPIfNonMatch / IPOnDemand 这三个值决定了域名匹配不同类型规则后的执行逻辑,比如 portnetworkprotocol 时候,优先级是如何的?这个决定了域名请求的逻辑。

AsIs 时,域名 与 port 、network 、protocol 的优先级,谁在上面,先命中谁,就匹配对应的出站。

IPIfNonMatch 时,域名 与 port 、network 、protocol 的优先级,谁在上面,先命中谁,就匹配对应的出站。如果没有命中前面类型规则,遇到 IP 类规则,首先先跳过。如果第一次都没有匹配中,则把域名解析成 IP 再进行 IP 类规则匹配。域名检测网站总是会出现默认的第一个代理,那是因为在域名解析成 IP 时候,会把域名发送到 DNS 模块,根据 DNS 的规则发起解析,一般需要代理的域名服务器会发送到默认的远程服务器,所以这个默认的远程服务器会一直在。检测方式就是换掉默认的远程服务器节点,如果变成了更换后的服务器域名解析提供商,则结论正确。(已测试该结论正确!!!)

IPOnDemand 时,域名 与 IP 类规则 、port 、network 、protocol 的优先级,谁在上面,先命中谁,就匹配对应的出站。如果是遇到 IP 类规则,立即发起 DNS 域名解析,域名解析交给配置的 DNS 服务器处理。如果匹配的 IP 类规则不正确,则进行下一条的规则匹配,直到匹配成功,或者最终没有匹配走默认的第一条出站。一般检测网站都会出现恒定的默认出站节点的域名服务商,因为该节点是域名服务商比如 8.8.8.8 走默认 proxy 出站,然后被域名检测网站记录到。(通过更换 IP 规则匹配中的出站节点,默认出站 proxy 不变,检测网站中恒定出现默认 proxy 的域名提供商, IP 规则匹配中的出站节点因为不同的服务商变化,检测网站列表中域名服务商也随之变化。)(通过固定 IP 规则匹配到的出站方式,但是更换默认出站的 proxy 服务商,域名检测网站恒定出现了 IP 规则对应的固定节点所提供的域名解析服务器节点,一些变化出现的域名节点随默认 proxy 服务商变化而变化。)

开宗明义

Xray 在理论上,如果出现了 DNS 泄露,与 geosite 数据库无关,与域名解析策略 domainStrategy 的值(AsIs / IPIfNonMatch / IPOnDemand)也无关。

为什么会出现泄露情况,只和 DNS 模块的分流以及一些隐藏的默认参数导致的非法请求有关。(当然某些极少的情况下,上游数据库错误以至于 geosite 出错导致的错误请求,只是很少有这样的情况。如果出现了无法理解的请求错误,可以查一下是否出现了这样的情况。)

DNS 处理流程

开始之前,先明确两个关键的参数,会导致 DNS 请求的逻辑与理解的不一样。如果实际出现的情况与预想的结论不一样,结果一般都是这两个参数影响的。disableFallbacktrue 和 " local://119.29.29.29 " 中 “ local:// ”。

先贴一下官方文档对 DNS 处理流程的描述,v2flyxray 两者描述一致。至于为什么还要再详细讲解,主要原因还是官方文档整体讲解没问题,但是忽略了很多细节,话只讲到一半,以至于很多后面的情况只能通过数据来佐证。

命中了 hosts 中的「域名 - IP」、「域名 - IP 数组」映射,则将该 IP 或 IP 数组作为 DNS 解析结果返回。
命中了 hosts 中的「域名 - 域名」映射,则该映射的值(另一个域名)将作为当前要查询的域名,进入 DNS 处理流程,直到解析出 IP 后返回,或返回空解析。
没有命中 hosts,但命中了某(几)个 DNS 服务器中的 domains 域名列表,则按照命中的规则的优先级,依次使用该规则对应的 DNS 服务器进行查询。若命中的 DNS 服务器查询失败或 expectIPs 不匹配,则使用下一个命中的 DNS 服务器进行查询;否则返回解析得到的 IP。若所有命中的 DNS 服务器均查询失败或 expectIPs 不匹配,此时 DNS 组件:
默认会进行 「DNS 回退(fallback)查询」:使用「上一轮失败查询中未被使用的、且 skipFallback 为默认值 false 的 DNS 服务器」依次查询。若查询失败或 expectIPs 不匹配,返回空解析;否则返回解析得到的 IP。
若 disableFallback 设置为 true,则不会进行「DNS 回退(fallback)查询」。
既没有命中 hosts,又没有命中 DNS 服务器中的 domains 域名列表,则:
默认使用「skipFallback 为默认值 false 的 DNS 服务器」依次查询。若第一个被选中的 DNS 服务器查询失败或 expectIPs 不匹配,则使用下一个被选中的 DNS 服务器进行查询;否则返回解析得到的 IP。若所有被选中的 DNS 服务器均查询失败或 expectIPs 不匹配,返回空解析。
若「skipFallback 为默认值 false 的 DNS 服务器」数量为 0 或 disableFallback 设置为 true,则使用 DNS 配置中的第一个 DNS 服务器进行查询。查询失败或 expectIPs 不匹配,返回空解析;否则返回解析得到的 IP。

整体 DNS 请求逻辑分为三个大步骤,host,domains,非 domains

host 比较简单 ,只需要简单提及,官方文档描述如下:

  • 命中了 hosts 中的「域名 - IP」、「域名 - IP 数组」映射,则将该 IPIP 数组作为 DNS 解析结果返回。
  • 命中了 hosts 中的「域名 - 域名」映射,则该映射的值(另一个域名)将作为当前要查询的域名,进入 DNS 处理流程,直到解析出 IP 后返回,或返回空解析。

主要讲解 domains,非 domains,至于前面提及的 disableFallbacklocal:// ,会在请求逻辑中逐一讲解。

当一个域名命中 domains 时:
一个域名的 DNS 解析,首先判断是否命中 domains ,如果命中则选择命中的服务器按照其规则判断,返回为空或者 expectIPs 不匹配,依次按照 domains 命中的顺序逐一请求(第一次请求中,skipFallback 的值不参与域名解析的行为)。若所有命中的 DNS 服务器均查询失败或 expectIPs 不匹配,此时 DNS 服务器有以下判断:

​ 如果命中的 domains 服务器都没有匹配或者返回空,则进入 fallback 匹配。skipFallback 这个参数为默认,不需要的则 DNS 服务器必须设置跳过。使用「上一轮失败查询中未被使用的、且 skipFallback 为默认值 falseDNS 服务器」依次查询 (原始理解:依次选择命中的 domains 服务器中按照 skipfallbackfalse 的服务器再次请求;这个理解说法其实没错,官方文档倒是不容易理解),按照其规则进行判断。不符合规则的按照 skipfallbackfalse 的服务器下一个继续请求,直到符合的为止。如果都不符合,则返回空。

​ 这个时候有个关键问题,当 domains 列表服务器都没返回想要的值,且 skipFallbackfalse 的服务器又没返回想要的值,这个时候该怎么办?直接返回空吗?其实不然,这个时候需要看 DNS 模块全局的 disableFallback 值是什么?如果是 false 的默认值,则返回到未命中 domains 的状态,进行那些 skipFallback 值为 false 的服务器继续请求。如果没有设置 expectIPs,那么就会使用匹配到的第一个服务器作为返回 IP 使用。当然如果全局的 disableFallback 值是 true,那么则禁止 fallback 的查询,只能返回空值了。注意:全局的 disableFallback 值是 true,第一轮查询失败后,就不进行 skipFallback 的判断了,都不符合的话,只能返回空。(这些只影响最终得到的值,不影响 DNS 检测网站出现的域名服务商,只要发起过 DNS 域名请求,就会出现在检测网站上。)

所以在下面用于测试的 DNS 模块,如果设置 routingdomainStrategy 值为 IPOnDemand ,为什么会出现 DNS 泄露?

原因在于当匹配到 "IP" 规则类时那些 "DNS" 检测网站虽然没有命中 "domains" 列表,"disableFallback" 设置又为 "false",于是进入 "fallback" 的匹配。"https+local://223.5.5.5/dns-query" 这个服务器的 "skipFallback" 设置为 "false" ,于是该服务器被用于域名的 "DNS" 解析,泄露便发生了。
因为官方文档没有描述,只能通过测试得到的数据来佐证观点:

最后一个问题,命中了 "domains" ,但是最终返回的结果都不对,这时是终止请求直接返回空,还是回退到既没命中 "domians" 又没命中 "host" ,进行最终的 "Fallback" 请求。


需要设计一下实验的逻辑:
"domains" 选择非国内或者直接 "domain" 对应的 "DNS" 请求域名,"expectIPs" 为国内 "IP",域名服务器选择国外的服务器,这样都不匹配,然后非 "domains" 选择一些国内的服务器测试即可验证。

如果域名服务列表出站国内服务,则说明一个命中 "domains" 列表,但是没有得到 "expectIPs" 的 "IP" 后,又进行整体的 "fallback" 请求了,否则说明不进行最终的 "fallback" 请求,直接返回空解析,最终到服务器解析。


结论:::
命中 "domains" 服务器后,如果所有结果都不符合,最终还会来到未匹配 "host" 和未匹配 "domains" 的 "fallback" 匹配。

比如 "fallback" 列表中的国内服务商,通过上述描述情况会出现在 "DNS" 检测网站请求列表中。但是如果使用 "disableFallback" 为 "true" 参数,则不会出现。说明命中了 "domains" 服务器后,如果所有结果都不符合,最终会来到未匹配 "host" 和未匹配 "domains" 的 "fallback" 匹配模式,"disableFallback" 为 "true" 禁止了最终的 "fallback" 匹配(可以在第一个服务器选择国内的运营商,不会出现国内服务器,再次说明不会进行最终的匹配)。


又设计了实验,默认第一个为国内服务器,但是设置了 "skipFallback" 为 "true" 参数,命中了 "domains" 服务器后,所有结果不匹配,来到最终的 "fallback" 匹配,但是第一个设置了 "skipFallback" 为 "true" 参数,跳过这个服务商,最终匹配到最后一个 "119.29.29.29"。

测试用的 "DNS" 模块设置:
"servers": [
      {
        "address": "223.5.5.5",
        "skipFallback": true
      },
      {
        "address": "8.8.8.8",
        "domains": [
          "domain:surfsharkdns.com"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "1.1.1.1",
        "domains": [
          "domain:surfsharkdns.com"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      "119.29.29.29"
    ],
    "queryStrategy": "UseIPv4",
    "disableCache": false,
    "disableFallback": false,
    "disableFallbackIfMatch": false,
    "tag": "dns_inbound"

当一个域名没有命中 domains 时:
下面进入没有命中 domains 的匹配,默认使用「skipFallback 为默认值 falseDNS 服务器」依次查询,这里会忽略 domains 规则判断。 若第一个被选中的 DNS 服务器查询失败或 expectIPs 不匹配,则使用下一个被选中的 DNS 服务器进行查询;否则返回解析得到的 IP。若所有被选中的 DNS 服务器均查询失败或 expectIPs 不匹配,返回空解析。当然,如果全局的 disableFallback 值是 true,则直接使用第一个作为默认的 DNS 服务器请求。不过如果该 DNS 服务器的写法是 “local://” 类型,则返回为空或者不解析,这两个没有官方没有说明,不过不影响结果。

既没有命中 domains ,「skipFallback 为默认值 falseDNS 服务器」数量为 0disableFallback 设置为 true 时候,则使用 DNS 配置中的第一个 DNS 服务器进行查询。查询失败或 expectIPs 不匹配,返回空解析;否则返回解析得到的 IP。(这里注意:如果是使用 local:// ,则不遵循该规则,直接返回空。域名的解析最终只会发到服务器再解析)(还要一些检测网站会出现的迷惑域名提供商,比如 Aliyun 这个服务商,相同环境相同配置,不同的测试会随机出现。)

一些额外参数的注释

"local://223.5.5.5"223.5.5.5 两种类型写法?

若「skipFallback 为默认值 falseDNS 服务器」数量为 0disableFallback 设置为 true,则使用 DNS 配置中的第一个 DNS 服务器进行查询。查询失败或不匹配 expectIPs 列表,返回空解析;否则返回解析得到的 IP

上面这句话描述的情况必须有前提条件:服务器地址不能有 local://,默认的 233.5.5.5 才生效。

这个规则是生效的,第一个服务器只要不是 local:// 地址,那么就会最终被选择为默认,但是策略列表总有出现的服务商结果作为干扰导致前期理解错误。上面规则没错,出现运营商的服务器地址不是因为 local 或者不遵循路由规则的影响,主要还是国内服务商递归查询导致的结果。(基本所有国内的服务商都会产生运营商所在地的服务器结果)

"address": "local://223.5.5.5"
local:// 会导致默认的规则:《《《skipFallback 为默认值 falseDNS 服务器」数量为 0disableFallback 设置为 true 时候,则使用 DNS 配置中的第一个 DNS 服务器进行查询。》》》 失效,直接返回空解析。

"disableFallback": true
这个参数会让所有没有命中 domains 的域名请求,放弃 fallback 的匹配,直接使用默认的第一个作为请求服务器。但是,如果第一个服务器使用的地址为 local:// 类型,则依旧返回空解析。(即使没有命中 domains 列表中有 "skipFallback": false 的服务器也不会被采用;)

      {
        "address": "localhost",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },

和 

      {
        "address": "119.29.29.29",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },

产生的结果一样,不会像 local:// 那样,对请求造成影响。
      {
        "address": "local://localhost",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },

和

      {
        "address": "local://223.5.5.5",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },

的结果是一样的,会跳过直接返回空解析。

如果想避免回退(fallback)导致的 DNS 泄露,应该设置 "disableFallback": true

但是不建议全局设置 "disableFallback": true ,会导致所有没有命中 domains 的服务商,无论国内外都无法使用,只能默认使用第一个,但是第一个如果使用了 local:// ,又会失效。如果希望为不同的域名设计不同的 DNS 服务商,应该避免全局的 disableFallback 设置,需要为 DNS 模块每个域名服务商设置是否启动回退(fallback)的解析。

用于测试的 DNS 模块设置

DNS 模块如果日常使用,可能会出现的问题。比如:

domainStrategy 值为 IPOnDemand ,则会出现 DNS 泄露,原因上面有分析。

domainStrategy 值为 IPIfNonMatch ,如果路由模块按照 V2rayN 官方设置了 port 作为兜底策略,则不会出现 DNS 泄露。但是如果不设置兜底策略,则会出现 DNS 泄露,原因上面有分析。(小声吐槽,设置了port 作为兜底策略后,IPIfNonMatchAsIs 两者行为没有区别,直接导致了 IP 类规则无法用于域名路由的匹配了。)

  "dns": {
    "hosts": {
      "gfwsl.geforce.com": "127.0.0.1",
      "services.gfe.nvidia.com": "127.0.0.1",
      "license.piriform.com": "127.0.0.1",
      "dns.google": [
        "8.8.8.8",
        "8.8.4.4"
      ]
    },
    "servers": [
      {
        "address": "localhost",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "local://223.5.5.5",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "local://119.29.29.29",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "local://114.114.114.114",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "https+local://223.5.5.5/dns-query",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": false
      },
      {
        "address": "8.8.8.8",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": false,
        "clientIP": "114.114.114.114"
      },
      {
        "address": "1.1.1.1",
        "domains": [
          "geosite:geolocation-!cn"
        ],
        "expectIPs": [
          "geoip:!cn"
        ],
        "skipFallback": false
      },
      {
        "address": "https://1.1.1.1/dns-query",
        "domains": [
          "geosite:geolocation-!cn"
        ],
        "expectIPs": [
          "geoip:!cn"
        ],
        "skipFallback": false,
        "queryStrategy": "UseIPv4"
      },
      "8.8.8.8",
      "8.8.4.4",
      "1.1.1.1"
    ],
    "queryStrategy": "UseIPv4",
    "disableCache": false,
    "disableFallback": false,
    "disableFallbackIfMatch": false,
    "tag": "dns_inbound"
  }

xray 配置文件一份

{
  "log": {
    //    "access": "Vaccess.txt",
    //    "error": "Verror.txt",
    "loglevel": "info",
    "dnsLog": false
  },
  "api": {
    "tag": "api",
    "services": [
      "StatsService"
    ]
  },
  "policy": {
    "system": {
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  },
  "transport": null,
  "stats": {},
  "reverse": null,
  "fakeDns": null,
  "dns": {
    "hosts": {
      "dns.google": [
        "8.8.8.8",
        "8.8.4.4"
      ]
    },
    "servers": [
      {
        "address": "localhost",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "local://223.5.5.5",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "local://119.29.29.29",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "local://114.114.114.114",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": true
      },
      {
        "address": "https+local://223.5.5.5/dns-query",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": false
      },
      {
        "address": "8.8.8.8",
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ],
        "skipFallback": false,
        "clientIP": "114.114.114.114"
      },
      {
        "address": "1.1.1.1",
        "domains": [
          "geosite:geolocation-!cn"
        ],
        "expectIPs": [
          "geoip:!cn"
        ],
        "skipFallback": false
      },
      {
        "address": "https://1.1.1.1/dns-query",
        "domains": [
          "geosite:geolocation-!cn"
        ],
        "expectIPs": [
          "geoip:!cn"
        ],
        "skipFallback": false,
        "queryStrategy": "UseIPv4"
      },
      "8.8.8.8",
      "8.8.4.4",
      "1.1.1.1"
    ],
    "queryStrategy": "UseIPv4",
    "disableCache": false,
    "disableFallback": false,
    "disableFallbackIfMatch": false,
    "tag": "dns_inbound"
  },
  "inbounds": [
    {
      "tag": "socks",
      "port": 1080,
      "listen": "127.0.0.1",
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ],
        "routeOnly": true
      },
      "settings": {
        "auth": "noauth",
        "udp": true,
        "allowTransparent": false
      }
    },
    {
      "tag": "http",
      "port": 1081,
      "listen": "127.0.0.1",
      "protocol": "http",
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ],
        "routeOnly": true
      },
      "settings": {
        "auth": "noauth",
        "udp": true,
        "allowTransparent": false
      }
    },
    {
      "tag": "api",
      "port": 1085,
      "listen": "127.0.0.1",
      "protocol": "dokodemo-door",
      "settings": {
        "udp": false,
        "address": "127.0.0.1",
        "allowTransparent": false
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy",
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "0000000000000000",
            "port": 200,
            "users": [
              {
                "id": "0000000000000000",
                "alterId": 0,
                "email": "[email protected]",
                "security": "auto",
                "encryption": "none",
                "flow": "xtls-rprx-vision"
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "reality",
        "realitySettings": {
          "serverName": "0000000000000000",
          "fingerprint": "chrome",
          "show": false,
          "publicKey": "0000000000000000",
          "shortId": "0000000000000000",
          "spiderX": ""
        }
      },
      "mux": {
        "enabled": false,
        "concurrency": -1
      }
    },
    {
      "tag": "proxy 2",
      "protocol": "shadowsocks",
      "settings": {
        "servers": [
          {
            "address": "0000000000000000",
            "method": "aes-256-gcm",
            "ota": false,
            "password": "0000000000000000",
            "port": 200,
            "level": 1
          }
        ]
      },
      "streamSettings": {
        "network": "tcp"
      },
      "mux": {
        "enabled": false,
        "concurrency": -1
      }
    },
    {
      "tag": "proxy 3",
      "protocol": "shadowsocks",
      "settings": {
        "servers": [
          {
            "address": "0000000000000000",
            "method": "aes-256-gcm",
            "ota": false,
            "password": "0000000000000000",
            "port": 200,
            "level": 1
          }
        ]
      },
      "streamSettings": {
        "network": "tcp"
      },
      "mux": {
        "enabled": false,
        "concurrency": -1
      }
    },
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "UseIP", // 修改,如 在 freedom 出站中,将 domainStrategy 设置为 UseIP, 由此出站发出的请求, 会先将域名通过内置服务器解析成 IP, 然后进行连接。
        "userLevel": 0
      }
    },
    {
      "tag": "block",
      "protocol": "blackhole",
      "settings": {
        "response": {
          "type": "http"
        }
      }
    }
  ],
  "routing": {
    "domainStrategy": "IPIfNonMatch",
    "domainMatcher": "hybrid",
    "rules": [
      {
        "type": "field",
        "inboundTag": [
          "api"
        ],
        "outboundTag": "api"
      },
      {
        "type": "field",
        "protocol": [
          "bittorrent"
        ],
        "marktag": "ban_bt",
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "outboundTag": "block",
        "domain": [
          "domain:xcz.im",
          "domain:ads.fmdisk.com",
          "domain:ads.feemoo.com",
          "domain:ads.google.com",
          "domain:afd.l.google.com",
          "domain:mobileads.google.com",
          "domain:g1.tagtic.cn",
          "domain:log.tagtic.cn",
          "domain:pgdt.ugdtimg.com",
          "domain:sdownload.stargame.com",
          "keyword:googleads",
          "keyword:pagead",
          "keyword:umeng",
          "keyword:adnyg",
          "keyword:adwords",
          "keyword:analysis",
          "keyword:analytics",
          "keyword:duomeng",
          "keyword:dwtrack",
          "keyword:guanggao",
          "keyword:lianmeng",
          "keyword:monitor",
          "keyword:omgmta",
          "keyword:tracking",
          "keyword:uedas",
          "keyword:usage",
          "keyword:wlmonitor",
          "geosite:category-httpdns",
          "geosite:category-ads-all",
//          "ext:adblock.dat:adblock", // 引用 adblock.dat 文件中的 adblock 标签,adblock 标签由放入 data 文件夹中用于构建的文件名决定。
          "keyword:zjtoolbar"
        ]
      },
      {
        "type": "field",
        "outboundTag": "proxy 2", // Apple Intelligence
        "domain": [
          "domain:claude.ai",
          "ai.google.dev",
          "aida.googleapis.com",
          "aistudio.google.com",
          "alkalicore-pa.clients6.google.com",
          "alkalimakersuite-pa.clients6.google.com",
          "api-iam.intercom.io",
          "api.githubcopilot.com",
          "api.msn.com",
          "api.statsig.com",
          "assets.msn.com",
          "bard.google.com"
        ]
      },
      {
        "type": "field",
        "outboundTag": "proxy 1",
        "domain": [
          "domain:objects.githubusercontent.com",
          "codeload.github.com",
          "domain:iosapps.itunes.apple.com"
        ]
      },
      {
        "type": "field",
        "outboundTag": "proxy",
        "domain": [
          "geosite:google",
          "geosite:google-cn",
          "geosite:telegram",
          "geosite:netflix",
          "geosite:geolocation-!cn",
          "geosite:appledaily",
          "geosite:github",
          "geosite:twitter",
          "geosite:amp",
          "geosite:openai",
          "geosite:instagram"
        ]
      },
      {
        "type": "field",
        "outboundTag": "proxy",
        "ip": [
          "1.1.1.1",
          "8.8.8.8",
          "geoip:telegram",
          "geoip:netflix",
          "geoip:twitter"
        ]
      },
      {
        "type": "field",
        "outboundTag": "direct",
        "domain": [
          "geosite:geolocation-cn",
          "geosite:cn",
          "geosite:private",
          "geosite:bilibili",
          "geosite:apple-cn",
          "domain:ip6-localhost",
          "domain:ip6-loopback",
          "domain:local",
          "domain:localhost",
          "domain:cdn.jsdelivr.net",
          "keyword:moutai"
        ]
      },
      {
        "type": "field",
        "outboundTag": "direct",
        "ip": [
          "geoip:private",
          "geoip:cn",
          "::1/128",
          "fc00::/7",
          "fe80::/10",
          "fd00::/8"
        ]
      },
      {
        "type": "field",
        "port": "0-65535",
        "outboundTag": "proxy"
      }
    ]
  }
}

如何使用

首先明确一下,xray 系列也是可以不同路由规则选择不同的出站方式。不过因为没有 GUI 界面和代理组的方式,使用起来的确不太方便。一般因为 xray 内核都是在客户端的 GUI 界面使用,而 GUI 界面难以实现复杂的分流配置,因此很少有人使用 xray 的分流出站功能。这里给大家介绍两种使用方法:

V2rayN 客户端使用,当然只能通过自定义内核的方式实现。之前在 sing-box 客户端选择中就讲过,现在 V2rayN 自定义内核支持完成的配置文件。因此把上面的配置文件导入到 V2rayN 自定义内核中,内核选择 xray 即可。至于 xray 节点信息怎么生成,不会配置怎么办?最简单的方式就是使用 V2rayN 客户端,把订阅信息或者节点导入到客户端,选择一个节点后,在 V2rayN 配置中会自动生成对应节点的信息,位置在(v2rayN\guiConfigs\config.json) 中,自己拷贝到上面配置文件中,你可以选择多个协议配置,然后把不同的出站方式选择对应的节点 tag 即可。

②内核运行,最简单也是最省内存的方式,掌握了这个之后,服务端的配置也就自然掌握了。通过上面方式把节点信息拷贝到配置文件,然后就是运行内核直接启动了。xray 官方提供了两个内核版本,一个就是 xray 内核,会显示 CMD 黑框,至于怎么消除,我在之前的 sing-box 和 mihomo 教程中实操过,有兴趣可以去看看。还要一个内核 wxray.exe,这个运行后不会显示 CMD 黑框,可以直接选择这个。这样就可以通过内核运行了,不过有个小问题,运行内核后,不会自动打开系统代理,需要手动打开,日常使用多少麻烦些,这里提供解决方案,自己保存为 BAT 运行即可。

proxy_on.bat

echo off
cls
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable /t REG_DWORD /d 1 /f
exit

proxy_off.bat

echo off
cls
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f
exit

autooutsystemproxy.bat

taskkill /im wxray.exe /f
call proxy_off.bat
ping -n 2 127.1 >nul

autorunsystemproxy.bat

@ECHO OFF
%1 start mshta vbscript:createobject("wscript.shell").run("""%~0"" ::",0)(window.close)&&exit
start /b wxray run -c config.json
call proxy_on.bat

自定义 geoip 和 geosite 数据库

如果你有一些自定义规则,随着日常收集的越来越多,配置文件也越来越大,日常修改多少有点麻烦,可以使用自定义 geoip 和 geosite 数据库的方式引入到配置文件中,和 mihomo 规则集类似。不过一般没有人愿意这么折腾,如果你愿意,可以参阅下面的地址自己制作。

geosite 定制:
https://github.com/v2fly/domain-list-community?tab=readme-ov-file#generate-dlcdat-manually

geoip 定制:
https://github.com/Loyalsoldier/geoip?tab=readme-ov-file#%E8%87%AA%E8%A1%8C%E5%AE%9A%E5%88%B6-geoip-%E6%96%87%E4%BB%B6

具体操作流程就不讲解了,建议使用 Windows 构建,速度快。安装 Go 程序,然后把上面链接的仓库下载到本地,解压进入文件夹,把需要自定义的规则收集到一起,放在 data 文件夹中,然后 go run ./ ,具体操作仓库给了详细介绍,需要的可以自己查阅。

小工具分享

https://github.com/MetaCubeX/geo

简化地理资源管理的轻松方式。可作为 CLI 和 Go 库。方便管理 geo 数据库,比如 DNS 检测出现某些不可理解的情况时,就可以考虑是否是 geo 数据库出错,该工具就可以方便查询数据库是否有对应的值。

复活 Clash for Windows

前面写 mihomo 教程时,答应给大家写一份复活 Clash for Windows 的教程,一直研究 xray 也没写。其实不难,自己动手几分钟就搞定,也就不着急着写,这次顺便写一下。

至于选择什么客户端,看个人喜好,Clash for Windows 应该还有不少人使用,不过 Clash Core Premium 内核现在很多功能不支持了,如果只是使用订阅服务,倒也没问题。其实如果只更换 Clash Core Premium 内核为 mihomo 的话,Clash for Windows 也是可以正常运行,mihomo 功能也全支持,tun 模式也可以打开。不过网络日志加载总是很慢,内核版本显示 unkown,小地球也是红的,强迫症有点介绍不了,通过下面操作可以恢复到原版状态。

原帖链接:https://www.nodeseek.com/post-135393-1

教程
进行教程之前先关闭 cfw 和 Clash 内核 的进程!!!


下载内核
先去 mihomo 仓库 (也就是 Clash Meta) 下载最新的内核程序。

项目地址
https://github.com/MetaCubeX/mihomo/releases

以最新release举例,选择 
mihomo-windows-amd64-compatible-go120-v1.18.6.zip (https://github.com/MetaCubeX/mihomo/releases/download/v1.18.6/mihomo-windows-amd64-compatible-go120-v1.18.6.zip)
mihomo-windows-amd64-compatible-go121-v1.18.6.zip (https://github.com/MetaCubeX/mihomo/releases/download/v1.18.6/mihomo-windows-amd64-compatible-go121-v1.18.6.zip)
mihomo-windows-amd64-go120-v1.18.6.zip (https://github.com/MetaCubeX/mihomo/releases/download/v1.18.6/mihomo-windows-amd64-go120-v1.18.6.zip)
mihomo-windows-amd64-go121-v1.18.6.zip (https://github.com/MetaCubeX/mihomo/releases/download/v1.18.6/mihomo-windows-amd64-go121-v1.18.6.zip)

任何一个皆可
下载完毕后解压,你会得到一个程序。


➡️替换内核
打开 cfw 安装目录的 \resources\static\files\win\x64 文件夹,将上面解压出来的程序改名为 clash-win64.exe 替换进去。


➡️测试
打开 cfw,在 Clash 内核 的位置显示 Unknown 并且可以正常使用节点就是成功啦。


➡️修改 cfw 面板上的 Clash 内核 文本,使其显示 Meta版本号


工具:asar解包/打包工具 ( https://www.cnblogs.com/binghe021/p/17622220.html)

步骤:解包cfw 安装目录的 \resources\ app.asar ,后进入 \dist\electron 目录,打开 renderer.js 文件,搜索 n.premium,首先将后面的 0!==o&& 删掉,然后找到后面的 "Premium":"",改成 "Premium":"Meta",然后重新打包替换即可。


PS:
修复服务模式小地球灰色问题
回退 cfw  SERVICE版本。
将旧版的目录 \resources\static\files\win\x64\service 内的两个程序( https://www.mediafire.com/file/n47f6kpt7j4sbkg/service.zip/file ),替换到新版相同目录内即可。

Clash 系列内核第三方支持的 GUI 客户端实在太多,处于遥遥领先的地步!目前很多第三方客户端有了很多集成的功能,比如 sub-store ,内置的编辑器等等,可能是时候选择新的开始了!

20 个赞

先赞后看,养成习惯 :melting_face:

好复杂,我想看看mihomo系的教程

感谢你的教程

感谢大佬,正需要

好东西啊,光看标题就知道是我想要的,
谢谢楼主,先回帖表示敬礼

1 个赞

写的很详细,感谢教程

1 个赞

mark一下

1 个赞

翻以前的帖子

好强,码那么多字