AndroidLibV2ray icon indicating copy to clipboard operation
AndroidLibV2ray copied to clipboard

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

Open yoshikotama opened this issue 7 years ago • 16 comments

在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、传出规则都是一致的)

yoshikotama avatar Nov 23 '17 14:11 yoshikotama

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

cnperi avatar Nov 24 '17 01:11 cnperi

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

yoshikotama avatar Nov 24 '17 02:11 yoshikotama

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

cnperi avatar Nov 24 '17 04:11 cnperi

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

yoshikotama avatar Nov 24 '17 06:11 yoshikotama

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

cnperi avatar Nov 24 '17 06:11 cnperi

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

cnperi avatar Nov 24 '17 07:11 cnperi

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

xiaokangwang avatar Nov 25 '17 00:11 xiaokangwang

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

cnperi avatar Nov 25 '17 00:11 cnperi

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

xiaokangwang avatar Nov 25 '17 00:11 xiaokangwang

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

cnperi avatar Nov 25 '17 01:11 cnperi

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

xiaokangwang avatar Nov 25 '17 01:11 xiaokangwang

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

yoshikotama avatar Nov 26 '17 04:11 yoshikotama

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

xiaokangwang avatar Nov 26 '17 08:11 xiaokangwang

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

cnperi avatar Nov 28 '17 11:11 cnperi

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

xiaokangwang avatar Nov 29 '17 03:11 xiaokangwang

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

Platway avatar May 31 '18 05:05 Platway