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); } }
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); }