Exemple #1
0
 public void BufferPool()
 {
     BufferPool bp = new BufferPool(10000);
     int ct = 1000;
     for (int i = 0; i < ct; i++) {
         byte[] b = bp.GetBuffer();
         Assert.AreEqual(10000, b.Length);
         bp.ReturnBuffer(b);
     }
     Console.WriteLine("{0} allocations for {1} uses of buffer pool.", bp.NumAllocations, ct);
 }
        public ushort SendPacket(byte[] packetData, int length, byte channelID)
        {
            if (length > config.MaxPacketSize)
            {
                throw new ArgumentOutOfRangeException("Packet is too large to send, max packet size is " + config.MaxPacketSize + " bytes");
            }

            ushort sequence = this.sequence++;
            ushort ack;
            uint   ackBits;

            receivedPackets.GenerateAckBits(out ack, out ackBits);

            SentPacketData sentPacketData = sentPackets.Insert(sequence);

            sentPacketData.time        = this.time;
            sentPacketData.packetBytes = (uint)(config.PacketHeaderSize + length);
            sentPacketData.acked       = false;

            if (length <= config.FragmentThreshold)
            {
                // regular packet

                byte[] transmitData         = BufferPool.GetBuffer(2048);
                int    headerBytes          = PacketIO.WritePacketHeader(transmitData, channelID, sequence, ack, ackBits);
                int    transmitBufferLength = length + headerBytes;

                Buffer.BlockCopy(packetData, 0, transmitData, headerBytes, length);

                config.TransmitPacketCallback(transmitData, transmitBufferLength);

                BufferPool.ReturnBuffer(transmitData);
            }
            else
            {
                // fragmented packet

                byte[] packetHeader      = BufferPool.GetBuffer(Defines.MAX_PACKET_HEADER_BYTES);
                int    packetHeaderBytes = PacketIO.WritePacketHeader(packetHeader, channelID, sequence, ack, ackBits);

                int numFragments       = (length / config.FragmentSize) + ((length % config.FragmentSize) != 0 ? 1 : 0);
                int fragmentBufferSize = Defines.FRAGMENT_HEADER_BYTES + Defines.MAX_PACKET_HEADER_BYTES + config.FragmentSize;

                byte[] fragmentPacketData = BufferPool.GetBuffer(2048);
                int    qpos = 0;

                byte prefixByte = 1;
                prefixByte |= (byte)((channelID & 0x03) << 6);

                for (int fragmentID = 0; fragmentID < numFragments; fragmentID++)
                {
                    using (var writer = ByteArrayReaderWriter.Get(fragmentPacketData))
                    {
                        writer.Write(prefixByte);
                        writer.Write(sequence);
                        writer.Write((byte)fragmentID);
                        writer.Write((byte)(numFragments - 1));

                        if (fragmentID == 0)
                        {
                            writer.WriteBuffer(packetHeader, packetHeaderBytes);
                        }

                        int bytesToCopy = config.FragmentSize;
                        if (qpos + bytesToCopy > length)
                        {
                            bytesToCopy = length - qpos;
                        }

                        for (int i = 0; i < bytesToCopy; i++)
                        {
                            writer.Write(packetData[qpos++]);
                        }

                        int fragmentPacketBytes = (int)writer.WritePosition;
                        config.TransmitPacketCallback(fragmentPacketData, fragmentPacketBytes);
                    }
                }

                BufferPool.ReturnBuffer(packetHeader);
                BufferPool.ReturnBuffer(fragmentPacketData);
            }

            return(sequence);
        }
Exemple #3
0
 public PageScheduler()
 {
     _stream = new ConstantStream <Page>(Config.NAME_FILE_PAGE_SOURCE, new PageDeserializer(),
                                         capacity => { return(_pool.GetBuffer(capacity)); });
 }
Exemple #4
0
        ///////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="stream"></param>
        public luaProtoReader(MemoryStream stream)
        {
            _stream = stream;
            _buff   = BufferPool.GetBuffer();
        }
Exemple #5
0
        public void ReceivePacket(byte[] packetData, int bufferLength)
        {
            if (bufferLength > config.MaxPacketSize)
            {
                throw new ArgumentOutOfRangeException("Packet is larger than max packet size");
            }
            if (packetData == null)
            {
                throw new InvalidOperationException("Tried to receive null packet!");
            }
            if (bufferLength > packetData.Length)
            {
                throw new InvalidOperationException("Buffer length exceeds actual packet length!");
            }
            byte b = packetData[0];

            if ((b & 1) == 0)
            {
                byte   channelID;
                ushort arg;
                ushort ack;
                uint   ackBits;
                int    num = PacketIO.ReadPacketHeader(packetData, 0, bufferLength, out channelID, out arg, out ack, out ackBits);
                bool   flag;
                lock (receivedPackets)
                {
                    flag = !receivedPackets.TestInsert(arg);
                }
                if (!flag && (b & 0x80) == 0)
                {
                    if (num >= bufferLength)
                    {
                        throw new FormatException("Buffer too small for packet data!");
                    }
                    ByteBuffer byteBuffer = ObjPool <ByteBuffer> .Get();

                    byteBuffer.SetSize(bufferLength - num);
                    byteBuffer.BufferCopy(packetData, num, 0, byteBuffer.Length);
                    config.ProcessPacketCallback(arg, byteBuffer.InternalBuffer, byteBuffer.Length);
                    lock (receivedPackets)
                    {
                        ReceivedPacketData receivedPacketData = receivedPackets.Insert(arg);
                        if (receivedPacketData == null)
                        {
                            throw new InvalidOperationException("Failed to insert received packet!");
                        }
                        receivedPacketData.time        = time;
                        receivedPacketData.packetBytes = (uint)(config.PacketHeaderSize + bufferLength);
                    }
                    ObjPool <ByteBuffer> .Return(byteBuffer);
                }
                if (flag && (b & 0x80) == 0)
                {
                    return;
                }
                for (int i = 0; i < 32; i++)
                {
                    if ((ackBits & 1) != 0)
                    {
                        ushort         obj            = (ushort)(ack - i);
                        SentPacketData sentPacketData = sentPackets.Find(obj);
                        if (sentPacketData != null && !sentPacketData.acked)
                        {
                            sentPacketData.acked = true;
                            if (config.AckPacketCallback != null)
                            {
                                config.AckPacketCallback(obj);
                            }
                            float num2 = (float)(time - sentPacketData.time) * 1000f;
                            if ((rtt == 0f && num2 > 0f) || Math.Abs(rtt - num2) < 1E-05f)
                            {
                                rtt = num2;
                            }
                            else
                            {
                                rtt += (num2 - rtt) * config.RTTSmoothFactor;
                            }
                        }
                    }
                    ackBits >>= 1;
                }
                return;
            }
            int    fragmentID;
            int    numFragments;
            int    fragmentBytes;
            ushort num3;
            ushort ack2;
            uint   ackBits2;
            byte   channelID2;
            int    num4 = PacketIO.ReadFragmentHeader(packetData, 0, bufferLength, config.MaxFragments, config.FragmentSize, out fragmentID, out numFragments, out fragmentBytes, out num3, out ack2, out ackBits2, out channelID2);
            FragmentReassemblyData fragmentReassemblyData = fragmentReassembly.Find(num3);

            if (fragmentReassemblyData == null)
            {
                fragmentReassemblyData = fragmentReassembly.Insert(num3);
                if (fragmentReassemblyData == null)
                {
                    return;
                }
                fragmentReassemblyData.Sequence             = num3;
                fragmentReassemblyData.Ack                  = 0;
                fragmentReassemblyData.AckBits              = 0u;
                fragmentReassemblyData.NumFragmentsReceived = 0;
                fragmentReassemblyData.NumFragmentsTotal    = numFragments;
                fragmentReassemblyData.PacketBytes          = 0;
                Array.Clear(fragmentReassemblyData.FragmentReceived, 0, fragmentReassemblyData.FragmentReceived.Length);
            }
            if (numFragments == fragmentReassemblyData.NumFragmentsTotal && !fragmentReassemblyData.FragmentReceived[fragmentID])
            {
                fragmentReassemblyData.NumFragmentsReceived++;
                fragmentReassemblyData.FragmentReceived[fragmentID] = true;
                byte[] buffer = BufferPool.GetBuffer(2048);
                Buffer.BlockCopy(packetData, num4, buffer, 0, bufferLength - num4);
                fragmentReassemblyData.StoreFragmentData(channelID2, num3, ack2, ackBits2, fragmentID, config.FragmentSize, buffer, bufferLength - num4);
                BufferPool.ReturnBuffer(buffer);
                if (fragmentReassemblyData.NumFragmentsReceived == fragmentReassemblyData.NumFragmentsTotal)
                {
                    ByteBuffer byteBuffer2 = ObjPool <ByteBuffer> .Get();

                    byteBuffer2.SetSize(fragmentReassemblyData.PacketDataBuffer.Length - fragmentReassemblyData.HeaderOffset);
                    Buffer.BlockCopy(fragmentReassemblyData.PacketDataBuffer.InternalBuffer, fragmentReassemblyData.HeaderOffset, byteBuffer2.InternalBuffer, 0, byteBuffer2.Length);
                    ReceivePacket(byteBuffer2.InternalBuffer, byteBuffer2.Length);
                    ObjPool <ByteBuffer> .Return(byteBuffer2);

                    fragmentReassemblyData.PacketDataBuffer.SetSize(0);
                    fragmentReassembly.Remove(num3);
                }
            }
        }
