使用WCF来实现一个ShadowSocks客户端(四)

实现ShadowSocks客户端

在具体说实现之前,还有个问题要描述一下,就是我在不使用WCF Session管理的情况下在实现了IDuplexChannel后为何还要实现IDuplexSessionChannel,而且IDuplexSessionChannel的实现仅仅是继承了IDuplexChannel实现,这样做的目的是什么。

    class CustomTcpSocketSessionChannel : CustomTcpSocketChannel, IDuplexSessionChannel
    {
        internal CustomTcpSocketSessionChannel(Socket socket, ChannelManagerBase channelManager, MessageEncoderFactory factory, BindingContext context)
            : base(socket, channelManager, factory, context)
        { }

        public IDuplexSession Session
        {
            get
            {
                return null;
            }
        }
     }

本来我是只想用IDuplexChannel作为自定义Channel实现的,就是为了简单,但是用IDuplexChannel实现的Channel在实际使用中有个很大问题,就是当有多个客户端连接到服务端时,服务端对客户端发送的请求进行回调时,所有回调都只回调到第一个连接上服务端的客户端,这是个非常困惑的问题,目前也不清楚具体是什么原因导致。但是如果是作为IDuplexSessionChannel实现,就像上面贴的用原有IDuplexChannel实现类仅仅实现该接口,就算让该接口的成员直接返回null也没关系,这个问题就解决了,不知道WCF内部是怎么回事,但换个接口的确就解决了问题。

开始说一说怎么用之前定义的那么多东西来实现ShadowSocks了。
先说说理由,为什么要做ShadowSocks客户端,原因就是为了测试之前做的东西管不管用,而选择ShadowSocks是因为ShadowSocks协议简单,实现起来方便,测试起来也非常简单。

Shadowsocks客户端本身其实就是转发socks5协议,只不过在转发的时候,除了协议头3个字节外,其他所有数据与shadowsocks服务端通讯时都是加密的,加密解密模块是直接用shadowsocks csharp客户端的,真正要做的就是数据转发。

发送加密的数据到ss服务器并从服务器接收加密数据这块是非常简单的,就是一个WCFTcpClient:

    internal class ProxyClient : WCFTcpClient
    {
        private string sid;
        public event Action<string, byte[]> DataReceived = delegate { };


        public ProxyClient(string uri, string sessionId) : base(uri)
        {
            sid = sessionId;
        }

        protected override void OnConnect(ISocketChannel session)
        {
        }

        protected override void OnData(string sessionId, byte[] data)
        {
            DataReceived(SessionId, data);
        }

        protected override void OnDisconnect(ISocketChannel session)
        {
        }

        public override void Invoke(byte[] data)
        {
            base.Invoke(data);
        }

        public string SessionId
        {
            get { return sid; }
        }
    }

伪socks5代理也就是转发服务这块,主要就是处理协议头这块

        protected override void OnData(string sessionId, byte[] data)
        {
            HandShakeState state;
            if (!states.TryGetValue(sessionId, out state))
                throw new InvalidOperationException();

            switch (state)
            {
                case HandShakeState.First:
                    states[sessionId] = HandShakeState.Second;
                    Handshake(sessionId, data);
                    break;

                case HandShakeState.Second:
                    states[sessionId] = HandShakeState.Final;
                    HandshakeSecond(sessionId, data);
                    break;

                case HandShakeState.Final:
                    Pass(sessionId, data);
                    break;
            }
        }

分成3次握手,第一次是响应浏览器等的socks5协议请求

        private void Handshake(string sessionId, byte[] data)
        {
            if (data.Length <= 1)
            {
                Close();
                return;
            }

            byte[] response = { 5, 0 };
            if (data[0] != 5)
            {
                // reject socks 4
                response = new byte[] { 0, 91 };
            }

            Callback(sessionId, response);
        }

第二次是跳过前3个字节也就是版本信息及服务请求之类的,把真正有用的信息加密转发给服务器,注意到这里拒绝掉了3(UDP)请求,因为没实现。

        private void HandshakeSecond(string sessionId, byte[] data)
        {
            if (data.Length < 3)
            {
                Close();
                return;
            }
            if (data.Length > 3)
            {
                var extraHeader = new byte[data.Length - 3];
                Array.Copy(data, 3, extraHeader, 0, data.Length - 3);
                extraHeaders[sessionId] = extraHeader;
            }            

            var command = data[1];
            if (command == 1)
            {
                var proxy = NewProxy(sessionId);
                proxy.Open();

                byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
                Callback(response);
            }
            else if (command == 3)
            {
                throw new NotSupportedException();
            }
        }

一旦前2次成功,后面就正常的把从ss服务器接到的数据解密返回给浏览器,把从浏览器收到的数据加密返回给ss服务器

        private void Pass(string sessionId, byte[] data)
        {
            int size;
            byte[] buffer1, buffer2;

            lock (encryptBuffer)
            {
                buffer2 = data;
                byte[] extraHeader;
                if (extraHeaders.TryRemove(sessionId, out extraHeader))
                {
                    buffer2 = new byte[extraHeader.Length + data.Length];
                    Array.Copy(extraHeader, buffer2, extraHeader.Length);
                    Array.Copy(data, 0, buffer2, extraHeader.Length, data.Length);
                }
                encryptor.Encrypt(buffer2, buffer2.Length, encryptBuffer, out size);
                buffer1 = Truncate(encryptBuffer, size);
            }

            var proxy = GetProxy(sessionId);
            proxy.Invoke(buffer1);
        }

        private byte[] Truncate(byte[] array, int size)
        {
            var data = new byte[size];

            Array.Copy(array, data, size);
            return data;
        }

        private void Proxy_DataReceived(string sessionId, byte[] data)
        {
            int size;
            byte[] buffer;

            lock (decryptBuffer)
            {
                encryptor.Decrypt(data, data.Length, decryptBuffer, out size);
                buffer = Truncate(decryptBuffer, size);
            }

            Callback(sessionId, buffer);
        }

就这样一个简单的Shadowsocks客户端lib就做好了,然后只要做个客户端创建ProxyServer,并Open就算大功告成了。

最后由于WCF架构非常复杂,我这些个实现可能并不符合WCF实施规范,而且还有可能存在很多错误实施,所有这个东西也仅作参考,不过这个测试用的shadowsocks还是能正常工作的,当然它作为一个网络程序还差非常多严格测试,目前还只能算是测试参考用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章