Skip to content

libv2ray处理DNS请求的方式有问题 #20

Description

@yoshikotama

在Android中,开启VPN后,OS仍会负责发起域名解析请求。libv2ray会将DNS请求当作UDP报文直接进行路由,而在默认使用8.8.8.8访问geoip:cn的情况下,在远端发送DNS请求会导致返回不适合本地的IP地址,导致网页访问缓慢或者返回了面向海外的页面。而在PC上,Socks客户端向服务端发送的只有域名信息,域名的解析由v2ray全权负责。tun2socks虽然将tun界面转换为了socks界面,却并不能良好的处理DNS请求,导致DNS请求全部被无脑转发、返回了不适合直连的CDN。比如,通过Actinium访问淘宝,在全局代理下会被重定向到国际版,而在PC上是正常的。原文一开始发在了v2ray/core下,但我感觉应该是libv2ray的实现方式有问题,和v2ray本身应该没有关系。所以稍加编辑、发在了这里。

环境:

  • 服务器端:2.47
  • PC端:2.47,传入连接使用Socks协议。
  • Android端:2.50+libv2ray 25+Actinium。

下面是我的猜测。
首先,Firefox在创建连接时是直接向v2ray发送的域名,本身并没有生成DNS请求。Socks协议允许客户端建立TCP连接时直接请求域名而无须预先将域名解析成IP地址,而Firefox则完全没有参与域名的解析过程,在访问某个页面时只是把域名直接发送给了Socks服务器。若访问的域名属于geosite:cn,则wireshark中可发现对该网站的DNS请求。若访问的域名不属于geosite:cn,wireshark中就不能发现对该网站的DNS请求,而v2ray的log中可见通过隧道向8.8.8.8:53发起的的DNS请求。真正生成DNS请求的应该是v2ray本身。V2ray将会通过路由规则来选择将DNS请求送给OS中设置的DNS服务器或v2ray的配置文件中指定的DNS服务器。

PC环境下Wireshark所获取的Socks报文如下。

Transmission Control Protocol, Src Port: 14696, Dst Port: 1082, Seq: 1, Ack: 1, Len: 3
Socks Protocol
    Version: 5
    Client Authentication Methods

Transmission Control Protocol, Src Port: 1082, Dst Port: 14696, Seq: 1, Ack: 4, Len: 2
Socks Protocol
    Version: 5
    Accepted Auth Method: 0x0 (No authentication)

Transmission Control Protocol, Src Port: 14696, Dst Port: 1082, Seq: 4, Ack: 3, Len: 17
Socks Protocol
    Version: 5
    Command: Connect (1)
    Reserved: 0
    Address Type: Domain Name (3)(注意这一行)
    Remote name: google.com(注意这一行)
    Port: 80

……

通过v2ray访问twitter和淘宝的日志输出如下(测试前已清空DNS缓存)。

2017/11/23 11:22:11 [Info]Proxy|Socks: TCP Connect request to tcp:twitter.com:443
2017/11/23 11:22:11 [Info]App|Router: looking up IP for tcp:twitter.com:443
2017/11/23 11:22:11 [Debug]App|DNS|Server: add pending request id 64489
2017/11/23 11:22:11 [Debug]Transport|Internet|UDP: dispatch request to: udp:8.8.8.8:53
2017/11/23 11:22:11 [Info]Transport|Internet|UDP: establishing new connection for udp:8.8.8.8:53
2017/11/23 11:22:11 [Info]App|Dispatcher|Default: default route for udp:8.8.8.8:53
2017/11/23 11:22:11 [Info]App|Proxyman|Mux: dispatching request to udp:8.8.8.8:53
2017/11/23 11:22:11 [Debug]App|DNS|Server: handling response for id 64489 content: ;; opcode: QUERY, status: NOERROR, id: 64489
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;twitter.com.   IN       A

;; ANSWER SECTION:
twitter.com.    466     IN      A       104.244.42.1
twitter.com.    466     IN      A       104.244.42.193