Exemple #6
0
        /// <summary>
        ///     This is called when client is aware of proxy
        ///     So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint">The explicit endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns>The task.</returns>
        private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream = new HttpClientStream(this, clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken);

            Task <TcpServerConnection?>?prefetchConnectionTask = null;
            bool closeServerConnection = false;

            try
            {
                TunnelConnectSessionEventArgs?connectArgs = null;

                var method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                if (clientStream.IsClosed)
                {
                    return;
                }

                // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (method == KnownMethod.Connect)
                {
                    // read the first line HTTP command
                    var requestLine = await clientStream.ReadRequestLine(cancellationToken);

                    if (requestLine.IsEmpty())
                    {
                        return;
                    }

                    var connectRequest = new ConnectRequest(requestLine.RequestUri)
                    {
                        RequestUriString8 = requestLine.RequestUri,
                        HttpVersion       = requestLine.Version
                    };

                    await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);

                    connectArgs             = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest, clientStream, cancellationTokenSource);
                    clientStream.DataRead  += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
                    clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);

                    await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);

                    // filter out excluded host names
                    bool decryptSsl  = endPoint.DecryptSsl && connectArgs.DecryptSsl;
                    bool sendRawData = !decryptSsl;

                    if (connectArgs.DenyConnect)
                    {
                        if (connectArgs.HttpClient.Response.StatusCode == 0)
                        {
                            connectArgs.HttpClient.Response = new Response
                            {
                                HttpVersion       = HttpHeader.Version11,
                                StatusCode        = (int)HttpStatusCode.Forbidden,
                                StatusDescription = "Forbidden"
                            };
                        }

                        // send the response
                        await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response, cancellationToken);

                        return;
                    }

                    if (await checkAuthorization(connectArgs) == false)
                    {
                        await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc);

                        // send the response
                        await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response, cancellationToken);

                        return;
                    }

                    // write back successful CONNECT response
                    var response = ConnectResponse.CreateSuccessfulConnectResponse(connectRequest.HttpVersion);

                    // Set ContentLength explicitly to properly handle HTTP 1.0
                    response.ContentLength = 0;
                    response.Headers.FixProxyHeaders();
                    connectArgs.HttpClient.Response = response;

                    await clientStream.WriteResponseAsync(response, cancellationToken);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                    if (clientStream.IsClosed)
                    {
                        return;
                    }

                    bool isClientHello = clientHelloInfo != null;
                    if (clientHelloInfo != null)
                    {
                        connectRequest.TunnelType      = TunnelType.Https;
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);

                    if (decryptSsl && clientHelloInfo != null)
                    {
                        connectRequest.IsHttps = true; // todo: move this line to the previous "if"
                        clientStream.Connection.SslProtocol = clientHelloInfo.SslProtocol;

                        bool http2Supported = false;

                        if (EnableHttp2)
                        {
                            var alpn = clientHelloInfo.GetAlpn();
                            if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
                            {
                                // test server HTTP/2 support
                                try
                                {
                                    // todo: this is a hack, because Titanium does not support HTTP protocol changing currently
                                    var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                                    true, SslExtensions.Http2ProtocolAsList,
                                                                                                    true, true, cancellationToken);

                                    if (connection != null)
                                    {
                                        http2Supported = connection.NegotiatedApplicationProtocol ==
                                                         SslApplicationProtocol.Http2;

                                        // release connection back to pool instead of closing when connection pool is enabled.
                                        await tcpConnectionFactory.Release(connection, true);
                                    }
                                }
                                catch (Exception)
                                {
                                    // ignore
                                }
                            }
                        }

                        if (EnableTcpServerConnectionPrefetch)
                        {
                            // don't pass cancellation token here
                            // it could cause floating server connections when client exits
                            prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                              true, null, false, true,
                                                                                              CancellationToken.None);
                        }

                        string connectHostname = requestLine.RequestUri.GetString();
                        int    idx             = connectHostname.IndexOf(":");
                        if (idx >= 0)
                        {
                            connectHostname = connectHostname.Substring(0, idx);
                        }

                        X509Certificate2?certificate = null;
                        SslStream?       sslStream   = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, false);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            var options = new SslServerAuthenticationOptions();
                            if (EnableHttp2 && http2Supported)
                            {
                                options.ApplicationProtocols = clientHelloInfo.GetAlpn();
                                if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
                                {
                                    options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
                                }
                            }

                            options.ServerCertificate              = certificate;
                            options.ClientCertificateRequired      = false;
                            options.EnabledSslProtocols            = SupportedSslProtocols;
                            options.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
                            await sslStream.AuthenticateAsServerAsync(options, cancellationToken);

#if NETSTANDARD2_1
                            clientStream.Connection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new HttpClientStream(this, clientStream.Connection, sslStream, BufferPool, cancellationToken);
                            sslStream    = null; // clientStream was created, no need to keep SSL stream reference

                            clientStream.DataRead  += (o, args) => connectArgs.OnDecryptedDataSent(args.Buffer, args.Offset, args.Count);
                            clientStream.DataWrite += (o, args) => connectArgs.OnDecryptedDataReceived(args.Buffer, args.Offset, args.Count);
                        }
                        catch (Exception e)
                        {
                            sslStream?.Dispose();

                            var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{connectHostname}' with certificate '{certName}'.", e, connectArgs);
                        }

                        method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                        if (clientStream.IsClosed)
                        {
                            return;
                        }

                        if (method == KnownMethod.Invalid)
                        {
                            sendRawData = true;
                            await tcpConnectionFactory.Release(prefetchConnectionTask, true);

                            prefetchConnectionTask = null;
                        }
                    }
                    else if (clientHelloInfo == null)
                    {
                        method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                        if (clientStream.IsClosed)
                        {
                            return;
                        }
                    }

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (method == KnownMethod.Invalid)
                    {
                        sendRawData = true;
                    }

                    // Hostname is excluded or it is not an HTTPS connect
                    if (sendRawData)
                    {
                        // create new connection to server.
                        // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
                        // this connection should not be HTTPS.
                        var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                         true, null,
                                                                                         true, false, cancellationToken)) !;

                        try
                        {
                            if (isClientHello)
                            {
                                int available = clientStream.Available;
                                if (available > 0)
                                {
                                    // send the buffered data
                                    var data = BufferPool.GetBuffer();

                                    try
                                    {
                                        // clientStream.Available should be at most BufferSize because it is using the same buffer size
                                        int read = await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                        if (read != available)
                                        {
                                            throw new Exception("Internal error.");
                                        }

                                        await connection.Stream.WriteAsync(data, 0, available, true, cancellationToken);
                                    }
                                    finally
                                    {
                                        BufferPool.ReturnBuffer(data);
                                    }
                                }

                                var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);

                                ((ConnectResponse)connectArgs.HttpClient.Response).ServerHelloInfo = serverHelloInfo;
                            }

                            if (!clientStream.IsClosed && !connection.Stream.IsClosed)
                            {
                                await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
                                                        null, null, connectArgs.CancellationTokenSource, ExceptionFunc);
                            }
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                if (connectArgs != null && method == KnownMethod.Pri)
                {
                    // todo
                    string?httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (httpCmd == "PRI * HTTP/2.0")
                    {
                        connectArgs.HttpClient.ConnectRequest !.TunnelType = TunnelType.Http2;

                        // HTTP/2 Connection Preface
                        string?line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != "SM")
                        {
                            throw new Exception($"HTTP/2 Protocol violation. 'SM' expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                         true, SslExtensions.Http2ProtocolAsList,
                                                                                         true, false, cancellationToken)) !;
                        try
                        {
#if NETSTANDARD2_1
                            var connectionPreface = new ReadOnlyMemory <byte>(Http2Helper.ConnectionPreface);
                            await connection.Stream.WriteAsync(connectionPreface, cancellationToken);

                            await Http2Helper.SendHttp2(clientStream, connection.Stream,
                                                        () => new SessionEventArgs(this, endPoint, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
                            {
                                UserData = connectArgs?.UserData
                            },
                                                        async args => { await onBeforeRequest(args); },
                                                        async args => { await onBeforeResponse(args); },
                                                        connectArgs.CancellationTokenSource, clientStream.Connection.Id, ExceptionFunc);
#endif
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }
                    }
                }

                var prefetchTask = prefetchConnectionTask;
                prefetchConnectionTask = null;

                // Now create the request
                await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource, connectArgs, prefetchTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }

                await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);

                clientStream.Dispose();
            }
        }
