shadowsocks-csharp 源码分析

坑死啊,之前使用 shadowsocks-csharp 作为上层代理就发现有些不对劲,这次仔细研究下代码,发现被坑了…
shadowsocks-csharp 并没有按标准 socks5 协议实现…

socks5 接受新连接是在这里:
https://github.com/shadowsocks/shadowsocks-windows/blob/master/shadowsocks-csharp/Controller/Service/Listener.cs#L83

// 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

        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

                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

        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

        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

        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

                    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();
                    }
  WordPress › 错误