Xray-core: gRPC cannot be proxied with dialerProxy to freedom

Hello I found a quite strange bug in gRPC transport. Take a look at this config:

{
  "log": {
    "loglevel": "debug"
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": "10808",
      "protocol": "socks",
      "settings": {
        "udp": true
      }
    },
    {
      "listen": "127.0.0.1",
      "port": "10809",
      "protocol": "http"
    }
  ],
  "outbounds": [
    {
      "protocol": "trojan",
      "settings": {
        "servers": [
          {
            "address": "example.com",
            "port": 443,
            "password": "pass"
          }
        ]
      },
      "streamSettings": {
        "network": "gun",
        "security": "tls",
        "grpcSettings": {
          "serviceName": "servername"
        },
        "sockopt": {
          "dialerProxy": "direct"
        }
      }
    },
    {
      "protocol": "freedom",
      "tag": "direct"
    }
  ]
}

This config should just route gRPC traffic via a freedom outbound and thus have no difference between adding dialerProxy and not. However, I couldn’t get this config to work. If I remove the sockopt from the config it works fine. Logs look like this:

2023/06/20 16:13:41 [Warning] core: Xray 1.8.3 started
2023/06/20 16:13:43 [Info] [2221018126] proxy/socks: TCP Connect request to tcp:[2a00:1450:4001:827::200e]:80
2023/06/20 16:13:43 [Info] [2221018126] app/dispatcher: default route for tcp:[2a00:1450:4001:827::200e]:80
2023/06/20 16:13:45 tcp:127.0.0.1:36732 accepted tcp:[2a00:1450:4001:827::200e]:80
2023/06/20 16:13:49 [Info] [2221018126] transport/internet/grpc: creating connection to tcp:104.21.2.133:443
2023/06/20 16:13:49 [Debug] transport/internet/grpc: using gRPC tun mode service name: `...` stream name: `Tun`
2023/06/20 16:13:49 [Info] [2221018126] transport/internet: redirecting request tcp:104.21.2.133:443 to fragment
2023/06/20 16:13:49 [Info] [2221018126] transport/internet/tcp: dialing TCP to tcp:104.21.2.133:443
2023/06/20 16:13:49 [Debug] transport/internet: dialing to tcp:104.21.2.133:443
2023/06/20 16:13:49 [Info] [2221018126] proxy/freedom: connection opened to tcp:104.21.2.133:443, local endpoint 172.16.0.2:48020, remote endpoint 104.21.2.133:443
2023/06/20 16:13:54 [Info] [2221018126] proxy/trojan: tunneling request to tcp:[2a00:1450:4001:827::200e]:80 via 104.21.2.133:443
multi read transport/internet/grpc/encoding: failed to fetch hunk from gRPC tunnel > rpc error: code = Unavailable desc = error reading from server: EOF
transport/internet/grpc/encoding: failed to send data over gRPC tunnel > EOF
2023/06/20 16:14:36 [Info] [2221018126] app/proxyman/outbound: failed to process outbound traffic > proxy/trojan: connection ends > transport/internet/grpc/encoding: failed to fetch hunk from gRPC tunnel > rpc error: code = Unavailable desc = error reading from server: EOF

And wiresharks shows that my own PC sends a FIN to server: image I tried digging into Xray and Google’s gRPC source code with debugging and watching when the pipes get closed but I couldn’t figure it out. HOWEVER, I found an alternative way to forward any traffic to a specific outbound. This method involves using dokodemo-door with a specific routing and using the dokedemo-door address as the outbound address. Consider following config file:

{
  "log": {
    "loglevel": "debug"
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": "10808",
      "protocol": "socks",
      "settings": {
        "udp": true
      }
    },
    {
      "listen": "127.0.0.1",
      "port": "10809",
      "protocol": "http"
    },
    {
      "listen": "127.0.0.1",
      "port": "28111",
      "protocol": "dokodemo-door",
      "settings": {
        "address": "104.21.2.133",
        "port": 443,
        "network": "tcp"
      },
      "tag": "fragmentedinbound"
    }
  ],
  "outbounds": [
    {
      "protocol": "trojan",
      "settings": {
        "servers": [
          {
            "address": "127.0.0.1",
            "port": 28111,
            "password": "password"
          }
        ]
      },
      "streamSettings": {
        "network": "gun",
        "security": "tls",
        "grpcSettings": {
          "serviceName": "..."
        },
        "tlsSettings": {
          "serverName": "..."
        }
      }
    },
    {
      "protocol": "freedom",
      "settings": {
        "fragment": {
          "length": "1-2",
          "interval": "0-1",
          "packets": "1"
        }
      },
      "tag": "fragment"
    },
    {
      "protocol": "freedom",
      "tag": "direct"
    }
  ],
  "routing": {
    "domainMatcher": "mph",
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      {
        "domain": [
          "regexp:.*\\.ir$",
          "ext:iran.dat:ir",
          "ext:iran.dat:other"
        ],
        "outboundTag": "direct",
        "type": "field"
      },
      {
        "ip": [
          "geoip:private",
          "geoip:ir"
        ],
        "outboundTag": "direct",
        "type": "field"
      },
      {
        "inboundTag": [
          "fragmentedinbound"
        ],
        "outboundTag": "fragment",
        "type": "field"
      }
    ]
  }
}

This is basically the config which I’m currently using to connect. I’m not expecting this to be fixed anytime soon considering that there is a neat workaround.

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 18 (17 by maintainers)

Commits related to this issue

Most upvoted comments

感谢你的测试,可能不是 gRPC 内部的问题,而是 Xray 调用 gRPC 的问题

dialerProxy 和 gRPC 传输层是 Xray-core v1.4.0 同时引入的,可能它们之间没适配,你可以检查一下代码,然后发个 PR