Exemple #7
0
        private static IEnumerator <IAsyncResult> AsyncStreamCopyImpl(CrcStream source, CrcStream destination, long numBytes, int bufferSize, AsyncCrcStreamCopy.ComputeAndLogCrcMethod computeCrcMethodForCrcLogging, TimeSpan timeout, AsyncIteratorContext <long> context)
        {
            Exception     exception;
            BufferWrapper buffer        = null;
            BufferWrapper bufferWrapper = null;

            try
            {
                Duration     startingNow  = Duration.StartingNow;
                IAsyncResult asyncResult  = null;
                IAsyncResult asyncResult1 = null;
                buffer        = BufferPool.GetBuffer(bufferSize);
                bufferWrapper = BufferPool.GetBuffer(bufferSize);
                long num      = (long)0;
                long?nullable = null;
                context.ResultData = (long)0;
                bool canTimeout = source.CanTimeout;
                bool flag       = destination.CanTimeout;
                while (true)
                {
                    int timeoutInMS = AsyncCrcStreamCopy.TimeSpanToTimeoutInMS(startingNow.Remaining(timeout));
                    if (timeoutInMS == 0)
                    {
                        throw new TimeoutException("The asynchronous stream copy timed out.");
                    }
                    if (canTimeout)
                    {
                        try
                        {
                            source.ReadTimeout = timeoutInMS;
                        }
                        catch (InvalidOperationException invalidOperationException)
                        {
                            canTimeout = false;
                        }
                    }
                    if (flag)
                    {
                        try
                        {
                            destination.WriteTimeout = timeoutInMS;
                        }
                        catch (InvalidOperationException invalidOperationException1)
                        {
                            flag = false;
                        }
                    }
                    if (numBytes > (long)0)
                    {
                        asyncResult = source.BeginRead(buffer.Buffer, 0, (int)Math.Min((long)((int)buffer.Buffer.Length), numBytes), context.GetResumeCallback(), context.GetResumeState("AsyncCrcStreamCopy.source.Read"));
                    }
                    exception = null;
                    if (num > (long)0)
                    {
                        try
                        {
                            asyncResult1 = destination.BeginWrite(bufferWrapper.Buffer, 0, (int)num, nullable, context.GetResumeCallback(), context.GetResumeState("AsyncCrcStreamCopy.dest.Write"));
                        }
                        catch (Exception exception1)
                        {
                            exception = exception1;
                        }
                        if (exception == null)
                        {
                            yield return(asyncResult1);

                            try
                            {
                                destination.EndWrite(asyncResult1);
                            }
                            catch (Exception exception2)
                            {
                                exception = exception2;
                            }
                        }
                    }
                    if (numBytes <= (long)0)
                    {
                        num = (long)0;
                    }
                    else
                    {
                        yield return(asyncResult);

                        num = (long)source.EndRead(out nullable, asyncResult);
                    }
                    if (exception != null)
                    {
                        throw ExceptionCloner.AttemptClone(exception, RethrowableWrapperBehavior.NoWrap);
                    }
                    if (num < (long)0)
                    {
                        break;
                    }
                    numBytes -= num;
                    AsyncIteratorContext <long> resultData = context;
                    resultData.ResultData = resultData.ResultData + num;
                    if (computeCrcMethodForCrcLogging != null)
                    {
                        computeCrcMethodForCrcLogging(buffer.Buffer, 0, (int)num, context.ResultData);
                    }
                    AsyncCrcStreamCopy.Swap <BufferWrapper>(ref buffer, ref bufferWrapper);
                    if (num <= (long)0)
                    {
                        goto Label2;
                    }
                }
                throw new TimeoutException("Reading from the stream resulted in a negative number of bytes read. This typically means an HttpWebRequest was aborted.");
            }
            finally
            {
                if (buffer != null)
                {
                    BufferPool.ReleaseBuffer(buffer);
                }
                if (bufferWrapper != null)
                {
                    BufferPool.ReleaseBuffer(bufferWrapper);
                }
            }
Label2:
            yield break;
            throw new TimeoutException("The asynchronous stream copy timed out.");
            throw ExceptionCloner.AttemptClone(exception, RethrowableWrapperBehavior.NoWrap);
        }
        // process an incoming connection request packet
        private void processConnectionRequest(ByteArrayReaderWriter reader, int size, EndPoint sender)
        {
            log("Got connection request", NetcodeLogLevel.Debug);

            var connectionRequestPacket = new NetcodeConnectionRequestPacket();

            if (!connectionRequestPacket.Read(reader, size - (int)reader.ReadPosition, protocolID))
            {
                log("Failed to read request", NetcodeLogLevel.Debug);
                return;
            }

            // expiration timestamp should be greater than current timestamp
            if (connectionRequestPacket.Expiration <= (ulong)Math.Truncate(time))
            {
                log("Connect token expired", NetcodeLogLevel.Debug);
                connectionRequestPacket.Release();
                return;
            }

            var privateConnectToken = new NetcodePrivateConnectToken();

            if (!privateConnectToken.Read(connectionRequestPacket.ConnectTokenBytes, privateKey, protocolID, connectionRequestPacket.Expiration, connectionRequestPacket.TokenSequenceNum))
            {
                log("Failed to read private token", NetcodeLogLevel.Debug);
                connectionRequestPacket.Release();
                return;
            }

            // if this server's public IP is not in the list of endpoints, packet is not valid
            bool serverAddressInEndpoints = privateConnectToken.ConnectServers.Any(x => x.Endpoint.CompareEndpoint(this.listenEndpoint, this.Port));

            if (!serverAddressInEndpoints)
            {
                log("Server address not listen in token", NetcodeLogLevel.Debug);
                return;
            }

            // if a client from packet source IP / port is already connected, ignore the packet
            if (clientSlots.Any(x => x != null && x.RemoteEndpoint.Equals(sender)))
            {
                log("Client {0} already connected", NetcodeLogLevel.Debug, sender.ToString());
                return;
            }

            // if a client with the same id as the connect token is already connected, ignore the packet
            if (clientSlots.Any(x => x != null && x.ClientID == privateConnectToken.ClientID))
            {
                log("Client ID {0} already connected", NetcodeLogLevel.Debug, privateConnectToken.ClientID);
                return;
            }

            // if the connect token has already been used by a different endpoint, ignore the packet
            // otherwise, add the token hmac and endpoint to the used token history
            // compares the last 16 bytes (token mac)
            byte[] token_mac = BufferPool.GetBuffer(Defines.MAC_SIZE);
            System.Array.Copy(connectionRequestPacket.ConnectTokenBytes, Defines.NETCODE_CONNECT_TOKEN_PRIVATE_BYTES - Defines.MAC_SIZE, token_mac, 0, Defines.MAC_SIZE);
            if (!findOrAddConnectToken(sender, token_mac, time))
            {
                log("Token already used", NetcodeLogLevel.Debug);
                BufferPool.ReturnBuffer(token_mac);
                return;
            }

            BufferPool.ReturnBuffer(token_mac);

            // if we have no slots, we need to respond with a connection denied packet
            var nextSlot = getFreeClientSlot();

            if (nextSlot == -1)
            {
                denyConnection(sender, privateConnectToken.ServerToClientKey);
                log("Server is full, denying connection", NetcodeLogLevel.Info);
                return;
            }

            // add encryption mapping for this endpoint as well as timeout
            // packets received from this endpoint are to be decrypted with the client-to-server key
            // packets sent to this endpoint are to be encrypted with the server-to-client key
            // if no messages are received within timeout from this endpoint, it is disconnected (unless timeout is negative)
            if (!encryptionManager.AddEncryptionMapping(sender,
                                                        privateConnectToken.ServerToClientKey,
                                                        privateConnectToken.ClientToServerKey,
                                                        time,
                                                        time + 30,
                                                        privateConnectToken.TimeoutSeconds,
                                                        0))
            {
                log("Failed to add encryption mapping", NetcodeLogLevel.Error);
                return;
            }

            // finally, send a connection challenge packet
            sendConnectionChallenge(privateConnectToken, sender);
        }
