shadowsocks / shadowsocks-rust

A Rust port of shadowsocks
https://shadowsocks.org/
MIT License
8.63k stars 1.17k forks source link

socks5模式时DNS解析没有在服务端执行? #870

Closed piangere closed 2 years ago

piangere commented 2 years ago

shadowsocks-rust版本:v1.15.0-alpha.5

根据这里:How does SOCK 5 proxy-ing of DNS work in browsers?

SOCKS5 proxy configured the browser simply asks to the SOCKS5 to connect to hosts using the host name of the host instead of its IP address. It's up to the SOCKS5 proxy then to do the lookup.

根据我的测试,疑似当前是这样的情况:

测试的应用在docker跑qBittorrent,配置SOCKS5代理为shadowsocks-rust。

zonyitoo commented 2 years ago

This is impossible. Just double check your socks5 client.

sslocal is fully compatible with RFC1928 CONNECT and UDP ASSOCIATE command, which is exactly the same as shadowsocks-libev.

If you suspects that the domain names are resolved locally, then the only reason is that your socks5 clients resolve names before sending socks5 requests to sslocal.

It is very simple to verify this behavior with curl

curl 'https://www.google.com' --socks5 127.0.0.1:1080 -v
*   Trying 127.0.0.1:1080...
* SOCKS5 connect to IPv6 2607:f8b0:400a:803::2003:443 (locally resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
...
curl 'https://www.google.com' --socks5-hostname 127.0.0.1:1080 -v
*   Trying 127.0.0.1:1080...
* SOCKS5 connect to www.google.com.hk:443 (remotely resolved)
* SOCKS5 request granted.
* Connected to 127.0.0.1 (127.0.0.1) port 1080 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
...
piangere commented 2 years ago

@zonyitoo How about UDP? Does the server resolves DNS as well?

There is definitely something not working with shadowsocks-rust with SOCKS5 (while shadowsocks-libev works fine). I wasn't able to pinpoint the root case (I have no idea how UDP BitTorrent tracker works). Could be related to DNS or IPv6.

My qBittorrent setting

Connection -> Proxy Server: Screenshot 2022-06-16 012100

Rust SOCKS5 proxy

sslocal -b 10.1.1.1:1099 -s xx.xx.xx.xx:83 -m 2022-blake3-aes-256-gcm -k yyyyyyyyyy --fast-open -U The server is also shadowsocks-rust (and of course, it supports UDP).

And I am not able to connect to any UDP tracker, e.g. "udp://tracker.slowcheetah.org:14730", though I can connect to HTTP trackers. Screenshot 2022-06-16 012213

libev SOCKS5 proxy

ss-local -b 10.1.1.1 -s xx.xx.xx.xx -p 8388 -l 1099 -k yyyyyyyyyy -m chacha20-ietf-poly1305 -u The server is also shadowsocks-libev.

I can connect to both UDP trackers and HTTP trackers: 3

zonyitoo commented 2 years ago

How about UDP? Does the server resolves DNS as well?

You can get the answer from RFC1928.

I don't think the root problem was domain names not resolving remotely. Here are some methods for debugging:

Run sslocal with -vvv and check out these line of logs:

https://github.com/shadowsocks/shadowsocks-rust/blob/9583f6b523f25f03ed3ee75c4eced9b3729929a3/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs#L161-L166

If you can see ... -> YOUR_TRACKERS_DOMAIN .., then SOCKS5 client is already sending requests for remote resolving domain names. And you can also verify that SOCKS5 client is actually send the request with SOCKS5 UDP ASSOCIATION.

And then check out this line of logs:

https://github.com/shadowsocks/shadowsocks-rust/blob/9583f6b523f25f03ed3ee75c4eced9b3729929a3/crates/shadowsocks-service/src/local/net/udp/association.rs#L426-L432

It shows that the association manager is going to send the UDP packet to the remote server.

https://github.com/shadowsocks/shadowsocks-rust/blob/9583f6b523f25f03ed3ee75c4eced9b3729929a3/crates/shadowsocks-service/src/local/net/udp/association.rs#L607-L613

This line of logs shows that the association manager have received a packet from remote target and going to send back to your client.

Run ssserver with -vvv and check out these line of logs:

https://github.com/shadowsocks/shadowsocks-rust/blob/9583f6b523f25f03ed3ee75c4eced9b3729929a3/crates/shadowsocks-service/src/server/udprelay.rs#L594-L600

Server's association manager receives a UDP packet from sslocal and going to send it to the target.

https://github.com/shadowsocks/shadowsocks-rust/blob/9583f6b523f25f03ed3ee75c4eced9b3729929a3/crates/shadowsocks-service/src/server/udprelay.rs#L719

Server's association manager receives a UDP packet from target and going to send back to sslocal.

qeatzy commented 2 years ago

@piangere @zonyitoo It's the client's fault. There are two variations in socks5, some client understand both, eg, curl will accept both socks5://127.0.0.1:1080 and socks5h://127.0.0.1:1080, (h for hostname), or firefox with config. Some client only understand one of them, eg, go get. In the unfortunate case of latter one (which is the default), the client will resolve domain name locally then pass ip & port to socks server. With ss, sslocal is the socks5/socks5h server, but it's just a front end, it cannot turn ip back to domain if that's what it only get passed to. You have two choices, 1 hijack dns 2 use a http/https proxy instead of socks5 proxy. -- this is the route ss-windows take, it launch a privoxy.exe in between. 3 inject dll to hijack connection or impl a network driver, eg, sstap or netch's solution. 4 or config you client or switch to alternative client that support socks5h.

It's very unlikely ss-rust will ever fail on this kind of simple case at 2022. If indeed some bug sneak in, it's a very good chance to pinpoint it to avoid further damage and contribute to community. (I'm quite doubtful about this, the surface is too small to make such gross mistake.)

dev4u commented 2 years ago

@qeatzy 或者你有找到什么证据支持你的疑问或者观点吗?

zonyitoo commented 2 years ago

I don't think this question is worth to discuss any further.

First of all, SOCKS Protocol Version 5, which is SOCKS5, is well defined in RFC1928. sslocal in this project, have fully supported CONNECT and UDP ASSOCIATE commands.

If anyone notice that domain names are not resolved by the remote server, just run sslocal with -vvv and see what exactly the CONNECT request was received from the SOCkS5 clients, for example:

socks5 TcpRequestHeader { command: TcpConnect, address: proxy.golang.org:443 } peer: 127.0.0.1:61852

If you found the address field was set with IP addresses, then your SOCKS5 clients resolves domain names before making SOCKS5 requests to sslocal.

qeatzy commented 2 years ago

@dev4u Seems there are some mis-understanding about socks & ss, or I didn't express it more clearly.

at https://github.com/shadowsocks/shadowsocks-rust/issues/870#issuecomment-1166459581, when I say client, it means socks client, it could be browser/curl/whatever.

As most users don't understand socks specification, being it RFC or xxx. Saying RTFM or RTFRFC helps nothing.

The real gap is this. And should be made more clear.

ssserver is not socks server, and sslocal is not socks client. You need to view ssserver + sslocal together as non-separable, sslocal is the socks server. And browser/curl/whatever is socks client. And if a socks server gets ip instead of domain name, it's the socks client's fault to resolve locally -- browser/curl/whatever.

@zonyitoo ss's socks might do have quirks, but not here. If i remember correctly, socks5 rfc mandate socks server return HTTP/1.1 200 Connection established only when remote established. In ss, that is CONNECT reach ssserver & dial remote & remote return & tell back to sslocal, only then should sslocal return 200 to socks5 client. Since I didn'g use ss for some time. The behavior might changed or I maybe remember it wrong. Besides, if above hold true, I still think it doesn't matter and not worth to change.

zonyitoo commented 2 years ago

If i remember correctly, socks5 rfc mandate socks server return HTTP/1.1 200 Connection established only when remote established. In ss, that is CONNECT reach ssserver & dial remote & remote return & tell back to sslocal, only then should sslocal return 200 to socks5 client.

This is the exact behavior of HTTP/1.1 proxy server protocol, which is defined in RFC7230. There is nothing related to SOCKS5.

IT IS IMPOSSIBLE FOR A SOCKS5 SERVER TO RETURN A HTTP STATUS CODE 200 TO SOCKS5 CLIENT!

If you are seeking for a HTTP/1.1 proxy server, just add "protocol": "http" to sslocal's configuration, and it will become a HTTP proxy server instead of SOCKS5.

qeatzy commented 2 years ago

@zonyitoo Seems mixing the two incorrectly, (my client support both https/http/socks) , only found it later. I known how how socks5 works, I wrote both socks5/https proxy from scratch. Just checked rfc & code, indeed no delay implications for socks5.

What I want to say is. The confusion comes from the mis-understanding.

The real gap is this. And should be made more clear.

ssserver is not socks server, and sslocal is not socks client. You need to view ssserver + sslocal together as non-separable, sslocal is the socks server. And browser/curl/whatever is socks client. And if a socks server gets ip instead of domain name, it's the socks client's fault to resolve locally -- browser/curl/whatever.

Words from poster illustrated the mis-understanding.

shadowsocks-rust的SOCK5模式,目前的DNS解析是本地(sslocal运行的机器)进行的,而不是服务端(ssserver)。不符合SOCKS5代理的要求。 shadowsocks-libev的SOCK5模式的DNS解析是在服务端进行。

And sslocal could even at another machine. Server: machine C -- US sslocal: machine B -- hk browser: machine A -- Beijing

It's sslocal being the socks5 server, if machine A resolve domain locally, there is nothing B & C can do. In the socks5 sense, only sslocal is exposed server, and ssserver is internal impl details, which doesn't match at all, v2ray & trojan & etc expose socks as front end. But many users incorrectly think ssserver is socks server due to the shadowsocks name.

As for the post to reproduce, he/she need to split A & B. Otherwise A & B is same, and many user has non technical qualifications to split the two, thus cannot tell A and B apart.

just add "protocol": "http" to sslocal's configuration,

Thx. I didn't use ss for some times. I write tcp multplexing tunnel approach myself.