2017/11/23 11:22:11 [Debug]App|DNS|Server: returning 2 IPs for domain twitter.com.
2017/11/23 11:22:11 [Info]App|Dispatcher|Default: default route for tcp:twitter.com:443
2017/11/23 11:22:11 [Info]App|Proxyman|Mux: dispatching request to tcp:twitter.com:443
2017/11/23 11:22:11 [Info]Proxy|Socks: TCP Connect request to tcp:twitter.com:443
2017/11/23 11:22:11 [Info]App|Router: looking up IP for tcp:twitter.com:443
2017/11/23 11:22:11 [Info]App|Dispatcher|Default: default route for tcp:twitter.com:443
2017/11/23 11:24:06 [Info]Proxy|Socks: TCP Connect request to tcp:taobao.com:80
2017/11/23 11:24:06 [Info]App|Dispatcher|Default: taking detour [direct] for [tcp:taobao.com:80]
2017/11/23 11:24:06 [Info]Proxy|Freedom: opening connection to tcp:taobao.com:80
2017/11/23 11:24:06 [Info]Transport|Internet|TCP: dailing TCP to tcp:taobao.com:80
2017/11/23 11:24:07 [Info]Proxy|Socks: TCP Connect request to tcp:taobao.com:80
2017/11/23 11:24:07 [Info]App|Dispatcher|Default: taking detour [direct] for [tcp:taobao.com:80]
2017/11/23 11:24:07 [Info]Proxy|Freedom: opening connection to tcp:taobao.com:80
2017/11/23 11:24:07 [Info]Transport|Internet|TCP: dailing TCP to tcp:taobao.com:80
2017/11/23 11:24:07 [Info]Proxy|Socks: TCP Connect request to tcp:www.taobao.com:443
2017/11/23 11:24:07 [Info]App|Dispatcher|Default: taking detour [direct] for [tcp:www.taobao.com:443]
2017/11/23 11:24:07 [Info]Proxy|Freedom: opening connection to tcp:www.taobao.com:443
2017/11/23 11:24:07 [Info]Transport|Internet|TCP: dailing TCP to tcp:www.taobao.com:443
2017/11/23 11:24:08 [Info]Proxy|Socks: TCP Connect request to tcp:www.taobao.com:443
2017/11/23 11:24:08 [Info]App|Dispatcher|Default: taking detour [direct] for [tcp:www.taobao.com:443]
2017/11/23 11:24:08 [Info]Proxy|Freedom: opening connection to tcp:www.taobao.com:443

但是在Android平台上,建立TCP链接的流程与PC+Firefox+v2ray有者很大的差别。libv2ray是通过tun2socks将隧道设备转换为socks链接的,而Android OS本身依旧在按照正常的流程(DNS解析、建立连接)对外建立TCP链接,所有的DNS请求都是由OS本身发出的,v2ray本身只能将DNS报文当作普通的UDP报文、按照路由设置机械的转发。Android在建立连接时将会直接使用IP建立连接。

通过v2ray访问twitter和淘宝的日志输出如下。