Exemple #9
0
        public static int Decrypt(byte[] ciphertext, int offset, int len, byte[] additionalData, byte[] nonce, byte[] key, byte[] outBuffer)
        {
            lock (mutex) {
                if (cipher == null)
                {
                    cipher = new ChaCha7539Engine();
                }
                else
                {
                    cipher.Reset();
                }

                if (_decryptKey == null)
                {
                    _decryptKey = new KeyParameter(key);
                }
                else
                {
                    _decryptKey.Reset();
                    _decryptKey.SetKey(key);
                }

                if (_temp_Params == null)
                {
                    _temp_Params = new ParametersWithIV(_decryptKey, nonce);
                }
                else
                {
                    _temp_Params.Reset();
                    _temp_Params.Set(_decryptKey, nonce);
                }

                cipher.Init(false, _temp_Params);

                byte[]       firstBlock = BufferPool.GetBuffer(64);
                KeyParameter macKey     = GenerateRecordMacKey(cipher, firstBlock);

                int plaintextLength = len - 16;

                byte[] calculatedMac = BufferPool.GetBuffer(16);
                CalculateRecordMac(macKey, additionalData, ciphertext, offset, plaintextLength, calculatedMac);

                byte[] receivedMac = BufferPool.GetBuffer(16);
                Array.Copy(ciphertext, offset + plaintextLength, receivedMac, 0, receivedMac.Length);

                if (!Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac))
                {
                    BufferPool.ReturnBuffer(calculatedMac);
                    BufferPool.ReturnBuffer(receivedMac);
                    BufferPool.ReturnBuffer(firstBlock);

                    throw new TlsFatalAlert(AlertDescription.bad_record_mac);
                }

                BufferPool.ReturnBuffer(calculatedMac);
                BufferPool.ReturnBuffer(receivedMac);
                BufferPool.ReturnBuffer(firstBlock);

                cipher.ProcessBytes(ciphertext, offset, plaintextLength, outBuffer, 0);
                return(plaintextLength);
            }
        }
Exemple #10
0
        ///////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ms">目标内存流</param>
        public luaProtoWriter(MemoryStream ms)
        {
            _stream = ms;
            _buff   = BufferPool.GetBuffer();
        }
Exemple #11
0
 public void Set(ICipherParameters parameters, byte[] iv)
 {
     this.parameters = parameters;
     this.iv         = BufferPool.GetBuffer(iv.Length);
     Array.Copy(iv, 0, this.iv, 0, this.iv.Length);
 }
        /// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferSize);

            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);

            try
            {
                var clientHelloInfo = await SslTools.PeekClientHello(clientStream, cancellationToken);

                bool   isHttps       = clientHelloInfo != null;
                string httpsHostName = null;

                if (isHttps)
                {
                    httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;

                    var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource)
                    {
                        SniHostName = httpsHostName
                    };

                    await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (endPoint.DecryptSsl && args.DecryptSsl)
                    {
                        SslStream sslStream = null;

                        try
                        {
                            sslStream = new SslStream(clientStream);

                            string certName    = HttpHelper.GetWildCardDomainName(httpsHostName);
                            var    certificate = await CertificateManager.CreateCertificateAsync(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new CustomBufferedStream(sslStream, BufferSize);

                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
                        }
                        catch (Exception e)
                        {
                            sslStream?.Dispose();
                            throw new ProxyConnectException(
                                      $"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e, null);
                        }
                    }
                    else
                    {
                        // create new connection
                        var connection = new TcpClient(UpStreamEndPoint);
                        await connection.ConnectAsync(httpsHostName, endPoint.Port);

                        connection.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
                        connection.SendTimeout    = ConnectionTimeOutSeconds * 1000;

                        using (connection)
                        {
                            var serverStream = connection.GetStream();

                            int available = clientStream.Available;
                            if (available > 0)
                            {
                                // send the buffered data
                                var data = BufferPool.GetBuffer(BufferSize);

                                try
                                {
                                    // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
                                    await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                    await serverStream.WriteAsync(data, 0, available, cancellationToken);

                                    await serverStream.FlushAsync(cancellationToken);
                                }
                                finally
                                {
                                    BufferPool.ReturnBuffer(data);
                                }
                            }

                            ////var serverHelloInfo = await SslTools.PeekServerHello(serverStream);

                            await TcpHelper.SendRaw(clientStream, serverStream, BufferSize,
                                                    null, null, cancellationTokenSource, ExceptionFunc);
                        }
                    }
                }

                // HTTPS server created - we can now decrypt the client's traffic
                // Now create the request
                await HandleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
                                               cancellationTokenSource, isHttps?httpsHostName : null, null);
            }
            catch (ProxyException e)
            {
                OnException(clientStream, e);
            }
            catch (IOException e)
            {
                OnException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                OnException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                OnException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                clientStream.Dispose();
                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            }
        }
