public async Task HandleTcp(IClient client, CancellationToken cancellationToken = default) { if (null == client) { return; } await HandleClient(client, Defaults.ReceiveBufferSize, (pipe) => { var cipher = _remoteServerConfig.CreateCipher(_logger); IClientFilter cipherFilter = new Cipher.TcpCipherFilter(cipher, _logger); pipe.AddFilter(client, cipherFilter); }, async (targetIPEndPoint) => { return(await TcpClient1.ConnectAsync(targetIPEndPoint, _logger)); }, async (request, pipe, targetClient, targetSsAddr) => { pipe.ClientB = targetClient; if (request.SignificantLength > targetSsAddr.RawMemory.Length) //have some payload { _logger?.LogInformation($"Writing payload before piping..."); //await targetClient.WriteAsync(request.SignificantMemory.Slice(ssaddr.RawMemory.Length)); await pipe.GetWriter(targetClient).Write(request.SignificantMemory.Slice(targetSsAddr.RawMemory.Length), cancellationToken); } request.Dispose(); }, cancellationToken); await Task.CompletedTask; }
public async Task HandleHttp(IClient client, CancellationToken cancellationToken) { if (null == client) { return; } using (SmartBuffer clientRequest = SmartBuffer.Rent(2048)) { clientRequest.SignificantLength = await client.ReadAsync(clientRequest.Memory, cancellationToken); if (10 >= clientRequest.SignificantLength) { goto GOODBYE503; } if (HttpProxyHeaderResolver.TryResolve(clientRequest.SignificantMemory, out HttpProxyHeaderResolver.Verb httpVerb, out Uri targetHost, out byte[] normalHttpRequestHeader)) { //_logger?.LogInformation($"HttpRoxyServer Verb={httpVerb.ToString()}."); var server = _serverLoader.Load(null); if (null == server) { _logger?.LogInformation($"HttpRoxyServer Proxy server not found."); goto GOODBYE503; } var serverAddr = await server.GetIPEndPoint(); if (null == serverAddr) { _logger?.LogInformation($"HttpRoxyServer Unable to get proxy server address."); goto GOODBYE503; } var relayClient = await TcpClient1.ConnectAsync(serverAddr, _logger); //A. connect ss-remote if (null == relayClient) //unable to connect ss-remote { _logger?.LogInformation($"HttpRoxyServer Unable to connect ss-remote:[{serverAddr.ToString()}]"); goto GOODBYE503; } using (var relayRequst = SmartBuffer.Rent(HttpProxyHeaderResolver.Verb.CONNECT == httpVerb ? 300 : normalHttpRequestHeader.Length + 300)) { if (ShadowsocksAddress.TryParse(targetHost, out Tuple <byte, ushort, byte[]> ssaddr))//B. construct sssaddr { //_logger?.LogInformation($"HttpRoxyServer ATYP={ssaddr.Item1}, port={ssaddr.Item2}"); ShadowsocksAddress.TrySerailizeTo(ssaddr.Item1, ssaddr.Item3, ssaddr.Item2, relayRequst.Memory, out int written); relayRequst.SignificantLength = written; if (HttpProxyHeaderResolver.Verb.CONNECT != httpVerb) { normalHttpRequestHeader.CopyTo(relayRequst.FreeMemory); relayRequst.SignificantLength += normalHttpRequestHeader.Length; } var cipher = server.CreateCipher(_logger); DefaultPipe pipe = new DefaultPipe(client, relayClient, Defaults.ReceiveBufferSize, _logger); Cipher.TcpCipherFilter cipherFilter = new Cipher.TcpCipherFilter(relayClient, cipher, _logger); pipe.ApplyClientFilter(cipherFilter); var writeResult = await pipe.Writer[relayClient].Write(relayRequst.SignificantMemory, cancellationToken);//C. send target addr (& http header) to ss-remote. _logger?.LogInformation($"Send target addr {writeResult.Written} bytes. {writeResult.Result}."); if (HttpProxyHeaderResolver.Verb.CONNECT == httpVerb) { await client.WriteAsync(RESPONSE_200_Connection_Established, cancellationToken); } PipeClient(pipe, cancellationToken);//D. start piping. return; } else { _logger?.LogInformation($"HttpRoxyServer parse host URL failed."); goto GOODBYE503; } } }
public async Task HandleTcp(IClient client, CancellationToken cancellationToken) { if (null == client) { return; } if (!await Handshake(client, cancellationToken)) { return; } //Handshake using (SmartBuffer request = SmartBuffer.Rent(300), response = SmartBuffer.Rent(300)) { request.SignificantLength = await client.ReadAsync(request.Memory, cancellationToken); if (5 >= request.SignificantLength) { client.Close(); return; } #region socks5 /* +----+-----+-------+------+----------+----------+ |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ | | Where: | | o VER protocol version: X'05' | o CMD | o CONNECT X'01' | o BIND X'02' | o UDP ASSOCIATE X'03' | o RSV RESERVED | o ATYP address type of following address | o IP V4 address: X'01' | o DOMAINNAME: X'03' | o IP V6 address: X'04' | o DST.ADDR desired destination address | o DST.PORT desired destination port in network octet | order | | +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+ | | Where: | | o VER protocol version: X'05' | o REP Reply field: | o X'00' succeeded | o X'01' general SOCKS server failure | o X'02' connection not allowed by ruleset | o X'03' Network unreachable | o X'04' Host unreachable | o X'05' Connection refused | o X'06' TTL expired | o X'07' Command not supported | o X'08' Address type not supported | o X'09' to X'FF' unassigned | o RSV RESERVED | o ATYP address type of following address | o IP V4 address: X'01' | o DOMAINNAME: X'03' | o IP V6 address: X'04' | o BND.ADDR server bound address | o BND.PORT server bound port in network octet order */ #endregion switch (request.Memory.Span[1]) { case 0x1: //connect //TODO validate addr { var server = _serverLoader.Load(null); if (null == server) { _logger?.LogInformation($"proxy server not found."); client.Close(); break; } var serverAddr = await server.GetIPEndPoint(); if (null == serverAddr) { _logger?.LogInformation($"unable to get proxy server address."); client.Close(); break; } var relayClient = await TcpClient1.ConnectAsync(serverAddr, _logger); //A. connect ss-remote if (null == relayClient) //unable to connect ss-remote { _logger?.LogInformation($"unable to connect ss-remote:[{serverAddr.ToString()}]"); await client.WriteAsync(NegotiationResponse.CommandConnectFailed, cancellationToken); client.Close(); break; } var clientRequest = request; if (ShadowsocksAddress.TryResolve(clientRequest.Memory.Slice(3), out ShadowsocksAddress ssaddr)) //B.resove target addr. { var cipher = server.CreateCipher(_logger); using (var targetAddrCipher = cipher.EncryptTcp(ssaddr.RawMemory)) { int sent = await relayClient.WriteAsync(targetAddrCipher.SignificanMemory, cancellationToken); //C. send target addr to ss-remote. _logger?.LogInformation($"Send target addr {sent}={targetAddrCipher.SignificantLength}."); await client.WriteAsync(NegotiationResponse.CommandConnectOK, cancellationToken); //D. notify client to send data. PipeTcp(client, relayClient, cipher, cancellationToken); //E. start piping. } } else { _logger?.LogWarning("resolve target addr failed."); client.Close(); } } break; case 0x2: //bind { request.SignificanMemory.CopyTo(response.Memory); response.Memory.Span[1] = 0x7; await client.WriteAsync(response.Memory.Slice(0, request.SignificantLength), cancellationToken); client.Close(); } break; case 0x3: //udp assoc { if (ShadowsocksAddress.TrySerailizeTo( (byte)(AddressFamily.InterNetworkV6 == client.LocalEndPoint.AddressFamily ? 0x4 : 0x1), client.LocalEndPoint.Address.GetAddressBytes(), (ushort)client.LocalEndPoint.Port, response.Memory, out int written)) { response.SignificantLength = written; await client.WriteAsync(response.SignificanMemory, cancellationToken); } //TODO client.Closing += this.Client_Closing; } break; default: break; } } }
public async Task HandleTcp(IClient client, CancellationToken cancellationToken = default) { if (null == client) { return; } using (SmartBuffer localRequestCipher = SmartBuffer.Rent(Defaults.ReceiveBufferSize)) { localRequestCipher.SignificantLength = await client.ReadAsync(localRequestCipher.Memory, cancellationToken);//A. Receive target addr. //decrypt var cipher = _remoteServerConfig.CreateCipher(_logger); using (var localReqestPlain = cipher.DecryptTcp(localRequestCipher.SignificanMemory)) { if (0 == localRequestCipher.SignificantLength) { //decrypt failed, available options: 1.leave it. 2.close connection. 3.add to blocklist. _logger?.LogWarning($"Decrypt target addr failed, client=[{client.EndPoint.ToString()}]"); client.Close();//->local pipe broken-> local pipe close. return; } IPAddress targetIP = IPAddress.Any; //TODO target address check if (ShadowsocksAddress.TryResolve(localReqestPlain.Memory, out ShadowsocksAddress ssaddr)) //B. Resolve target addr. { if (0x3 == ssaddr.ATYP) //a domain name { var ips = await _dnsCache.ResolveHost(Encoding.UTF8.GetString(ssaddr.Address.ToArray())); if (ips != null && ips.Length > 0) { targetIP = ips[0]; } } else//IPv4/v6 { targetIP = new IPAddress(ssaddr.Address.Span); } if (IPAddress.Any == targetIP)//a empty IP. { _logger?.LogWarning($"Invalid target addr. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } IPEndPoint ipeTarget = new IPEndPoint(targetIP, ssaddr.Port); _logger.LogInformation($"Resolved target address:[{ipeTarget.ToString()}]"); _logger.LogInformation($"Connecting to [{ipeTarget.ToString()}]..."); var targetClient = await TcpClient1.ConnectAsync(ipeTarget, _logger); //C. Connect target if (null == targetClient) //connect target failed. { _logger?.LogInformation($"Unable to connect target [{ipeTarget.ToString()}]. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } _logger.LogInformation($"Connected to [{ipeTarget.ToString()}]"); if (localReqestPlain.Memory.Length > ssaddr.RawMemory.Length)//have some payload { _logger.LogInformation($"Writing payload before piping..."); await targetClient.WriteAsync(localReqestPlain.SignificanMemory.Slice(ssaddr.RawMemory.Length)); } _logger.LogInformation($"Start piping..."); PipeTcp(client, targetClient, cipher, cancellationToken);//D. start piping. } else//invalid socks5 addr { _logger?.LogWarning($"StandardRemoteSocks5Handler HandleTcp resolve target addr failed. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } } } await Task.CompletedTask; }