2017/11/23 05:21:30 [Info]Proxy|Socks: client UDP connection from udp:127.0.0.1:43520
2017/11/23 05:21:30 [Debug]Proxy|Socks: send packet to udp:8.8.8.8:53 with 28 bytes
2017/11/23 05:21:30 [Debug]Transport|Internet|UDP: dispatch request to: udp:8.8.8.8:53
2017/11/23 05:21:30 [Info]Transport|Internet|UDP: establishing new connection for udp:8.8.8.8:53
2017/11/23 05:21:30 [Info]App|Dispatcher|Default: default route for udp:8.8.8.8:53
2017/11/23 05:21:30 [Info]App|Proxyman|Mux: dispatching request to udp:8.8.8.8:53
………………
2017/11/23 05:21:30 [Info]Proxy|Socks: TCP Connect request to tcp:202.47.28.119:443
2017/11/23 05:21:30 [Info]App|Dispatcher|Default: sniffed domain: taobao.com
2017/11/23 05:21:30 [Info]App|Dispatcher|Default: taking detour [direct] for [tcp:taobao.com:80]
2017/11/23 05:21:30 [Info]Proxy|Freedom: opening connection to tcp:taobao.com:80
2017/11/23 05:21:30 [Info]Transport|Internet|TCP: dailing TCP to tcp:taobao.com:80
………………
2017/11/23 05:21:30 [Info]Proxy|Socks: TCP Connect request to tcp:47.89.66.254:443
2017/11/23 05:21:31 [Info]App|Dispatcher|Default: sniffed domain: m.taobao.com
2017/11/23 05:21:31 [Info]App|Dispatcher|Default: taking detour [direct] for [tcp:m.taobao.com:443]
2017/11/23 05:21:31 [Info]Proxy|Freedom: opening connection to tcp:m.taobao.com:443
2017/11/23 05:21:31 [Info]Transport|Internet|TCP: dailing TCP to tcp:m.taobao.com:443
………………
2017/11/23 05:21:38 [Info]Proxy|Socks: client UDP connection from udp:127.0.0.1:55957
2017/11/23 05:21:38 [Debug]Proxy|Socks: send packet to udp:8.8.8.8:53 with 19 bytes
2017/11/23 05:21:38 [Debug]Transport|Internet|UDP: dispatch request to: udp:8.8.8.8:53
2017/11/23 05:21:38 [Info]Transport|Internet|UDP: establishing new connection for udp:8.8.8.8:53
2017/11/23 05:21:38 [Info]App|Dispatcher|Default: default route for udp:8.8.8.8:53
2017/11/23 05:21:38 [Info]App|Proxyman|Mux: dispatching request to udp:8.8.8.8:53
2017/11/23 05:21:38 [Info]App|Dispatcher|Default: sniffed domain: m.intl.taobao.com
2017/11/23 05:21:38 [Info]App|Dispatcher|Default: taking detour [direct] for [tcp:m.intl.taobao.com:443]
2017/11/23 05:21:38 [Info]Proxy|Freedom: opening connection to tcp:m.intl.taobao.com:443
2017/11/23 05:21:38 [Info]Transport|Internet|TCP: dailing TCP to tcp:m.intl.taobao.com:443
2017/11/23 04:47:54 [Info]Proxy|Socks: client UDP connection from udp:127.0.0.1:35010
2017/11/23 04:47:54 [Info]Transport|Internet|UDP: establishing new connection for udp:8.8.8.8:53
2017/11/23 04:47:54 [Info]App|Dispatcher|Default: default route for udp:8.8.8.8:53
2017/11/23 04:47:54 [Info]App|Proxyman|Mux: dispatching request to udp:8.8.8.8:53
2017/11/23 04:47:54 [Info]Proxy|Socks: TCP Connect request to tcp:104.244.42.65:443
2017/11/23 04:47:54 [Info]Proxy|Socks: TCP Connect request to tcp:104.244.42.65:443
2017/11/23 04:47:54 [Info]Proxy|Socks: TCP Connect request to tcp:104.244.42.65:443
2017/11/23 04:47:54 [Info]Proxy|Socks: TCP Connect request to tcp:104.244.42.65:443
2017/11/23 04:47:54 [Info]App|Dispatcher|Default: sniffed domain: twitter.com
2017/11/23 04:47:54 [Info]App|Router: looking for IP for domain: twitter.com
2017/11/23 04:47:54 [Info]App|Dispatcher|Default: default route for tcp:twitter.com:443
2017/11/23 04:47:54 [Info]App|Proxyman|Mux: dispatching request to tcp:twitter.com:443
2017/11/23 04:47:54 [Info]App|Dispatcher|Default: sniffed domain: twitter.com
2017/11/23 04:47:54 [Info]App|Dispatcher|Default: sniffed domain: twitter.com
2017/11/23 04:47:54 [Info]App|Router: looking for IP for domain: twitter.com
2017/11/23 04:47:54 [Info]App|Router: looking for IP for domain: twitter.com
2017/11/23 04:47:54 [Info]App|Dispatcher|Default: default route for tcp:twitter.com:443
2017/11/23 04:47:54 [Info]App|Proxyman|Mux: dispatching request to tcp:twitter.com:443
2017/11/23 04:47:54 [Info]App|Router: looking for IP for domain: twitter.com
2017/11/23 04:47:54 [Info]App|Dispatcher|Default: default route for tcp:twitter.com:443
2017/11/23 04:47:54 [Info]App|Proxyman|Mux: dispatching request to tcp:twitter.com:443
2017/11/23 04:47:54 [Info]Transport|Internet|WebSocket: creating connection to tcp:wodeipdizhi:80
2017/11/23 04:47:55 [Info]Proxy|VMess|Outbound: tunneling request to tcp:v1.mux.cool:9527 via tcp:wodeipdizhi:80

因此v2ray在这个情况下就不能将域名与按照路由规则送给不同的服务器解析。如果DNS设置为海外的服务器就会导致淘宝返回海外版页面。即使是设置了domainOverride也只能是在建立连接时再通过嗅探选择直连或走隧道,无法解决问题。

