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); DuplexPipe pipe = new DuplexPipe(client, relayClient, Defaults.ReceiveBufferSize, _logger); IClientFilter cipherFilter = new Cipher.TcpCipherFilter(cipher, _logger); pipe.AddFilter(relayClient, cipherFilter); var writeResult = await pipe.GetWriter(relayClient).Write(ssaddr.RawMemory, cancellationToken); //C. send target addr to ss-remote. _logger?.LogInformation($"Send target addr {writeResult.Written} bytes. {writeResult.Result}."); await client.WriteAsync(NegotiationResponse.CommandConnectOK, cancellationToken); //D. notify client to send data. PipeClient(pipe, cancellationToken); //E. start piping. } else { _logger?.LogWarning("resolve target addr failed."); client.Close(); } } break; case 0x2: //bind { request.SignificantMemory.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.SignificantMemory, cancellationToken); } //TODO client.Closing += this.Client_Closing; } break; default: break; } } }