Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion assets/strings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"sing-box log level": "Уровень логов sing-box",
"Applies after reconnection. Debug and trace include destination addresses and application paths and increase log volume.": "Применяется после переподключения. Debug и trace включают адреса назначения и пути приложений и увеличивают объём журнала.",
"SOCKS5 does not encrypt the server connection; UDP requires server support.": "SOCKS5 не шифрует соединение с сервером; UDP требует поддержки сервера.",
"The imported global SSH fingerprint applies to all SSH profiles. Changing it here updates that global setting.": "Импортированный общий SSH fingerprint применяется ко всем SSH-профилям. Изменение здесь обновляет общую настройку.",
"Import notices": "Замечания к импорту",
Expand Down Expand Up @@ -574,7 +576,7 @@
"TLS fingerprints and bootstrap DNS": "TLS fingerprint и начальный DNS",
"Windows DNS": "DNS Windows",
"Invalid bootstrap DNS provider.": "Некорректный провайдер начального DNS.",
"Resolves proxy server names directly, before connecting. Automatic uses Cloudflare for proxy-default mode and Windows DNS otherwise. Does not replace application DNS.": "Разрешает имена прокси-серверов напрямую, до подключения. Автоматически: Cloudflare при прокси по умолчанию, иначе DNS Windows. Не заменяет DNS приложений.",
"Resolves proxy server names before connecting. Automatic uses Cloudflare. While connected, ordinary application DNS uses the active profile DNS, including direct traffic. System DNS resumes after disconnection.": "Разрешает имена прокси до подключения. Автоматически используется Cloudflare. Во время подключения обычные DNS-запросы приложений используют DNS активного профиля, включая прямой трафик. После отключения возвращается системный DNS.",
"DNS provider": "Провайдер DNS",
"Custom DoH URL": "Свой адрес DoH",
"Yandex Basic": "Яндекс Базовый",
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ to fresh settings. MTU and traffic units are portable within this section.
| tunMtu | integer 1280–65535; 1500 |
| trafficUnits | IEC (default), SI |
| bootstrapDnsProvider | auto (default), system, CLOUDFLARE, GOOGLE, QUAD9, YANDEX, YANDEX_SAFE, YANDEX_FAMILY |
| coreLogLevel | trace, debug, info, warn (default), error, fatal, panic; applies after reconnection |
| reverseDnsCacheHours | 0 (DNS TTL), 1, 6, 12, 24 (default), 72; failed TTL-mode queries cache for one hour |
| resolveConnectionHosts | boolean; false |
| showAllConnections | boolean; false |
Expand Down
17 changes: 14 additions & 3 deletions docs/en/profiles-and-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,25 @@ startup uses cached lists and refreshes them after connecting.
## DNS and connection settings

Profile DNS supports Cloudflare, Google, Quad9, Yandex or custom DoH and TCP/UDP
endpoints. When the profile is the default route, its DNS uses the proxy; system
mode retains Windows DNS. UDP DNS needs a UDP-capable transport and does not fall
endpoints. While connected with an active profile, all ordinary application DNS
uses that profile's DNS through the proxy, including applications routed directly.
Traffic rules do not select a separate DNS provider. UDP DNS needs a UDP-capable transport and does not fall
back directly. Custom DNS URL query strings are unsupported.

Bootstrap DNS resolves proxy hostnames before connecting and can use Windows DNS
or a selected DoH provider directly. Connection settings also include TCP TLS and
or a selected DoH provider directly. Automatic selects Cloudflare. Bootstrap does
not select application DNS. Disconnecting removes the TUN DNS configuration and
returns to the existing Windows DNS settings; physical adapter settings are unchanged.
The Windows DNS cache is cleared at TUN DNS startup and shutdown; applications
with their own DNS caches may need restarting. Without an active profile, system DNS is retained.
Application-managed DoH is unaffected. Connection settings also include TCP TLS and
SSH fingerprint presets; H3 uses the core's native fingerprint.