Exemple #13
0
        private static void SpinFileReaders(BlockingCollection <ReadFileWork> readFileWork, BlockingCollection <CrcComputationWork> crcComputationWork)
        {
            // These I/O Actors allows us to parallelize all the I/O work. On slow / cloud machines, that allows the I/O subsystem to get
            // bulk read speed from the underlying systems.
            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    while (!readFileWork.IsCompleted)
                    {
                        ReadFileWork work = null;

                        if (!readFileWork.TryTake(out work, 20))
                        {
                            continue;
                        }

                        var fileInfo = new FileInfo(work.FileName);
                        // some buffers can be very large, we will use consistent 16KB buffers
                        // and if the file is too large, we will use multiple 16KB buffers.
                        // For a file that is 260 KB (jQuery 1.8.3 is 261.46 KB) in size, that means we will use 272 KB only,
                        // versus the 512KB we would use if we used a power of two approach.
                        // So we'll waste only 12KB instead of 252KB
                        var buffers = new List <byte[]>();
                        try
                        {
                            const int bufferSize = 16 * 1024;
                            using (var fs = fileInfo.OpenRead())
                            {
                                for (int j = 0; j < fileInfo.Length; j += bufferSize)
                                {
                                    var buffer = BufferPool.GetBuffer(bufferSize);
                                    buffers.Add(buffer);
                                    // we can't call fs.Read here, it will allocate a 4KB buffer that will then be discarded.
                                    // so we use the native ReadFile method and read directly to our pooled buffer
                                    int read;
                                    if (ReadFile(fs.SafeFileHandle, buffer, buffer.Length, out read, IntPtr.Zero) == false)
                                    {
                                        throw new Win32Exception();
                                    }
                                }
                            }
                        }
                        catch (Exception)
                        {
                            // If there has ben any error in reading from the file or opening it, we've have
                            // to make sure that we won't forget about this buffer. Even with the error that just
                            // happened, we can still make use of it.
                            foreach (var buffer in buffers)
                            {
                                BufferPool.ReturnBuffer(buffer);
                            }
                            throw;
                        }
                        crcComputationWork.Add(new CrcComputationWork
                        {
                            Length   = (int)fileInfo.Length,
                            Buffers  = buffers,                           // the SpinCrcComputations will be returning the bufer to the ppol
                            FileName = work.FileName
                        });
                    }
                    _countDown.Signal();
                });
            }
        }
        public void ReceivePacket(byte[] packetData, int bufferLength)
        {
            if (bufferLength > config.MaxPacketSize)
            {
                throw new ArgumentOutOfRangeException("Packet is larger than max packet size");
            }

            byte prefixByte = packetData[0];

            if ((prefixByte & 1) == 0)
            {
                // regular packet

                ushort sequence;
                ushort ack;
                uint   ackBits;

                byte channelID;

                int  packetHeaderBytes = PacketIO.ReadPacketHeader(packetData, 0, bufferLength, out channelID, out sequence, out ack, out ackBits);
                bool isStale           = !receivedPackets.TestInsert(sequence);

                if (!isStale && (prefixByte & 0x80) == 0)
                {
                    ByteBuffer tempBuffer = ObjPool <ByteBuffer> .Get();

                    tempBuffer.SetSize(bufferLength - packetHeaderBytes);
                    tempBuffer.BufferCopy(packetData, packetHeaderBytes, 0, tempBuffer.Length);

                    // process packet
                    config.ProcessPacketCallback(sequence, tempBuffer.InternalBuffer, tempBuffer.Length);

                    // add to received buffer
                    ReceivedPacketData receivedPacketData = receivedPackets.Insert(sequence);
                    receivedPacketData.time        = this.time;
                    receivedPacketData.packetBytes = (uint)(config.PacketHeaderSize + bufferLength);

                    ObjPool <ByteBuffer> .Return(tempBuffer);
                }

                if (!isStale || (prefixByte & 0x80) != 0)
                {
                    for (int i = 0; i < 32; i++)
                    {
                        if ((ackBits & 1) != 0)
                        {
                            ushort         ack_sequence   = (ushort)(ack - i);
                            SentPacketData sentPacketData = sentPackets.Find(ack_sequence);

                            if (sentPacketData != null && !sentPacketData.acked)
                            {
                                sentPacketData.acked = true;

                                if (config.AckPacketCallback != null)
                                {
                                    config.AckPacketCallback(ack_sequence);
                                }

                                float rtt = (float)(this.time - sentPacketData.time) * 1000.0f;
                                if ((this.rtt == 0f && rtt > 0f) || Math.Abs(this.rtt - rtt) < 0.00001f)
                                {
                                    this.rtt = rtt;
                                }
                                else
                                {
                                    this.rtt += (rtt - this.rtt) * config.RTTSmoothFactor;
                                }
                            }
                        }

                        ackBits >>= 1;
                    }
                }
            }
            else
            {
                // fragment packet

                int fragmentID;
                int numFragments;
                int fragmentBytes;

                ushort sequence;
                ushort ack;
                uint   ackBits;

                byte fragmentChannelID;

                int fragmentHeaderBytes = PacketIO.ReadFragmentHeader(packetData, 0, bufferLength, config.MaxFragments, config.FragmentSize,
                                                                      out fragmentID, out numFragments, out fragmentBytes, out sequence, out ack, out ackBits, out fragmentChannelID);

                FragmentReassemblyData reassemblyData = fragmentReassembly.Find(sequence);
                if (reassemblyData == null)
                {
                    reassemblyData = fragmentReassembly.Insert(sequence);

                    // failed to insert into buffer (stale)
                    if (reassemblyData == null)
                    {
                        return;
                    }

                    reassemblyData.Sequence             = sequence;
                    reassemblyData.Ack                  = 0;
                    reassemblyData.AckBits              = 0;
                    reassemblyData.NumFragmentsReceived = 0;
                    reassemblyData.NumFragmentsTotal    = numFragments;
                    reassemblyData.PacketBytes          = 0;
                    Array.Clear(reassemblyData.FragmentReceived, 0, reassemblyData.FragmentReceived.Length);
                }

                if (numFragments != reassemblyData.NumFragmentsTotal)
                {
                    return;
                }

                if (reassemblyData.FragmentReceived[fragmentID])
                {
                    return;
                }

                reassemblyData.NumFragmentsReceived++;
                reassemblyData.FragmentReceived[fragmentID] = true;

                byte[] tempFragmentData = BufferPool.GetBuffer(2048);
                Buffer.BlockCopy(packetData, fragmentHeaderBytes, tempFragmentData, 0, bufferLength - fragmentHeaderBytes);
                reassemblyData.StoreFragmentData(fragmentChannelID, sequence, ack, ackBits, fragmentID, config.FragmentSize, tempFragmentData, bufferLength - fragmentHeaderBytes);
                BufferPool.ReturnBuffer(tempFragmentData);

                if (reassemblyData.NumFragmentsReceived == reassemblyData.NumFragmentsTotal)
                {
                    // grab internal buffer and pass it to ReceivePacket. Internal buffer will be packet marked as normal packet, so it will go through normal packet path
                    this.ReceivePacket(reassemblyData.PacketDataBuffer.InternalBuffer, reassemblyData.PacketDataBuffer.Length);
                    reassemblyData.PacketDataBuffer.SetSize(0);
                    fragmentReassembly.Remove(sequence);
                }
            }
        }
Exemple #15
0
        internal static byte[] Serialize(GsWriteStream writeStream)
        {
            var(bufferSize, objectInfos) = GetInfos(writeStream);

            var packetBuffer = BufferPool.GetBuffer(bufferSize);

            using (var packetWriter = ByteArrayReaderWriter.Get(packetBuffer))
            {
                packetWriter.Write((byte)objectInfos.Count);

                foreach (var objectInfo in objectInfos)
                {
                    packetWriter.Write((byte)objectInfo.Type);
                    switch (objectInfo.Type)
                    {
                    case Types.Byte:
                        packetWriter.Write((byte)objectInfo.Src);
                        break;

                    case Types.Char:
                        packetWriter.Write((char)objectInfo.Src);
                        break;

                    case Types.Double:
                        packetWriter.Write(BitConverter.GetBytes((double)objectInfo.Src));
                        break;

                    case Types.Float:
                        packetWriter.Write(BitConverter.GetBytes((float)objectInfo.Src));
                        break;

                    case Types.Int:
                        packetWriter.Write((int)objectInfo.Src);
                        break;

                    case Types.Long:
                        packetWriter.Write((long)objectInfo.Src);
                        break;

                    case Types.Short:
                        packetWriter.Write((short)objectInfo.Src);
                        break;

                    case Types.Uint:
                        packetWriter.Write((uint)objectInfo.Src);
                        break;

                    case Types.Ushort:
                        packetWriter.Write((ushort)objectInfo.Src);
                        break;

                    case Types.Ulong:
                        packetWriter.Write((ulong)objectInfo.Src);
                        break;

                    case Types.Bool:
                        byte data = 0x0;
                        if ((bool)objectInfo.Src)
                        {
                            data = 0x1;
                        }
                        packetWriter.Write(data);
                        break;

                    case Types.CustomObject:
                        var bufferDataObject = (byte[])objectInfo.Src;
                        packetWriter.Write((int)objectInfo.Extra);
                        packetWriter.Write((ushort)bufferDataObject.Length);
                        packetWriter.Write(bufferDataObject);
                        break;

                    case Types.String:
                    case Types.ByteArray:
                    case Types.CharArray:
                    case Types.DoubleArray:
                    case Types.FloatArray:
                    case Types.IntArray:
                    case Types.LongArray:
                    case Types.StringArray:
                        var bArray = (byte[])objectInfo.Src;
                        packetWriter.Write((ushort)bArray.Length);
                        packetWriter.Write(bArray);
                        break;

                    case Types.Null: break;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            }

            return(packetBuffer);
        }
        /// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream       = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);

            SslStream sslStream = null;

            try
            {
                var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                bool   isHttps       = clientHelloInfo != null;
                string httpsHostName = null;

                if (isHttps)
                {
                    httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;

                    var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource)
                    {
                        SniHostName = httpsHostName
                    };

                    await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (endPoint.DecryptSsl && args.DecryptSsl)
                    {
                        //do client authentication using certificate
                        X509Certificate2 certificate = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, true);

                            string certName = HttpHelper.GetWildCardDomainName(httpsHostName);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new CustomBufferedStream(sslStream, BufferPool, BufferSize);

                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
                        }
                        catch (Exception e)
                        {
                            var certname = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            var session  = new SessionEventArgs(this, endPoint, cancellationTokenSource)
                            {
                                ProxyClient = { Connection = clientConnection },
                                HttpClient  = { ConnectRequest = null }
                            };
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{httpsHostName}' with certificate '{certname}'.", e, session);
                        }
                    }
                    else
                    {
                        var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
                                                                                        httpVersion : null, isHttps : false, applicationProtocols : null,
                                                                                        isConnect : true, proxyServer : this, session : null, upStreamEndPoint : UpStreamEndPoint,
                                                                                        externalProxy : UpStreamHttpsProxy, noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            CustomBufferedStream serverStream = null;
                            int available = clientStream.Available;

                            if (available > 0)
                            {
                                // send the buffered data
                                var data = BufferPool.GetBuffer(BufferSize);
                                try
                                {
                                    // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
                                    await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                    serverStream = connection.Stream;
                                    await serverStream.WriteAsync(data, 0, available, cancellationToken);

                                    await serverStream.FlushAsync(cancellationToken);
                                }
                                finally
                                {
                                    BufferPool.ReturnBuffer(data);
                                }
                            }

                            await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize,
                                                    null, null, cancellationTokenSource, ExceptionFunc);
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }
                // HTTPS server created - we can now decrypt the client's traffic
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
                                               cancellationTokenSource, isHttps?httpsHostName : null, null, null);
            }
            catch (ProxyException e)
            {
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                sslStream?.Dispose();
                clientStream.Dispose();

                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            }
        }
