public async Task Connect(EndPoint destination, IDuplexPipe client, IDuplexPipe server)
        {
            var pmp = new ProtocolMessagePipe(server);

            await pmp.WriteAsync(new Socks5VersionIdentifierMessage
            {
                Auth = _credential == null ? new [] { Socks5Message.AuthNone } : new [] { Socks5Message.AuthNone, Socks5Message.AuthUserPass }
            });

            var msm = await pmp.ReadAsync <Socks5MethodSelectionMessage>();

            switch (msm.SelectedAuth)
            {
            case Socks5Message.AuthNone:
                break;

            case Socks5Message.AuthUserPass:
                Debug.Assert(_credential != null);
                var name     = _credential.UserName;
                var password = _credential.Password;

                await pmp.WriteAsync(new Socks5UserPasswordRequestMessage
                {
                    User     = Encoding.UTF8.GetBytes(name),
                    Password = Encoding.UTF8.GetBytes(password),
                });

                var upResp = await pmp.ReadAsync <Socks5UserPasswordResponseMessage>();

                if (!upResp.Success)
                {
                    throw new UnauthorizedAccessException("Wrong username / password");
                }

                break;

            default:
                throw new NotSupportedException("Server not support our authencation method");
            }
            await pmp.WriteAsync(new Socks5RequestMessage
            {
                Command  = Socks5Message.CmdConnect,
                EndPoint = destination,
            });

            var reply = await pmp.ReadAsync <Socks5ReplyMessage>();

            if (reply.Reply != Socks5Message.ReplySucceed)
            {
                throw new Exception();
            }

            await DuplexPipe.CopyDuplexPipe(client, server);
        }
        public async Task ConvertUplink(IDuplexPipe client, IDuplexPipe server)
        {
            var up  = parameter.GetCrypto();
            var pmp = new ProtocolMessagePipe(server);
            var key = CryptoUtils.SSKDF(password, parameter.KeySize);

            var salt = new SaltMessage(parameter.NonceSize, true);
            await pmp.WriteAsync(salt);

            up.Init(key, salt.Salt.ToArray());
            Memory <byte> nonce = new byte[parameter.NonceSize];

            nonce.Span.Fill(0);
            // TODO write salt with data
            while (true)
            {
                var result = await client.Input.ReadAsync();

                if (result.IsCanceled || result.IsCompleted)
                {
                    return;
                }

                // TODO compress into one chunk when possible

                foreach (var item in result.Buffer)
                {
                    var mem = server.Output.GetMemory(item.Length);
                    var len = up.Encrypt(null, item.Span, mem.Span);
                    server.Output.Advance(len);
                }
                client.Input.AdvanceTo(result.Buffer.End);
            }
        }
Exemple #3
0
        public async Task ConvertUplink(IDuplexPipe client, IDuplexPipe server)
        {
            using var up = cryptoParameter.GetCrypto();
            var pmp  = new ProtocolMessagePipe(server);
            var salt = new SaltMessage(16, true);
            await pmp.WriteAsync(salt);

            var key = new byte[cryptoParameter.KeySize];

            HKDF.DeriveKey(HashAlgorithmName.SHA1, mainKey, key, salt.Salt.Span, _ssSubKeyInfo);
            up.Init(key, null);
            Memory <byte> nonce = new byte[cryptoParameter.NonceSize];

            nonce.Span.Fill(0);
            // TODO write salt with data
            while (true)
            {
                var result = await client.Input.ReadAsync();

                if (result.IsCanceled || result.IsCompleted)
                {
                    return;
                }

                // TODO compress into one chunk when possible

                foreach (var item in result.Buffer)
                {
                    foreach (var i in SplitBigChunk(item))
                    {
                        await pmp.WriteAsync(new AeadBlockMessage(up, nonce, cryptoParameter)
                        {
                            // in send routine, Data is readonly
                            Data = MemoryMarshal.AsMemory(i),
                        });
                    }
                }
                client.Input.AdvanceTo(result.Buffer.End);
            }
        }
        public async Task <IDuplexPipe> Handle(IDuplexPipe pipe)
        {
            var pmp = new ProtocolMessagePipe(pipe);
            var hs  = await pmp.ReadAsync <Socks5VersionIdentifierMessage>();

            var selected = Socks5Message.AuthNoAcceptable;

            if (enablePassword)
            {
                foreach (var a in Util.GetArray(hs.Auth))
                {
                    if (a == Socks5Message.AuthUserPass)
                    {
                        selected = Socks5Message.AuthUserPass;
                        break;
                    }

                    if (a == Socks5Message.AuthNone)
                    {
                        selected = Socks5Message.AuthNone;
                    }
                }
            }
            else
            {
                if (Util.GetArray(hs.Auth).Any(a => a == Socks5Message.AuthNone))
                {
                    selected = Socks5Message.AuthNone;
                }
            }

            await pmp.WriteAsync(new Socks5MethodSelectionMessage()
            {
                SelectedAuth = selected,
            });

            switch (selected)
            {
            case Socks5Message.AuthNoAcceptable:
            default:
                await pipe.Output.CompleteAsync();

                return(null);

            case Socks5Message.AuthNone:
                break;

            case Socks5Message.AuthUserPass:
                var token = await pmp.ReadAsync <Socks5UserPasswordRequestMessage>();

                var user     = Encoding.UTF8.GetString(token.User.Span);
                var password = Encoding.UTF8.GetString(token.Password.Span);
                var ar       = new Socks5UserPasswordResponseMessage();
                var success  =
                    passwords.TryGetValue(user, out var expectPassword) &&
                    expectPassword == password;
                ar.Success = success;
                await pmp.WriteAsync(ar);

                if (!success)
                {
                    await pipe.Output.CompleteAsync();

                    return(null);
                }

                break;
            }

            var req = await pmp.ReadAsync <Socks5RequestMessage>();

            var resp = new Socks5ReplyMessage();

            switch (req.Command)
            {
            case Socks5Message.CmdBind:
            case Socks5Message.CmdUdpAssociation:     // not support yet
                resp.Reply = Socks5Message.ReplyCommandNotSupport;
                break;

            case Socks5Message.CmdConnect:
                Console.WriteLine(req.EndPoint);
                // TODO: route and dial outbound


                resp.Reply = Socks5Message.ReplySucceed;
                break;
            }
            // TODO: write response, hand out connection
            await pmp.WriteAsync(resp);

            if (req.Command != Socks5Message.CmdConnect)
            {
                return(null);
            }

            return(pipe);
        }