Connection settings include the sing-box log level (default `warn`). Changes are
saved automatically and apply after reconnection. Use `debug` for troubleshooting;
`trace` is more verbose. These logs include destination addresses and application
paths and use the existing local log rotation limit.

## Configuration transfer and subscriptions

Import MegaProxy JSON v1–8, FoxyProxy JSON, ProxyList or SuperProxy. Export MegaProxy
Expand Down
17 changes: 14 additions & 3 deletions docs/ru/profiles-and-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,25 @@ SSH и HTTPS только через TCP не предоставляют пер
## DNS и параметры подключения

DNS профиля поддерживает Cloudflare, Google, Quad9, Yandex, собственный DoH
и TCP/UDP-серверы. Когда профиль — маршрут по умолчанию, его DNS идёт через прокси;
системный режим сохраняет DNS Windows. Для UDP DNS нужен совместимый транспорт,
и TCP/UDP-серверы. При подключении с активным профилем все обычные DNS-запросы
приложений идут через DNS этого профиля и прокси, включая приложения с прямым
маршрутом. Правила трафика не выбирают отдельный DNS. Для UDP DNS нужен совместимый транспорт,
прямого резервного маршрута нет. Query-параметры в DNS URL не поддерживаются.

Bootstrap DNS разрешает имя прокси до подключения через DNS Windows или выбранный
DoH напрямую. Доступны TCP TLS и SSH fingerprint; для H3 используется штатный
DoH напрямую. Автоматический выбор означает Cloudflare. Bootstrap не выбирает DNS
приложений. При отключении удаляется настройка DNS туннеля и возвращается системный
DNS; настройки физических адаптеров не меняются. При включении и отключении DNS
туннеля очищается кеш DNS Windows; приложения со своим кешем может потребоваться
перезапустить. Без активного профиля сохраняется системный DNS.
Собственный DoH приложений не меняется. Доступны TCP TLS и SSH fingerprint; для H3 используется штатный
fingerprint ядра.

В настройках подключения выбирается уровень логов sing-box (по умолчанию `warn`).
Выбор сохраняется автоматически и применяется после переподключения. Для диагностики
используйте `debug`; `trace` ещё подробнее. Журнал содержит адреса назначения и пути
приложений, его размер ограничивается существующей настройкой ротации.

## Конфигурации, подписки и диагностика

Импорт: MegaProxy JSON v1–8, FoxyProxy JSON, ProxyList и SuperProxy.
Expand Down
25 changes: 17 additions & 8 deletions src/WindowsMegaProxy.Core/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public sealed class Settings
public string Theme { get; set; } = "auto";
public bool AlwaysMinimizeToTray { get; set; }
public string TlsFingerprint { get; set; } = "DEFAULT";
public string CoreLogLevel { get; set; } = "warn";
public string BootstrapDnsProvider { get; set; } = "auto";
public int TunMtu { get; set; } = 1500;
public string TrafficUnits { get; set; } = "IEC";
Expand Down Expand Up @@ -101,7 +102,13 @@ public static class Configuration
"FIREFOX_ANDROID" => "firefox", "EDGE_ANDROID" => "edge", "RANDOMIZED" => "randomized", _ => "chrome"
};
public static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
public static bool UsesProxy(Settings settings) => settings.DefaultAction == "proxy" || AppUpdatePolicy.ThroughProxy(settings) ||
public static void ValidateCoreLogLevel(string value)
{
if (value is not ("trace" or "debug" or "info" or "warn" or "error" or "fatal" or "panic"))
throw new ArgumentException("Invalid sing-box log level.");
}

