坑死啊,之前使用 shadowsocks-csharp 作为上层代理就发现有些不对劲,这次仔细研究下代码,发现被坑了…
shadowsocks-csharp 并没有按标准 socks5 协议实现…
socks5 接受新连接是在这里:
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/Listener.cs#L83
1 2 3 4 5 6 7 | // Start an asynchronous socket to listen for connections. Console.WriteLine("Shadowsocks started"); _tcpSocket.BeginAccept( new AsyncCallback(AcceptCallback), _tcpSocket); UDPState udpState = new UDPState(); _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); |
开始第一次数据接收,这里有个坑,第一次会接受 buf.Length (4096) 直接的数据,但是后面时却只处理了前几个字节,后面的内容被抛弃了。我上次为了降低延迟鉴定及请求命令一起发过来,结果只收到了鉴定回应,现在知道原因了,ss 只处理了鉴定,连接请求被丢弃了…
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/Listener.cs#L159
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void AcceptCallback(IAsyncResult ar) { Socket listener = (Socket)ar.AsyncState; try { Socket conn = listener.EndAccept(ar); byte[] buf = new byte[4096]; object[] state = new object[] { conn, buf }; conn.BeginReceive(buf, 0, buf.Length, 0, new AsyncCallback(ReceiveCallback), state); } catch (ObjectDisposedException) |
这里遍历自身支持的服务,确定是用什么服务来处理。
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/Listener.cs#L198
1 2 3 4 5 6 7 | foreach (Service service in _services) { if (service.Handle(buf, bytesRead, conn, null)) { return; } } |
TCPRelay 为每个连接建立一个 Handler 来处理请求。
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L22
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public bool Handle(byte[] firstPacket, int length, Socket socket, object state) { if (socket.ProtocolType != ProtocolType.Tcp) { return false; } if (length < 2 || firstPacket[0] != 5) { return false; } socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); Handler handler = new Handler(); handler.connection = socket; handler.controller = _controller; handler.Start(firstPacket, length); return true; } |
将第一次收到的数据保存到内部变量,HandshakeReceive里面去处理。
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L94
1 2 3 4 5 6 | public void Start(byte[] firstPacket, int length) { this._firstPacket = firstPacket; this._firstPacketLength = length; this.HandshakeReceive(); } |
这里就是有问题的地方,对第一次收到的数据只检查了下头就把数据抛弃了…
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L175
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | private void HandshakeReceive() { if (closed) { return; } try { int bytesRead = _firstPacketLength; if (bytesRead > 1) { byte[] response = { 5, 0 }; if (_firstPacket[0] != 5) { // reject socks 4 response = new byte[] { 0, 91 }; Console.WriteLine("socks 5 protocol error"); } connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); } else { this.Close(); } } catch (Exception e) { Logging.LogUsefulException(e); this.Close(); } } |
这里对收到的代理请求直接回复连接成功,而没有管是不是成功。这又是一个坑啊…造成测速失败…
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/TCPRelay.cs#L232
1 2 3 4 5 6 7 8 9 10 | command = connetionRecvBuffer[1]; if (command == 1) { byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); } else if (command == 3) { HandleUDPAssociate(); } |