Android端配置文件

{
  "port": 10808,
  "log": {
    "loglevel": "debug"
  },
  "inbound": {
    "domainOverride": [
        "http",
        "tls"
    ],
    "listen": "127.0.0.1",
    "protocol": "socks",
    "settings": {
      "auth": "noauth",
      "udp": true
    },
    "allowPassive": true
  },
  "inboundDetour": [
    {
      "protocol": "http",
      "port": 10845,
      "settings": {},
      "listen": "127.0.0.1"
    },
    {
      "protocol": "dokodemo-door",
      "port": 10846,
      "listen": "0.0.0.0",
      "settings": {
        "network": "tcp",
        "timeout": 0,
        "followRedirect": true
      }
    }
  ],
  "outbound": {
    "protocol": "vmess",
    "settings": {
      "vnext": [
        {
          "address": "<ws.address>",
          "port": <ws.port>,
          "users": [
            {
              "id": "<id>",
              "alterId": 64,
              "security": "auto"
            }
          ]
        }
      ]
    },
    "mux":{
      "enabled":true
    }
  },
  "outboundDetour": [
    {
      "protocol": "freedom",
      "settings": {},
      "tag": "direct"
    }
  ],
  "dns": {
    "servers": [
      "8.8.8.8",
      "8.8.4.4",
      "localhost"
    ]
  },
  "routing": {
    "strategy": "rules",
    "settings": {
      "domainStrategy": "IPIfNonMatch",
      "rules": [
        {
          "type": "field",
          "port": "1-52",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "port": "54-79",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "port": "81-442",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "port": "444-65535",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "domain": ["geosite:cn"],
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "ip": [
            "0.0.0.0/8",
            "10.0.0.0/8",
            "100.64.0.0/10",
            "127.0.0.0/8",
            "169.254.0.0/16",
            "172.16.0.0/12",
            "192.0.0.0/24",
            "192.0.2.0/24",
            "192.168.0.0/16",
            "198.18.0.0/15",
            "198.51.100.0/24",
            "203.0.113.0/24",
            "::1/128",
            "fc00::/7",
            "fe80::/10",
            "geoip:cn"
          ],
          "outboundTag": "direct"
        }
      ]
    }
  },
  "transport": {},
  "#lib2ray": {
    "enabled": true,
    "listener": {
      "onUp": "#none",
      "onDown": "#none"
    },
    "env": [
      "V2RayDNSPort=5353",
      "DNSForwardingProxyPort=5350",
      "V2RaySocksPort=10808"
    ],
    "render": [],
    "escort": [],
    "vpnservice": {
      "Target": "${datadir}tun2socks",
      "Args": [
        "--netif-ipaddr",
        "26.26.26.2",
        "--netif-netmask",
        "255.255.255.0",
        "--socks-server-addr",
        "127.0.0.1:$V2RaySocksPort",
        "--tunfd",
        "3",
        "--tunmtu",
        "1500",
        "--sock-path",
        "/dev/null",
        "--loglevel",
        "4",
        "--enable-udprelay"
      ],
      "VPNSetupArg": "m,1500 a,26.26.26.1,24 r,0.0.0.0,0 d,208.67.222.222"
    },
    "preparedDomainName": {
      "domainName": [
        "<ws.address>:<ws.port>"
      ],
      "tcpVersion": "tcp4",
      "udpVersion": "udp4"
    }
  }
}

PC端配置文件

