async Task HandleClient(IClient client, int pipeBufferSize, Action <DuplexPipe> pipeCreatedAction, Func <IPEndPoint, Task <IClient> > createTargetClientFunc, Action <SmartBuffer, DuplexPipe, IClient, ShadowsocksAddress> targetClientConnectedAction, CancellationToken cancellationToken) { try { DuplexPipe pipe = new DuplexPipe(pipeBufferSize, _logger); pipe.ClientA = client; pipeCreatedAction(pipe);/////////////////// var readClientResult = await pipe.GetReader(client).Read(cancellationToken);//A. Read target addr (and payload). if (readClientResult.Result != ReadWriteResult.Succeeded) { client.Close(); return; } if (readClientResult.Read <= 0) { _logger?.LogWarning($"This should not happen. [{client.EndPoint.ToString()}]"); ////decrypt failed? available options: 1.leave it. 2.close connection. 3.add to blocklist. client.Close(); return; } var request = readClientResult.Memory; IPAddress targetIP = IPAddress.Any; //TODO target address check if (ShadowsocksAddress.TryResolve(request.SignificantMemory, out ShadowsocksAddress ssaddr)) //B. Resolve target addr. { _logger?.LogWarning($"Reading target addr. ATYP={ssaddr.ATYP}, client=[{client.EndPoint.ToString()}]"); IPEndPoint ipeTarget = await ssaddr.ToIPEndPoint(); if (IPAddress.Any == ipeTarget.Address || IPAddress.IPv6Any == ipeTarget.Address)//an empty IP. { _logger?.LogWarning($"Invalid target addr. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } _logger?.LogInformation($"Resolved target address:[{ipeTarget.ToString()}]. Connecting..."); IClient targetClient = await createTargetClientFunc(ipeTarget); //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()}]"); targetClientConnectedAction(request, pipe, targetClient, ssaddr);//////////////////////////////// _logger?.LogInformation($"Start piping..."); PipeClient(pipe, cancellationToken);//D. start piping. } else//invalid socks5 addr { _logger?.LogWarning($"Resolve target addr failed. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } await Task.CompletedTask; } catch (Exception ex) { _logger?.LogError(ex, "HandleClient"); } }
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; }
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; } } }
/* * # shadowsocks UDP Request (before encrypted) # +------+----------+----------+----------+ # | ATYP | DST.ADDR | DST.PORT | DATA | # +------+----------+----------+----------+ # | 1 | Variable | 2 | Variable | # +------+----------+----------+----------+ # # shadowsocks UDP Response (before encrypted) # +------+----------+----------+----------+ # | ATYP | DST.ADDR | DST.PORT | DATA | # +------+----------+----------+----------+ # | 1 | Variable | 2 | Variable | # +------+----------+----------+----------+ */ public async Task HandleUdp(IClient client, CancellationToken cancellationToken = default) { if (null == client) { return; } using (SmartBuffer localRequestCipher = SmartBuffer.Rent(1500)) { localRequestCipher.SignificantLength = await client.ReadAsync(localRequestCipher.Memory, cancellationToken);//A. read a packet if (0 == localRequestCipher.SignificantLength) { _logger?.LogWarning($"HandleUdp an empty udp packet received, client=[{client.EndPoint.ToString()}]"); client.Close(); return; } var cipher = _remoteServerConfig.CreateCipher(_logger); using (var localReqest = cipher.DecryptUdp(localRequestCipher.SignificanMemory)) //B. decrypt { if (null == localReqest || 0 == localReqest.SignificantLength) //decrypt failed, available options: 1.leave it. 2.close connection. 3.add to blocklist. { _logger?.LogWarning($"HandleUdp decrypt 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(localReqest.SignificanMemory, out ShadowsocksAddress ssaddr)) //C. resolve target address { 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 { targetIP = new IPAddress(ssaddr.Address.Span); } if (IPAddress.Any != targetIP)//D. target addr resolved. { IPEndPoint targetEndPoint = new IPEndPoint(targetIP, ssaddr.Port); var targetClient = await UdpClient1.ConnectAsync(targetEndPoint, _logger);//E. connect to target if (null == targetClient) { _logger?.LogInformation($"HandleUdp unable to connect target [{targetEndPoint.ToString()}]. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } await targetClient.WriteAsync(localReqest.Memory.Slice(ssaddr.RawMemory.Length), cancellationToken); //F. send payload . PipeUdp(client, targetClient, cipher, cancellationToken); //G. piping } else//resolve target address failed. { _logger?.LogWarning($"HandleUdp invalid target addr. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } } else { _logger?.LogWarning($"HandleUdp resolve target addr failed. client=[{client.EndPoint.ToString()}]"); client.Close(); return; } } }//end using }