Exemple #17
0
        /// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private async Task handleClient(SocksProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var stream = clientConnection.GetStream();
            var buffer = BufferPool.GetBuffer();
            int port   = 0;

            try
            {
                int read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);

                if (read < 3)
                {
                    return;
                }

                if (buffer[0] == 4)
                {
                    if (read < 9 || buffer[1] != 1)
                    {
                        // not a connect request
                        return;
                    }

                    port = (buffer[2] << 8) + buffer[3];

                    buffer[0] = 0;
                    buffer[1] = 90; // request granted
                    await stream.WriteAsync(buffer, 0, 8, cancellationToken);
                }
                else if (buffer[0] == 5)
                {
                    int authenticationMethodCount = buffer[1];
                    if (read < authenticationMethodCount + 2)
                    {
                        return;
                    }

                    int acceptedMethod = 255;
                    for (int i = 0; i < authenticationMethodCount; i++)
                    {
                        int method = buffer[i + 2];
                        if (method == 0 && ProxyBasicAuthenticateFunc == null)
                        {
                            acceptedMethod = 0;
                            break;
                        }

                        if (method == 2)
                        {
                            acceptedMethod = 2;
                            break;
                        }
                    }

                    buffer[1] = (byte)acceptedMethod;
                    await stream.WriteAsync(buffer, 0, 2, cancellationToken);

                    if (acceptedMethod == 255)
                    {
                        // no acceptable method
                        return;
                    }

                    if (acceptedMethod == 2)
                    {
                        read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);

                        if (read < 3 || buffer[0] != 1)
                        {
                            // authentication version should be 1
                            return;
                        }

                        int userNameLength = buffer[1];
                        if (read < 3 + userNameLength)
                        {
                            return;
                        }

                        string userName = Encoding.ASCII.GetString(buffer, 2, userNameLength);

                        int passwordLength = buffer[2 + userNameLength];
                        if (read < 3 + userNameLength + passwordLength)
                        {
                            return;
                        }

                        string password = Encoding.ASCII.GetString(buffer, 3 + userNameLength, passwordLength);
                        bool   success  = true;
                        if (ProxyBasicAuthenticateFunc != null)
                        {
                            success = await ProxyBasicAuthenticateFunc.Invoke(null, userName, password);
                        }

                        buffer[1] = success ? (byte)0 : (byte)1;
                        await stream.WriteAsync(buffer, 0, 2, cancellationToken);

                        if (!success)
                        {
                            return;
                        }
                    }

                    read = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);

                    if (read < 10 || buffer[1] != 1)
                    {
                        return;
                    }

                    int portIdx;
                    switch (buffer[3])
                    {
                    case 1:
                        // IPv4
                        portIdx = 8;
                        break;

                    case 3:
                        // Domainname
                        portIdx = buffer[4] + 5;

#if DEBUG
                        var    hostname    = new ByteString(buffer.AsMemory(5, buffer[4]));
                        string hostnameStr = hostname.GetString();
#endif
                        break;

                    case 4:
                        // IPv6
                        portIdx = 20;
                        break;

                    default:
                        return;
                    }

                    if (read < portIdx + 2)
                    {
                        return;
                    }

                    port      = (buffer[portIdx] << 8) + buffer[portIdx + 1];
                    buffer[1] = 0; // succeeded
                    await stream.WriteAsync(buffer, 0, read, cancellationToken);
                }
                else
                {
                    return;
                }
            }
            finally
            {
                BufferPool.ReturnBuffer(buffer);
            }

            await handleClient(endPoint, clientConnection, port, cancellationTokenSource, cancellationToken);
        }
Exemple #18
0
        public ushort SendPacket(byte[] packetData, int length, byte channelID)
        {
            if (length > config.MaxPacketSize)
            {
                throw new ArgumentOutOfRangeException("Packet is too large to send, max packet size is " + config.MaxPacketSize + " bytes");
            }
            ushort num = sequence++;
            ushort ack;
            uint   ackBits;

            lock (receivedPackets)
            {
                receivedPackets.GenerateAckBits(out ack, out ackBits);
            }
            SentPacketData sentPacketData = sentPackets.Insert(num);

            sentPacketData.time        = time;
            sentPacketData.packetBytes = (uint)(config.PacketHeaderSize + length);
            sentPacketData.acked       = false;
            if (length <= config.FragmentThreshold)
            {
                byte[] buffer = BufferPool.GetBuffer(2048);
                int    num2   = PacketIO.WritePacketHeader(buffer, channelID, num, ack, ackBits);
                int    arg    = length + num2;
                Buffer.BlockCopy(packetData, 0, buffer, num2, length);
                config.TransmitPacketCallback(buffer, arg);
                BufferPool.ReturnBuffer(buffer);
            }
            else
            {
                byte[] buffer2 = BufferPool.GetBuffer(10);
                int    num3    = 0;
                try
                {
                    num3 = PacketIO.WritePacketHeader(buffer2, channelID, num, ack, ackBits);
                }
                catch
                {
                    throw;
                }
                int    num4    = length / config.FragmentSize + ((length % config.FragmentSize != 0) ? 1 : 0);
                byte[] buffer3 = BufferPool.GetBuffer(2048);
                int    num5    = 0;
                byte   b       = 1;
                b = (byte)(b | (byte)((channelID & 3) << 6));
                for (int i = 0; i < num4; i++)
                {
                    using (ByteArrayReaderWriter byteArrayReaderWriter = ByteArrayReaderWriter.Get(buffer3))
                    {
                        byteArrayReaderWriter.Write(b);
                        byteArrayReaderWriter.Write(channelID);
                        byteArrayReaderWriter.Write(num);
                        byteArrayReaderWriter.Write((byte)i);
                        byteArrayReaderWriter.Write((byte)(num4 - 1));
                        if (i == 0)
                        {
                            byteArrayReaderWriter.WriteBuffer(buffer2, num3);
                        }
                        int num6 = config.FragmentSize;
                        if (num5 + num6 > length)
                        {
                            num6 = length - num5;
                        }
                        for (int j = 0; j < num6; j++)
                        {
                            byteArrayReaderWriter.Write(packetData[num5++]);
                        }
                        int arg2 = (int)byteArrayReaderWriter.WritePosition;
                        config.TransmitPacketCallback(buffer3, arg2);
                    }
                }
                BufferPool.ReturnBuffer(buffer2);
                BufferPool.ReturnBuffer(buffer3);
            }
            return(num);
        }
        internal byte[] Serialize()
        {
            byte  havePayload = 0x0, haveExtra = 0x0, haveSender = 0x0, haveReceiver = 0x0;
            short prefixLen = 4 * sizeof(byte);


            if (Payload != null)
            {
                havePayload = 0x1;
                _payloadLen = Payload.Length;
                prefixLen  += sizeof(ushort);
            }

            if (ExtraData != null)
            {
                haveExtra  = 0x1;
                _extraLen  = ExtraData.Length;
                prefixLen += sizeof(ushort);
            }


            if (SenderId != null)
            {
                haveSender = 0x1;
                _senderLen = SenderId.Length;
                prefixLen += sizeof(byte);
            }


            if (ReceiverId != null)
            {
                haveReceiver = 0x1;
                _receiverLen = ReceiverId.Length;
                prefixLen   += sizeof(byte);
            }


            var packetBuffer = BufferPool.GetBuffer(BufferSize(prefixLen));

            using (var packetWriter = ByteArrayReaderWriter.Get(packetBuffer))
            {
                // header Segment
                packetWriter.Write(haveSender);
                packetWriter.Write(haveReceiver);
                packetWriter.Write(havePayload);
                packetWriter.Write(haveExtra);

                if (haveSender == 0x1)
                {
                    packetWriter.Write((byte)_senderLen);
                }
                if (haveReceiver == 0x1)
                {
                    packetWriter.Write((byte)_receiverLen);
                }
                if (havePayload == 0x1)
                {
                    packetWriter.Write((ushort)_payloadLen);
                }
                if (haveExtra == 0x1)
                {
                    packetWriter.Write((ushort)_extraLen);
                }

                // data Segment
                if (haveSender == 0x1)
                {
                    packetWriter.Write(ConvertToBytes(SenderId));
                }
                if (haveReceiver == 0x1)
                {
                    packetWriter.Write(ConvertToBytes(ReceiverId));
                }
                if (havePayload == 0x1)
                {
                    packetWriter.Write(Payload);
                }
                if (haveExtra == 0x1)
                {
                    packetWriter.Write(ExtraData);
                }
            }

            return(packetBuffer);
        }
        /// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream = new HttpClientStream(clientConnection, clientConnection.GetStream(), BufferPool);

            SslStream?sslStream = null;

            try
            {
                var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                if (clientHelloInfo != null)
                {
                    var httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;

                    var args = new BeforeSslAuthenticateEventArgs(clientConnection, cancellationTokenSource, httpsHostName);

                    await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (endPoint.DecryptSsl && args.DecryptSsl)
                    {
                        clientStream.Connection.SslProtocol = clientHelloInfo.SslProtocol;

                        // do client authentication using certificate
                        X509Certificate2?certificate = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, false);

                            string certName = HttpHelper.GetWildCardDomainName(httpsHostName);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new HttpClientStream(clientStream.Connection, sslStream, BufferPool);
                            sslStream    = null; // clientStream was created, no need to keep SSL stream reference
                        }
                        catch (Exception e)
                        {
                            var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            var session  = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource);
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{httpsHostName}' with certificate '{certName}'.", e, session);
                        }
                    }
                    else
                    {
                        var sessionArgs = new SessionEventArgs(this, endPoint, clientStream, null, cancellationTokenSource);
                        var connection  = await tcpConnectionFactory.GetServerConnection(this, httpsHostName, endPoint.Port,
                                                                                         HttpHeader.VersionUnknown, false, null,
                                                                                         true, sessionArgs, UpStreamEndPoint,
                                                                                         UpStreamHttpsProxy, true, cancellationToken);

                        try
                        {
                            int available = clientStream.Available;

                            if (available > 0)
                            {
                                // send the buffered data
                                var data = BufferPool.GetBuffer();
                                try
                                {
                                    // clientStream.Available should be at most BufferSize because it is using the same buffer size
                                    await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                    await connection.Stream.WriteAsync(data, 0, available, true, cancellationToken);
                                }
                                finally
                                {
                                    BufferPool.ReturnBuffer(data);
                                }
                            }

                            if (!clientStream.IsClosed && !connection.Stream.IsClosed)
                            {
                                await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
                                                        null, null, cancellationTokenSource, ExceptionFunc);
                            }
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                // HTTPS server created - we can now decrypt the client's traffic
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource);
            }
            catch (ProxyException e)
            {
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                sslStream?.Dispose();
                clientStream.Dispose();
            }
        }
        /// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream       = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);

            Task <TcpServerConnection> prefetchConnectionTask = null;
            bool closeServerConnection = false;
            bool calledRequestHandler  = false;

            try
            {
                var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                bool   isHttps       = clientHelloInfo != null;
                string httpsHostName = null;

                if (isHttps)
                {
                    httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;

                    var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource)
                    {
                        SniHostName = httpsHostName
                    };

                    await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (endPoint.DecryptSsl && args.DecryptSsl)
                    {
                        //don't pass cancellation token here
                        //it could cause floating server connections when client exits
                        prefetchConnectionTask = tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
                                                                                null, true, null, false, this,
                                                                                UpStreamEndPoint, UpStreamHttpsProxy, false, CancellationToken.None);

                        SslStream sslStream = null;

                        //do client authentication using fake certificate
                        try
                        {
                            sslStream = new SslStream(clientStream);

                            string certName    = HttpHelper.GetWildCardDomainName(httpsHostName);
                            var    certificate = await CertificateManager.CreateCertificateAsync(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new CustomBufferedStream(sslStream, BufferPool, BufferSize);

                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
                        }
                        catch (Exception e)
                        {
                            sslStream?.Dispose();
                            throw new ProxyConnectException(
                                      $"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e, null);
                        }
                    }
                    else
                    {
                        var connection = await tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
                                                                              null, false, null,
                                                                              true, this, UpStreamEndPoint, UpStreamHttpsProxy, true, cancellationToken);

                        try
                        {
                            CustomBufferedStream serverStream = null;
                            int available = clientStream.Available;

                            if (available > 0)
                            {
                                // send the buffered data
                                var data = BufferPool.GetBuffer(BufferSize);
                                try
                                {
                                    // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
                                    await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                    serverStream = connection.Stream;
                                    await serverStream.WriteAsync(data, 0, available, cancellationToken);

                                    await serverStream.FlushAsync(cancellationToken);
                                }
                                finally
                                {
                                    BufferPool.ReturnBuffer(data);
                                }
                            }

                            await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize,
                                                    null, null, cancellationTokenSource, ExceptionFunc);
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }
                calledRequestHandler = true;
                // HTTPS server created - we can now decrypt the client's traffic
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
                                               cancellationTokenSource, isHttps?httpsHostName : null, null, prefetchConnectionTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!calledRequestHandler &&
                    prefetchConnectionTask != null)
                {
                    var connection = await prefetchConnectionTask;
                    await tcpConnectionFactory.Release(connection, closeServerConnection);
                }

                clientStream.Dispose();

                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            }
        }
Exemple #22
0
        /// <summary>
        ///     This is called when client is aware of proxy
        ///     So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint">The explicit endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns>The task.</returns>
        private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(this.cancellationTokenSource.Token);
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream       = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);

            Task <TcpServerConnection> prefetchConnectionTask = null;
            bool closeServerConnection = false;
            bool calledRequestHandler  = false;

            SslStream sslStream = null;

            try
            {
                string connectHostname = null;
                TunnelConnectSessionEventArgs connectArgs = null;


                // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (await HttpHelper.IsConnectMethod(clientStream) == 1)
                {
                    // read the first line HTTP command
                    string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (string.IsNullOrEmpty(httpCmd))
                    {
                        return;
                    }

                    Request.ParseRequestLine(httpCmd, out string _, out string httpUrl, out var version);

                    var httpRemoteUri = new Uri("http://" + httpUrl);
                    connectHostname = httpRemoteUri.Host;

                    var connectRequest = new ConnectRequest
                    {
                        RequestUri  = httpRemoteUri,
                        OriginalUrl = httpUrl,
                        HttpVersion = version
                    };

                    await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);

                    connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
                                                                    cancellationTokenSource);
                    connectArgs.ProxyClient.Connection   = clientConnection;
                    connectArgs.ProxyClient.ClientStream = clientStream;

                    await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);

                    // filter out excluded host names
                    bool decryptSsl = endPoint.DecryptSsl && connectArgs.DecryptSsl;

                    if (connectArgs.DenyConnect)
                    {
                        if (connectArgs.HttpClient.Response.StatusCode == 0)
                        {
                            connectArgs.HttpClient.Response = new Response
                            {
                                HttpVersion       = HttpHeader.Version11,
                                StatusCode        = (int)HttpStatusCode.Forbidden,
                                StatusDescription = "Forbidden"
                            };
                        }

                        // send the response
                        await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
                                                                    cancellationToken : cancellationToken);

                        return;
                    }

                    if (await checkAuthorization(connectArgs) == false)
                    {
                        await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc);

                        // send the response
                        await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
                                                                    cancellationToken : cancellationToken);

                        return;
                    }

                    // write back successful CONNECT response
                    var response = ConnectResponse.CreateSuccessfulConnectResponse(version);

                    // Set ContentLength explicitly to properly handle HTTP 1.0
                    response.ContentLength = 0;
                    response.Headers.FixProxyHeaders();
                    connectArgs.HttpClient.Response = response;

                    await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                    bool isClientHello = clientHelloInfo != null;
                    if (isClientHello)
                    {
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);

                    if (decryptSsl && isClientHello)
                    {
                        connectRequest.RequestUri = new Uri("https://" + httpUrl);

                        bool http2Supported = false;

                        var alpn = clientHelloInfo.GetAlpn();
                        if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
                        {
                            // test server HTTP/2 support
                            // todo: this is a hack, because Titanium does not support HTTP protocol changing currently
                            var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                            isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                            noCache : true, cancellationToken : cancellationToken);

                            http2Supported = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;

                            //release connection back to pool instead of closing when connection pool is enabled.
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        if (EnableTcpServerConnectionPrefetch)
                        {
                            IPAddress[] ipAddresses = null;
                            try
                            {
                                //make sure the host can be resolved before creating the prefetch task
                                ipAddresses = await Dns.GetHostAddressesAsync(connectArgs.HttpClient.Request.RequestUri.Host);
                            }
                            catch (SocketException) { }

                            if (ipAddresses != null && ipAddresses.Length > 0)
                            {
                                //don't pass cancellation token here
                                //it could cause floating server connections when client exits
                                prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                                  isConnect: true, applicationProtocols: null, noCache: false,
                                                                                                  cancellationToken: CancellationToken.None);
                            }
                        }

                        X509Certificate2 certificate = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, true);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            var options = new SslServerAuthenticationOptions();
                            if (http2Supported)
                            {
                                options.ApplicationProtocols = clientHelloInfo.GetAlpn();
                                if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
                                {
                                    options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
                                }
                            }

                            options.ServerCertificate              = certificate;
                            options.ClientCertificateRequired      = false;
                            options.EnabledSslProtocols            = SupportedSslProtocols;
                            options.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
                            await sslStream.AuthenticateAsServerAsync(options, cancellationToken);