public static bool UsesProxy(Settings settings) => settings.ActiveProfile is not null || settings.DefaultAction == "proxy" || AppUpdatePolicy.ThroughProxy(settings) ||
settings.Rules.Any(rule => rule.Enabled && (rule.Action == "proxy" || rule.PodkopThroughProxy && PodkopCatalog.Parse(rule.Destination.Trim()).Length > 0));
public static bool RequiresHttp3(Settings settings) => UsesProxy(settings) && settings.ActiveProfile?.UsesHttp3 == true;
public static bool RequiresAlpha(Settings settings) => RequiresHttp3(settings) || UsesProxy(settings) && settings.ActiveProfile?.PreferHttp2 == true;
Expand Down Expand Up @@ -137,7 +144,9 @@ public static string FolderPattern(string folder)
public static JsonObject Build(Settings settings, int apiPort, string apiSecret, bool allowUnresolvedSsh = false)
{
ValidateAction(settings.DefaultAction);
ValidateCoreLogLevel(settings.CoreLogLevel);
bool proxyDefault = settings.DefaultAction == "proxy";
bool interceptDns = settings.ActiveProfile is not null;
var outbounds = new JsonArray(new JsonObject { ["type"] = "direct", ["tag"] = "direct" });
if (UsesProxy(settings))
{
Expand All @@ -152,7 +161,7 @@ public static JsonObject Build(Settings settings, int apiPort, string apiSecret,

var rules = new JsonArray
{
proxyDefault ? new JsonObject { ["port"] = 53, ["action"] = "hijack-dns" }
interceptDns ? new JsonObject { ["port"] = 53, ["action"] = "hijack-dns" }
: new JsonObject { ["port"] = 53, ["action"] = "route", ["outbound"] = "direct" },
new JsonObject { ["process_name"] = new JsonArray(DirectProcessNames.Select(name => (JsonNode)JsonValue.Create(name)!).ToArray()), ["action"] = "route", ["outbound"] = "direct" }
};
Expand Down Expand Up @@ -183,27 +192,27 @@ public static JsonObject Build(Settings settings, int apiPort, string apiSecret,

return new JsonObject
{
["log"] = new JsonObject { ["level"] = "warn", ["timestamp"] = true },
["log"] = new JsonObject { ["level"] = settings.CoreLogLevel, ["timestamp"] = true },
["dns"] = new JsonObject
{
["servers"] = proxyDefault ? new JsonArray(
["servers"] = interceptDns ? new JsonArray(
ProfileDns.BuildBootstrap(settings.BootstrapDnsProvider, proxyDefault),
ProfileDns.Build(settings.ActiveProfile!))
: new JsonArray(ProfileDns.BuildBootstrap(settings.BootstrapDnsProvider, proxyDefault), new JsonObject { ["type"] = "local", ["tag"] = "system" }),
["final"] = proxyDefault ? "dns" : "system",
["final"] = interceptDns ? "dns" : "system",
["strategy"] = "prefer_ipv4", ["reverse_mapping"] = true
},
["inbounds"] = new JsonArray(new JsonObject
{
["type"] = "tun", ["tag"] = "tun", ["interface_name"] = AppIdentity.DisplayName,
["address"] = new JsonArray(tunAddresses.Ipv4, tunAddresses.Ipv6),
["mtu"] = settings.TunMtu, ["auto_route"] = true, ["strict_route"] = proxyDefault,
["dns_mode"] = proxyDefault ? "hijack" : "disabled"
["mtu"] = settings.TunMtu, ["auto_route"] = true, ["strict_route"] = interceptDns,
["dns_mode"] = interceptDns ? "hijack" : "disabled"
}),
["outbounds"] = outbounds,
["route"] = new JsonObject
{
["auto_detect_interface"] = true, ["find_process"] = true, ["default_domain_resolver"] = proxyDefault ? "bootstrap" : "system", ["rules"] = rules,
["auto_detect_interface"] = true, ["find_process"] = true, ["default_domain_resolver"] = interceptDns ? "bootstrap" : "system", ["rules"] = rules,
["rule_set"] = new JsonArray(PodkopCatalog.Groups(settings).Select(ids => (JsonNode)PodkopCatalog.Definition(ids)).ToArray()),
["final"] = proxyDefault ? "proxy" : "direct"
},
Expand Down
2 changes: 1 addition & 1 deletion src/WindowsMegaProxy.Core/ConfigurationFields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void Proxy(JsonObject proxy, bool jump = false)
}
else
{
Keep(windows, "version defaultAction bypassLocalNetworks autoConnect resetExistingTcpConnections logSizeMb reverseDnsCacheHours resolveConnectionHosts showAllConnections language theme alwaysMinimizeToTray tlsFingerprint bootstrapDnsProvider launchAtSignIn rules tunMtu trafficUnits automaticAppUpdates appUpdatesThroughProxy");
Keep(windows, "version defaultAction bypassLocalNetworks autoConnect resetExistingTcpConnections logSizeMb reverseDnsCacheHours resolveConnectionHosts showAllConnections language theme alwaysMinimizeToTray tlsFingerprint coreLogLevel bootstrapDnsProvider launchAtSignIn rules tunMtu trafficUnits automaticAppUpdates appUpdatesThroughProxy");
Fingerprint(windows, "tlsFingerprint");
}
}
Expand Down
1 change: 1 addition & 0 deletions src/WindowsMegaProxy.Core/ImportReview.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public Settings Apply(ISet<string> selected, bool applyGlobals, ISet<string> rem
result.AlwaysMinimizeToTray = current.AlwaysMinimizeToTray;
result.TlsFingerprint = current.TlsFingerprint; result.LaunchAtSignIn = current.LaunchAtSignIn;
result.BootstrapDnsProvider = current.BootstrapDnsProvider;
result.CoreLogLevel = current.CoreLogLevel;
result.ResetExistingTcpConnections = current.ResetExistingTcpConnections;
result.WindowsOptions = (JsonObject)current.WindowsOptions.DeepClone();
result.Rules = current.Rules.Select(rule => rule.Copy()).ToList();
Expand Down
3 changes: 3 additions & 0 deletions src/WindowsMegaProxy.Core/PortableConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public static Settings Import(string text, Settings current)
if (windows.ContainsKey("theme")) candidate.Theme = String(windows, "theme");
if (windows.ContainsKey("alwaysMinimizeToTray")) candidate.AlwaysMinimizeToTray = windows["alwaysMinimizeToTray"]!.GetValue<bool>();
if (windows.ContainsKey("tlsFingerprint")) candidate.TlsFingerprint = String(windows, "tlsFingerprint");
if (windows.ContainsKey("coreLogLevel")) candidate.CoreLogLevel = String(windows, "coreLogLevel");
if (windows.ContainsKey("bootstrapDnsProvider")) candidate.BootstrapDnsProvider = String(windows, "bootstrapDnsProvider");
if (windows.ContainsKey("launchAtSignIn")) candidate.LaunchAtSignIn = windows["launchAtSignIn"]!.GetValue<bool>();
if (windows.ContainsKey("rules")) candidate.Rules = windows["rules"]!.Deserialize<List<RoutingRule>>(PortableOptions) ?? throw Invalid(Ui.T("Invalid Windows rules."));
Expand Down Expand Up @@ -158,6 +159,7 @@ public static JsonObject Export(Settings settings, bool includePasswords = false
windows["theme"] = settings.Theme;
windows["alwaysMinimizeToTray"] = settings.AlwaysMinimizeToTray;
windows["bootstrapDnsProvider"] = settings.BootstrapDnsProvider;
windows["coreLogLevel"] = settings.CoreLogLevel;
windows["launchAtSignIn"] = settings.LaunchAtSignIn ?? false;
windows["rules"] = JsonSerializer.SerializeToNode(settings.Rules, PortableOptions);
}
Expand Down Expand Up @@ -190,6 +192,7 @@ public static void ValidateSettings(Settings settings)
if (settings.Language is not ("en" or "ru") || settings.Theme is not ("auto" or "light" or "dark")) throw Invalid("Invalid language or theme.");
settings.TlsFingerprint = ConfigurationFields.NormalizeFingerprint(settings.TlsFingerprint);
if (settings.ReverseDnsCacheHours is not (0 or 1 or 6 or 12 or 24 or 72)) throw Invalid("Unsupported reverse DNS cache duration.");
Configuration.ValidateCoreLogLevel(settings.CoreLogLevel);
_ = ProfileDns.BuildBootstrap(settings.BootstrapDnsProvider, false);
_ = ConfigurationSubscription.Get(settings);
_ = AppUpdatePolicy.Enabled(settings);
Expand Down
2 changes: 1 addition & 1 deletion src/WindowsMegaProxy.Core/ProfileDns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static readonly (string Id, string Name, string Url)[] Providers =

public static JsonObject BuildBootstrap(string provider, bool proxyDefault)
{
if (provider == "auto") provider = proxyDefault ? "CLOUDFLARE" : "system";
if (provider == "auto") provider = "CLOUDFLARE";
if (provider == "system") return new JsonObject { ["type"] = "local", ["tag"] = "bootstrap" };
string address = provider switch
{
Expand Down
2 changes: 1 addition & 1 deletion src/WindowsMegaProxy.Core/SettingsWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace WindowsMegaProxy;
public sealed record SettingsAnalysis(Settings Settings, string? Signature, string? Error)
{
public static bool SameNetwork(Settings a, Settings b) => a.DefaultAction == b.DefaultAction && a.BypassLan == b.BypassLan &&
a.TunMtu == b.TunMtu && a.TlsFingerprint == b.TlsFingerprint && a.BootstrapDnsProvider == b.BootstrapDnsProvider && a.ActiveProfileId == b.ActiveProfileId &&
a.CoreLogLevel == b.CoreLogLevel && a.TunMtu == b.TunMtu && a.TlsFingerprint == b.TlsFingerprint && a.BootstrapDnsProvider == b.BootstrapDnsProvider && a.ActiveProfileId == b.ActiveProfileId &&
AppUpdatePolicy.ThroughProxy(a) == AppUpdatePolicy.ThroughProxy(b) &&
SshConfiguration.AuthMode(a) == SshConfiguration.AuthMode(b) &&
JsonNode.DeepEquals(a.SharedOptions["ssh"]?["fingerprint"], b.SharedOptions["ssh"]?["fingerprint"]) &&
Expand Down
20 changes: 20 additions & 0 deletions src/WindowsMegaProxy.Core/TunAddresses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,31 @@
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text.Json.Nodes;

namespace WindowsMegaProxy;

public static class TunAddresses
{
public static bool HasIpv6Uplink(IEnumerable<IPAddress> addresses, IEnumerable<IPAddress> gateways) =>
addresses.Any(address => address.AddressFamily == AddressFamily.InterNetworkV6 &&
!address.IsIPv6LinkLocal && !address.IsIPv6SiteLocal && !IPAddress.IsLoopback(address) &&
!address.Equals(IPAddress.IPv6Any) && (address.GetAddressBytes()[0] & 0xfe) != 0xfc) &&
gateways.Any(address => address.AddressFamily == AddressFamily.InterNetworkV6 &&
!address.Equals(IPAddress.IPv6Any));

public static void Apply(JsonObject config, (string Ipv4, string Ipv6) addresses, bool ipv6)
{
config["inbounds"]![0]!["address"] = ipv6 ? new JsonArray(addresses.Ipv4, addresses.Ipv6) : new JsonArray(addresses.Ipv4);
if (ipv6) return;
config["dns"]!["strategy"] = "ipv4_only";
config["route"]!["rules"]!.AsArray().Insert(0, new JsonObject
{
["ip_version"] = 6, ["action"] = "reject"
});
}

public static IPNetwork Network(IPAddress address, int prefixLength)
{
byte[] bytes = address.GetAddressBytes();
Expand Down
Loading
Loading