2024-05-22 14:45:22 +00:00
|
|
|
|
namespace FireflySR.Tool.Proxy
|
|
|
|
|
{
|
|
|
|
|
using System;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Security;
|
2024-05-22 15:49:10 +00:00
|
|
|
|
using System.Text;
|
2024-05-22 14:45:22 +00:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Titanium.Web.Proxy;
|
|
|
|
|
using Titanium.Web.Proxy.EventArguments;
|
|
|
|
|
using Titanium.Web.Proxy.Models;
|
|
|
|
|
|
|
|
|
|
internal class ProxyService
|
|
|
|
|
{
|
|
|
|
|
private readonly ProxyConfig _conf;
|
|
|
|
|
private readonly ProxyServer _webProxyServer;
|
|
|
|
|
private readonly string _targetRedirectHost;
|
|
|
|
|
private readonly int _targetRedirectPort;
|
|
|
|
|
|
|
|
|
|
public ProxyService(string targetRedirectHost, int targetRedirectPort, ProxyConfig conf)
|
|
|
|
|
{
|
|
|
|
|
_conf = conf;
|
|
|
|
|
_webProxyServer = new ProxyServer();
|
|
|
|
|
_webProxyServer.CertificateManager.EnsureRootCertificate();
|
|
|
|
|
|
|
|
|
|
_webProxyServer.BeforeRequest += BeforeRequest;
|
|
|
|
|
_webProxyServer.ServerCertificateValidationCallback += OnCertValidation;
|
|
|
|
|
|
|
|
|
|
_targetRedirectHost = targetRedirectHost;
|
|
|
|
|
_targetRedirectPort = targetRedirectPort;
|
|
|
|
|
|
|
|
|
|
int port = conf.ProxyBindPort == 0 ? Random.Shared.Next(10000, 60000) : conf.ProxyBindPort;
|
|
|
|
|
SetEndPoint(new ExplicitProxyEndPoint(IPAddress.Any, port, true));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetEndPoint(ExplicitProxyEndPoint explicitEP)
|
|
|
|
|
{
|
|
|
|
|
explicitEP.BeforeTunnelConnectRequest += BeforeTunnelConnectRequest;
|
|
|
|
|
|
|
|
|
|
_webProxyServer.AddEndPoint(explicitEP);
|
|
|
|
|
_webProxyServer.Start();
|
|
|
|
|
|
2024-05-22 15:49:10 +00:00
|
|
|
|
if (OperatingSystem.IsWindows())
|
|
|
|
|
{
|
|
|
|
|
_webProxyServer.SetAsSystemHttpProxy(explicitEP);
|
|
|
|
|
_webProxyServer.SetAsSystemHttpsProxy(explicitEP);
|
|
|
|
|
}
|
2024-05-22 14:45:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Shutdown()
|
|
|
|
|
{
|
|
|
|
|
_webProxyServer.Stop();
|
|
|
|
|
_webProxyServer.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Task BeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
string hostname = args.HttpClient.Request.RequestUri.Host;
|
|
|
|
|
Console.WriteLine(hostname);
|
|
|
|
|
args.DecryptSsl = ShouldRedirect(hostname);
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Task OnCertValidation(object sender, CertificateValidationEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (args.SslPolicyErrors == SslPolicyErrors.None)
|
|
|
|
|
args.IsValid = true;
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ShouldForceRedirect(string path)
|
|
|
|
|
{
|
|
|
|
|
foreach (var keyword in _conf.ForceRedirectOnUrlContains)
|
|
|
|
|
{
|
|
|
|
|
if (path.Contains(keyword)) return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-22 15:49:10 +00:00
|
|
|
|
private bool ShouldBlock(Uri uri)
|
|
|
|
|
{
|
|
|
|
|
var path = uri.AbsolutePath;
|
|
|
|
|
return _conf.BlockUrls.Contains(path);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-22 14:45:22 +00:00
|
|
|
|
private Task BeforeRequest(object sender, SessionEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
string hostname = args.HttpClient.Request.RequestUri.Host;
|
|
|
|
|
if (ShouldRedirect(hostname) || ShouldForceRedirect(args.HttpClient.Request.RequestUri.AbsolutePath))
|
|
|
|
|
{
|
|
|
|
|
string requestUrl = args.HttpClient.Request.Url;
|
|
|
|
|
Uri local = new Uri($"http://{_targetRedirectHost}:{_targetRedirectPort}/");
|
|
|
|
|
|
2024-05-22 15:49:10 +00:00
|
|
|
|
Uri builtUrl = new UriBuilder(requestUrl)
|
2024-05-22 14:45:22 +00:00
|
|
|
|
{
|
|
|
|
|
Scheme = local.Scheme,
|
|
|
|
|
Host = local.Host,
|
|
|
|
|
Port = local.Port
|
2024-05-22 15:49:10 +00:00
|
|
|
|
}.Uri;
|
|
|
|
|
|
|
|
|
|
string replacedUrl = builtUrl.ToString();
|
|
|
|
|
if (ShouldBlock(builtUrl))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"Blocked: {replacedUrl}");
|
|
|
|
|
args.Respond(new Titanium.Web.Proxy.Http.Response(Encoding.UTF8.GetBytes("Fuck off"))
|
|
|
|
|
{
|
|
|
|
|
StatusCode = 404,
|
|
|
|
|
StatusDescription = "Oh no!!!",
|
|
|
|
|
}, true);
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2024-05-22 14:45:22 +00:00
|
|
|
|
|
|
|
|
|
Console.WriteLine("Redirecting: " + replacedUrl);
|
|
|
|
|
args.HttpClient.Request.Url = replacedUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ShouldRedirect(string hostname)
|
|
|
|
|
{
|
|
|
|
|
if (hostname.Contains(':'))
|
|
|
|
|
hostname = hostname[0..hostname.IndexOf(':')];
|
|
|
|
|
foreach (string domain in _conf.AlwaysIgnoreDomains)
|
|
|
|
|
{
|
|
|
|
|
if (hostname.EndsWith(domain))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foreach (string domain in _conf.RedirectDomains)
|
|
|
|
|
{
|
|
|
|
|
if (hostname.EndsWith(domain))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|