#if NETCOREAPP2_1
                            clientConnection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream       = new CustomBufferedStream(sslStream, BufferPool, BufferSize);
                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
                        }
                        catch (Exception e)
                        {
                            var certname = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{connectHostname}' with certificate '{certname}'.", e, connectArgs);
                        }

                        if (await HttpHelper.IsConnectMethod(clientStream) == -1)
                        {
                            decryptSsl = false;
                        }

                        if (!decryptSsl)
                        {
                            await tcpConnectionFactory.Release(prefetchConnectionTask, true);

                            prefetchConnectionTask = null;
                        }
                    }

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    // Hostname is excluded or it is not an HTTPS connect
                    if (!decryptSsl || !isClientHello)
                    {
                        // create new connection to server.
                        // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
                        // this connection should not be HTTPS.
                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                        noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            if (isClientHello)
                            {
                                int available = clientStream.Available;
                                if (available > 0)
                                {
                                    // send the buffered data
                                    var data = BufferPool.GetBuffer(BufferSize);

                                    try
                                    {
                                        await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                        // clientStream.Available should be at most BufferSize because it is using the same buffer size
                                        await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
                                    }
                                    finally
                                    {
                                        BufferPool.ReturnBuffer(data);
                                    }
                                }

                                var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);

                                ((ConnectResponse)connectArgs.HttpClient.Response).ServerHelloInfo = serverHelloInfo;
                            }

                            await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
                                                    (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
                                                    (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
                                                    connectArgs.CancellationTokenSource, ExceptionFunc);
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                if (connectArgs != null && await HttpHelper.IsPriMethod(clientStream) == 1)
                {
                    // todo
                    string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (httpCmd == "PRI * HTTP/2.0")
                    {
                        // HTTP/2 Connection Preface
                        string line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != "SM")
                        {
                            throw new Exception($"HTTP/2 Protocol violation. 'SM' expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                        noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);

                            await connection.StreamWriter.WriteLineAsync(cancellationToken);

                            await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);

                            await connection.StreamWriter.WriteLineAsync(cancellationToken);

#if NETCOREAPP2_1
                            await Http2Helper.SendHttp2(clientStream, connection.Stream, BufferSize,
                                                        (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
                                                        (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
                                                        connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }
                    }
                }

                calledRequestHandler = true;
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
                                               cancellationTokenSource, connectHostname, connectArgs, prefetchConnectionTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!calledRequestHandler)
                {
                    await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);
                }

                sslStream?.Dispose();
                clientStream.Dispose();

                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            }
        }
        /// <summary>
        /// This is called when client is aware of proxy
        /// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint"></param>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
        {
            var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);

            var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);

            try
            {
                string connectHostname = null;

                ConnectRequest connectRequest = null;

                //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (await HttpHelper.IsConnectMethod(clientStream) == 1)
                {
                    //read the first line HTTP command
                    string httpCmd = await clientStreamReader.ReadLineAsync();

                    if (string.IsNullOrEmpty(httpCmd))
                    {
                        return;
                    }

                    Request.ParseRequestLine(httpCmd, out string _, out string httpUrl, out var version);

                    var httpRemoteUri = new Uri("http://" + httpUrl);
                    connectHostname = httpRemoteUri.Host;

                    //filter out excluded host names
                    bool excluded = false;

                    if (endPoint.ExcludedHttpsHostNameRegex != null)
                    {
                        excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname));
                    }

                    if (endPoint.IncludedHttpsHostNameRegex != null)
                    {
                        excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname));
                    }

                    if (endPoint.BeforeTunnelConnect != null)
                    {
                        excluded = await endPoint.BeforeTunnelConnect(connectHostname);
                    }

                    connectRequest = new ConnectRequest
                    {
                        RequestUri  = httpRemoteUri,
                        OriginalUrl = httpUrl,
                        HttpVersion = version,
                    };

                    await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.Headers);

                    var connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint, connectRequest, ExceptionFunc);
                    connectArgs.ProxyClient.TcpClient    = tcpClient;
                    connectArgs.ProxyClient.ClientStream = clientStream;

                    await endPoint.InvokeTunnectConnectRequest(this, connectArgs, ExceptionFunc);


                    if (await CheckAuthorization(clientStreamWriter, connectArgs) == false)
                    {
                        await endPoint.InvokeTunnectConnectResponse(this, connectArgs, ExceptionFunc);

                        return;
                    }

                    //write back successfull CONNECT response
                    var response = ConnectResponse.CreateSuccessfullConnectResponse(version);
                    response.Headers.FixProxyHeaders();
                    connectArgs.WebSession.Response = response;

                    await clientStreamWriter.WriteResponseAsync(response);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream);

                    bool isClientHello = clientHelloInfo != null;
                    if (isClientHello)
                    {
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    await endPoint.InvokeTunnectConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);

                    if (!excluded && isClientHello)
                    {
                        connectRequest.RequestUri = new Uri("https://" + httpUrl);

                        SslStream sslStream = null;

                        try
                        {
                            sslStream = new SslStream(clientStream);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);

                            var certificate = endPoint.GenericCertificate ?? await CertificateManager.CreateCertificateAsync(certName);

                            //Successfully managed to authenticate the client using the fake certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false);

                            //HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new CustomBufferedStream(sslStream, BufferSize);

                            clientStreamReader.Dispose();
                            clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
                        }
                        catch
                        {
                            sslStream?.Dispose();
                            return;
                        }

                        if (await HttpHelper.IsConnectMethod(clientStream) == -1)
                        {
                            // It can be for example some Google (Cloude Messaging for Chrome) magic
                            excluded = true;
                        }
                    }

                    //Hostname is excluded or it is not an HTTPS connect
                    if (excluded || !isClientHello)
                    {
                        //create new connection
                        using (var connection = await GetServerConnection(connectArgs, true))
                        {
                            if (isClientHello)
                            {
                                int available = clientStream.Available;
                                if (available > 0)
                                {
                                    //send the buffered data
                                    var data = BufferPool.GetBuffer(BufferSize);

                                    try
                                    {
                                        // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
                                        await clientStream.ReadAsync(data, 0, available);

                                        await connection.StreamWriter.WriteAsync(data, 0, available, true);
                                    }
                                    finally
                                    {
                                        BufferPool.ReturnBuffer(data);
                                    }
                                }

                                var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream);

                                ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
                            }

                            await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize,
                                                    (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
                                                    (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
                                                    ExceptionFunc);
                        }

                        return;
                    }
                }

                //Now create the request
                await HandleHttpSessionRequest(tcpClient, clientStream, clientStreamReader, clientStreamWriter, connectHostname, endPoint, connectRequest);
            }
            catch (ProxyHttpException e)
            {
                ExceptionFunc(e);
            }
            catch (IOException e)
            {
                ExceptionFunc(new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                ExceptionFunc(new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                ExceptionFunc(new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                clientStreamReader.Dispose();
                clientStream.Dispose();
            }
        }
        private static void AquireBuffersConcurrently(BufferPool pool, List<IBuffer> bufferList, int threadNumber, int sizeOdd, int sizeEven)
        {
            object bufferList_sync = new object();
            ManualResetEvent mre = new ManualResetEvent(false);

            for (int i = 0; i < threadNumber; i++)
            {
                Thread thread = new Thread(delegate(object number)
                {
                    var size = ((int)number % 2 == 1 ? sizeOdd : sizeEven);

                    //wait for signal
                    mre.WaitOne();

                    //Aquire buffer
                    IBuffer buff = pool.GetBuffer(size);

                    //Add to Queue
                    lock (bufferList_sync)
                    {
                        bufferList.Add(buff);
                    }
                });

                thread.IsBackground = true;
                thread.Start(i);
            }

            Thread.Sleep(500); //ensure all threads are waiting for signal

            mre.Set(); //signal event

            //Wait till all threads are done
            while (true)
            {
                lock (bufferList)
                {
                    if (bufferList.Count == threadNumber) break;
                    Thread.Sleep(500);
                }
            }
        }
Exemple #25
0
 /// <summary>
 /// 保存现在的Buffer到命令Buffer队列
 /// 获取一个新的Buffer
 /// </summary>
 private void PushCurrentBufferAndGetNewBuffer()
 {
     _cmdBuffer.Add(new ArraySegment <byte>(_currentBuffer, 0, _currentBufferIndex));
     _currentBuffer      = BufferPool.GetBuffer();
     _currentBufferIndex = 0;
 }