Beispiel #1
0
        public void Run()
        {
            Task.Run(async() =>
            {
                LogMessage LogDownstreamText(LogEventLevel level, string text = null) => LogMessage.Downstream(level, Id, _downstreamClient.Endpoint.Address, text);
                LogMessage LogDownstreamException(Exception exception) => LogMessage.Downstream(exception, Id, _downstreamClient.Endpoint.Address);
                LogMessage LogUpstreamText(LogEventLevel level, string text = null) => LogMessage.Upstream(level, Id, _upstreamServer.Endpoint.Address, text);
                LogMessage LogUpstreamException(Exception exception) => LogMessage.Upstream(exception, Id, _upstreamServer.Endpoint.Address);
                LogMessage LogForwardProxyText(LogEventLevel level, string text = null) => LogMessage.ForwardProxy(level, Id, _upstreamServer.Endpoint.Address, text);
                LogMessage LogForwardProxyException(Exception exception) => LogMessage.ForwardProxy(exception, Id, _upstreamServer.Endpoint.Address);

                RequestLine requestLine;
                IEnumerable <Header> headers;
                HostHeader hostHeader;

                try
                {
                    (GetRequestLineResult result, RequestLine requestLine)getRequestLineResult = _downstreamClient.GetRequestLine();

                    switch (getRequestLineResult.result)
                    {
                    case GetRequestLineResult.Success:
                        requestLine = getRequestLineResult.requestLine;
                        break;

                    case GetRequestLineResult.InvalidRequestLine:
                        LogDownstreamText(LogEventLevel.Error, "Invalid request line").Write();
                        goto close;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    (GetHeadersResult result, IEnumerable <Header> headers)getHeadersResult = _downstreamClient.GetHeaders();

                    getHeadersResult.headers = getHeadersResult.headers ?? Enumerable.Empty <Header>();

                    switch (getHeadersResult.result)
                    {
                    case GetHeadersResult.Success:
                        headers = getHeadersResult.headers.ToImmutableArray();
                        break;

                    case GetHeadersResult.InvalidHeader:
                        LogDownstreamText(LogEventLevel.Error, "Invalid header").Write();
                        goto close;

                    default:
                        throw new ArgumentOutOfRangeException();
                    }

                    hostHeader = HostHeader.Parse(getHeadersResult.headers.FirstOrDefault(x => x.Name.Equals("Host", StringComparison.OrdinalIgnoreCase)));

                    LogDownstreamText(LogEventLevel.Information).RightArrow(true).Text($"{getRequestLineResult.requestLine}{(hostHeader != null ? $", {hostHeader}" : "")}").Write();
                }
                catch (Exception exception)
                {
                    LogDownstreamException(exception).Write();
                    goto close;
                }

                bool isTunnel = requestLine.Method == "CONNECT";
                string upstreamHost;
                int upstreamPort;

                if (requestLine.ParsedRequestUri.IsAbsoluteUri && _validAbsoluteUriSchemes.Contains(requestLine.ParsedRequestUri.Scheme, StringComparer.OrdinalIgnoreCase))
                {
                    upstreamHost = requestLine.ParsedRequestUri.Host;
                    upstreamPort = requestLine.ParsedRequestUri.Port;
                }
                else if (hostHeader != null)
                {
                    upstreamHost = hostHeader.Host;
                    upstreamPort = hostHeader.Port ?? (isTunnel ? 443 : 80);
                }
                else
                {
                    LogDownstreamText(LogEventLevel.Error, "Missing Host header").Write();
                    goto close;
                }

                if (_disallowedHosts.Any(x => x.Contains(upstreamHost) == ContainsResult.Yes))
                {
                    LogDownstreamText(LogEventLevel.Error, $"Host '{upstreamHost}' is disallowed").Write();
                    goto close;
                }

                ConfigModel.ForwardProxiesModel.ForwardProxyModel forwardProxy = isTunnel ? _forwardProxyHttps : _forwardProxyHttp;

                async Task <bool> ConnectToUpstreamServerAsync()
                {
                    _upstreamServer?.Close();

                    var upstreamServer = new TcpClient();

                    try
                    {
                        if (forwardProxy != null && _noProxyHosts.All(x => x.Contains(upstreamHost) != ContainsResult.Yes))
                        {
                            await upstreamServer.ConnectAsync(forwardProxy.Host, forwardProxy.Port);
                        }
                        else
                        {
                            forwardProxy = null;
                            await upstreamServer.ConnectAsync(upstreamHost, upstreamPort);
                        }
                    }
                    catch (Exception exception)
                    {
                        LogDownstreamException(exception).Write();
                        return(false);
                    }

                    _upstreamServer = _httpClientFactory.Create(upstreamServer);

                    return(true);
                }

                if (!await ConnectToUpstreamServerAsync())
                {
                    goto close;
                }

                if (isTunnel)
                {
                    var responseStatusLine = new ResponseStatusLine(requestLine.HttpVersion, 200);

                    _downstreamClient.WriteResponseStatusLine(responseStatusLine);
                    if (_configService.Config.Options.SendProxyAgentHeader)
                    {
                        _downstreamClient.WriteHeader("Proxy-Agent", typeof(Program).Namespace);
                    }
                    _downstreamClient.WriteNewLine();
                    _downstreamClient.Flush();

                    LogDownstreamText(LogEventLevel.Information).LeftArrow(true).Text(responseStatusLine.ToString()).Write();

                    if (forwardProxy != null)
                    {
                        LogForwardProxyText(LogEventLevel.Information).LeftArrow(true).Text($"{requestLine}{(hostHeader != null ? $", {hostHeader}" : "")}").Write();

                        _upstreamServer.WriteRequestLine(requestLine);
                        _upstreamServer.WriteHeaders(hostHeader);
                        _upstreamServer.WriteNewLine();

                        (GetResponseStatusLineResult result, ResponseStatusLine responseStatusLine)responseStatusLineResult = _upstreamServer.GetResponseStatusLine();

                        switch (responseStatusLineResult.result)
                        {
                        case GetResponseStatusLineResult.Success:
                            LogForwardProxyText(LogEventLevel.Information).RightArrow(true).Text(responseStatusLineResult.responseStatusLine.ToString()).Write();
                            break;

                        case GetResponseStatusLineResult.InvalidResponseStatusLine:
                            LogForwardProxyText(LogEventLevel.Error, "Invalid response status line").Write();
                            break;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }

                        _upstreamServer.GetHeaders();
                    }
                }
                else
                {
                    void WriteUpstreamServerRequest(params Header[] additionalHeaders)
                    {
                        _upstreamServer.WriteRequestLine(requestLine);
                        _upstreamServer.WriteHeaders(headers);
                        if (_configService.Config.Options.SendProxyAgentHeader)
                        {
                            _upstreamServer.WriteHeaders(Header.Parse($"Proxy-Agent: {typeof(Program).Namespace}"));
                        }
                        _upstreamServer.WriteHeaders(additionalHeaders);
                        _upstreamServer.WriteNewLine();
                        _upstreamServer.Flush();
                    }

                    WriteUpstreamServerRequest();

                    if (forwardProxy != null && forwardProxy.Authentication.Basic.Enabled)
                    {
                        (GetResponseStatusLineResult result, ResponseStatusLine responseStatusLine)getResponseStatusLineResult = _upstreamServer.GetResponseStatusLine();

                        switch (getResponseStatusLineResult.result)
                        {
                        case GetResponseStatusLineResult.Success:
                            break;

                        case GetResponseStatusLineResult.InvalidResponseStatusLine:
                            LogUpstreamText(LogEventLevel.Error, "Invalid response line");
                            goto close;

                        default:
                            throw new ArgumentOutOfRangeException();
                        }

                        if (getResponseStatusLineResult.responseStatusLine.StatusCode == 407)
                        {
                            (GetHeadersResult result, IEnumerable <Header> headers)getHeadersResult = _upstreamServer.GetHeaders();

                            switch (getHeadersResult.result)
                            {
                            case GetHeadersResult.Success:
                                break;

                            case GetHeadersResult.InvalidHeader:
                                LogUpstreamText(LogEventLevel.Error, "Invalid header").Write();
                                goto close;

                            default:
                                throw new ArgumentOutOfRangeException();
                            }

                            LogForwardProxyText(LogEventLevel.Warning).RightArrow(true).Text(getResponseStatusLineResult.responseStatusLine.ToString()).Write();

                            if (!await ConnectToUpstreamServerAsync())
                            {
                                goto close;
                            }

                            (GetCredentialsResult result, string username, string clearTextPassword)getCredentialsResult = _credentialService.GetCredentials();
                            string base64Credential = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{getCredentialsResult.username}:{getCredentialsResult.clearTextPassword}"));

                            LogForwardProxyText(LogEventLevel.Warning).LeftArrow(true).Text("Proxy authorization").Write();

                            WriteUpstreamServerRequest(new Header("Proxy-Authorization", $"Basic {base64Credential}"));
                        }
                        else
                        {
                            _downstreamClient.WriteResponseStatusLine(getResponseStatusLineResult.responseStatusLine);
                        }
                    }
                }

                var manualResetEvent = new ManualResetEventSlim();

#pragma warning disable 4014
                Task.Run(() =>
#pragma warning restore 4014
                {
                    try
                    {
                        while (true)
                        {
                            ArraySegment <byte> arraySegment = _downstreamClient.Read();

                            if (arraySegment.Count == 0)
                            {
                                break;
                            }

                            _upstreamServer.Write(arraySegment);
                            _upstreamServer.Flush();

                            LogDownstreamText(LogEventLevel.Verbose)
                            .RightArrow(true)
                            .Text($"{arraySegment.Count} byte{(arraySegment.Count == 1 ? "" : "s")}")
                            .RightArrow(true)
                            .UpstreamIdentifier(Id)
                            .Space()
                            .BracketedIpAddress(_upstreamServer.Endpoint.Address)
                            .Write();
                        }
                    }
                    catch (Exception exception)
                    {
                        LogDownstreamException(exception).Write();
                    }
                    finally
                    {
                        manualResetEvent.Set();
                    }
                });

#pragma warning disable 4014
                Task.Run(() =>
#pragma warning restore 4014
                {
                    try
                    {
                        while (true)
                        {
                            ArraySegment <byte> arraySegment = _upstreamServer.Read();

                            if (arraySegment.Count == 0)
                            {
                                break;
                            }

                            _downstreamClient.Write(arraySegment);
                            _downstreamClient.Flush();

                            (forwardProxy != null ? LogForwardProxyText(LogEventLevel.Verbose) : LogUpstreamText(LogEventLevel.Verbose))
                            .RightArrow(true)
                            .Text($"{arraySegment.Count} byte{(arraySegment.Count == 1 ? "" : "s")}")
                            .RightArrow(true)
                            .DownstreamIdentifier(Id)
                            .Space()
                            .BracketedIpAddress(_downstreamClient.Endpoint.Address)
                            .Write();
                        }
                    }
                    catch (Exception exception)
                    {
                        (forwardProxy != null ? LogForwardProxyException(exception) : LogUpstreamException(exception)).Write();
                    }
                    finally
                    {
                        manualResetEvent.Set();
                    }
                });

                manualResetEvent.Wait();

                close:
                CloseConnections();

                LogDownstreamText(LogEventLevel.Information, "Connection closed").Write();

                _closedByClient.OnNext(Unit.Default);
            });
        }
Beispiel #2
0
 private void CloseConnections()
 {
     _upstreamServer?.Close();
     _downstreamClient.Close();
 }