{
  "log": {
    "loglevel": "debug"
  },
  "inbound": {
    "port": 1082,
    "listen": "127.0.0.1",
    "protocol": "socks",
    "settings": {
      "auth": "noauth",
      "udp": true,
      "ip": "127.0.0.1",
      "timeout": 0
    
    }
  },
  "outbound": {
    "protocol": "vmess",
    "settings": {
      "vnext": [
        {
          "address": "<ws.address>",
          "port": <ws.port>,
          "users": [
            {
              "id": "<id>",
              "alterId": 64,
              "security": "auto"
            }
          ]
        }
      ]
    },
    "mux":{
      "enabled":true
    }
  },
  "outboundDetour": [
    {
      "protocol": "freedom",
      "settings": {},
      "tag": "direct"
    }
  ],
  "dns": {
    "servers": [
      "8.8.8.8",
      "8.8.4.4",
      "localhost"
    ]
  },
  "routing": {
    "strategy": "rules",
    "settings": {
      "domainStrategy": "IPIfNonMatch",
      "rules": [
        {
          "type": "field",
          "port": "1-52",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "port": "54-79",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "port": "81-442",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "port": "444-65535",
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "domain": ["geosite:cn"],
          "outboundTag": "direct"
        },
        {
          "type": "field",
          "ip": [
            "0.0.0.0/8",
            "10.0.0.0/8",
            "100.64.0.0/10",
            "127.0.0.0/8",
            "169.254.0.0/16",
            "172.16.0.0/12",
            "192.0.0.0/24",
            "192.0.2.0/24",
            "192.168.0.0/16",
            "198.18.0.0/15",
            "198.51.100.0/24",
            "203.0.113.0/24",
            "::1/128",
            "fc00::/7",
            "fe80::/10",
            "geoip:cn"
          ],
          "outboundTag": "direct"
        }
      ]
    }
  }
}

(两者的路由、DNS、传出规则都是一致的)

Activity

  1. cnperi commented on Nov 24, 2017

    @cnperi

    我对这个问题的解决办法,就是白名单。
    翻墙白名单内的走 vps,其他的走 direct。

  2. yoshikotama commented on Nov 24, 2017

    @yoshikotama
    Author

    @cnperi 你指的是app白名单还是config.json里设置域名白名单?

  3. cnperi commented on Nov 24, 2017

    @cnperi

    @yoshikotama config.json 白名单。虽然仍有问题,但可用。
    不过,好像只能用 v2raygo。

  4. yoshikotama commented on Nov 24, 2017

    @yoshikotama
    Author

    @cnperi 我试过,依旧会用8.8.8.8解析白名单的域名。

  5. cnperi commented on Nov 24, 2017

    @cnperi

    对啊,不就是要用墙外 DNS 解析这些被墙域名吗?那些不需要的,走默认 direct 啊。

  6. cnperi commented on Nov 24, 2017

    @cnperi

    哦。我理解你的意思了。这个问题,估计需要在 libv2ray里面加 pdnsd 或是 dnsmasq 来解决了。或者 iptables。

  7. xiaokangwang commented on Nov 25, 2017

    @xiaokangwang
    Owner

    是否可以使用core的domain override功能来解决这个问题呢?(对于HTTP|TLS数据)

  8. cnperi commented on Nov 25, 2017

    @cnperi

    @xiaokangwang 已经用了 domain override 了。
    关键是所有的 UDP 数据都导到 TUN 里面了。
    不知 vpn 环境里面用 墙内 DNS 的方式可行不?

  9. xiaokangwang commented on Nov 25, 2017

    @xiaokangwang
    Owner

    有DNS投毒的。。。
    你是希望DNS快还是返回兼容的结果?

  10. cnperi commented on Nov 25, 2017

    @cnperi

    @xiaokangwang 若是 v2ray core 的 domian override 能修正 DNS 投毒结果的话,就可以这么干啊。

  11. xiaokangwang commented on Nov 25, 2017

    @xiaokangwang
    Owner

    有可能能修正,你可以尝试一下。。。。会让兼容性下降的

  12. yoshikotama commented on Nov 26, 2017

    @yoshikotama
    Author

    @xiaokangwang domainOverride是事后诸葛亮,原文里写过了。

  13. xiaokangwang commented on Nov 26, 2017

    @xiaokangwang
    Owner

    这个其实是Android系统机制决定的,因此之前提到的方法都是对于这个情况的一种修补

  14. cnperi commented on Nov 28, 2017

    @cnperi

    @xiaokangwang 能不能把 libv2ray 的 pdnsd 换成 dnsmasq,把 udpgw 中目标地址 53 端口的 udp 包转给 dnsmasq。这样让 dnsmasq 来做 dns 分流的事儿?

  15. xiaokangwang commented on Nov 29, 2017

    @xiaokangwang
    Owner

    libv2ray里的pdnsd已经不在被使用了,现在所有的udp流量都作为udp数据直接转发

  16. Platway commented on May 31, 2018

    @Platway

    我也發現同樣的問題,但我測試v2rayNG似乎解決了這個問題,不知道用什麼方法。這方面做得最好的還是Shadowsocks Android,用overture分流。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions