Add UoT support

This commit is contained in:
DHR60 2026-02-18 22:28:43 +08:00
parent 584e538623
commit 6eedb7b37f
20 changed files with 91 additions and 10 deletions

View file

@ -30,7 +30,10 @@ public class TuicFmt : BaseFmt
var query = Utils.ParseQueryString(url.Query);
ResolveUriQuery(query, ref item);
item.HeaderType = GetQueryValue(query, "congestion_control");
item.SetProtocolExtra(item.GetProtocolExtra() with
{
CongestionControl = GetQueryValue(query, "congestion_control")
});
return item;
}
@ -51,7 +54,10 @@ public class TuicFmt : BaseFmt
var dicQuery = new Dictionary<string, string>();
ToUriQueryLite(item, ref dicQuery);
dicQuery.Add("congestion_control", item.HeaderType);
if (!item.GetProtocolExtra().CongestionControl.IsNullOrEmpty())
{
dicQuery.Add("congestion_control", item.GetProtocolExtra().CongestionControl);
}
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Username ?? ""}:{item.Password}", dicQuery, remark);
}

View file

@ -281,12 +281,11 @@ public sealed class AppManager
return await SQLiteHelper.Instance.TableAsync<FullConfigTemplateItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
}
#pragma warning disable CS0618
public async Task MigrateProfileExtra()
{
await MigrateProfileExtraGroup();
#pragma warning disable CS0618
const int pageSize = 100;
var offset = 0;
@ -310,7 +309,6 @@ public sealed class AppManager
}
//await ProfileGroupItemManager.Instance.ClearAll();
#pragma warning restore CS0618
}
private async Task<int> MigrateProfileExtraSub(List<ProfileItem> batch)
@ -356,6 +354,7 @@ public sealed class AppManager
break;
case EConfigType.TUIC:
extra = extra with { CongestionControl = item.HeaderType.NullIfEmpty(), };
item.Username = item.Id;
item.Id = item.Security;
item.Password = item.Security;
@ -412,7 +411,6 @@ public sealed class AppManager
private async Task<bool> MigrateProfileExtraGroup()
{
#pragma warning disable CS0618
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
@ -477,8 +475,8 @@ public sealed class AppManager
return true;
//await ProfileGroupItemManager.Instance.ClearAll();
#pragma warning restore CS0618
}
#pragma warning restore CS0618
#endregion SqliteHelper

View file

@ -2,6 +2,9 @@ namespace ServiceLib.Models;
public record ProtocolExtraItem
{
public bool? Uot { get; init; }
public string? CongestionControl { get; init; }
// vmess
public string? AlterId { get; init; }
public string? VmessSecurity { get; init; }

View file

@ -133,6 +133,7 @@ public class Outbound4Sbox : BaseServer4Sbox
public int? recv_window_conn { get; set; }
public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; }
public bool? udp_over_tcp { get; set; }
public string? method { get; set; }
public string? username { get; set; }
public string? password { get; set; }

View file

@ -179,6 +179,8 @@ public class ServersItem4Ray
public string flow { get; set; }
public bool? uot { get; set; }
public List<SocksUsersItem4Ray> users { get; set; }
}

View file

@ -4509,6 +4509,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 UDP over TCP 的本地化字符串。
/// </summary>
public static string TbUot {
get {
return ResourceManager.GetString("TbUot", resourceCulture);
}
}
/// <summary>
/// 查找类似 Validate Regional Domain IPs 的本地化字符串。
/// </summary>

View file

@ -1686,4 +1686,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
</root>

View file

@ -1683,4 +1683,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
</root>

View file

@ -1686,4 +1686,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
</root>

View file

@ -1686,4 +1686,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
</root>

View file

@ -1686,4 +1686,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
</root>

View file

@ -1683,4 +1683,7 @@
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>订阅后置节点 {0} 未找到,已跳过。</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
</root>

View file

@ -1683,4 +1683,7 @@
<data name="MsgSubscriptionNextProfileNotFound" xml:space="preserve">
<value>Subscription next proxy {0} not found. Skipping.</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
</root>

View file

@ -112,6 +112,7 @@ public partial class CoreConfigSingboxService
outbound.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod)
? protocolExtra.SsMethod : Global.None;
outbound.password = _node.Password;
outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null;
if (_node.Network == nameof(ETransport.tcp) && _node.HeaderType == Global.TcpHeaderHttp)
{
@ -269,7 +270,7 @@ public partial class CoreConfigSingboxService
{
outbound.uuid = _node.Username;
outbound.password = _node.Password;
outbound.congestion_control = _node.HeaderType;
outbound.congestion_control = protocolExtra.CongestionControl;
break;
}
case EConfigType.Anytls:

View file

@ -153,6 +153,7 @@ public partial class CoreConfigV2rayService
serversItem.password = _node.Password;
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod)
? protocolExtra.SsMethod : "none";
serversItem.uot = protocolExtra.Uot == true ? true : null;
serversItem.ota = false;
serversItem.level = 1;

View file

@ -61,6 +61,12 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public int WgMtu { get; set; }
[Reactive]
public bool Uot { get; set; }
[Reactive]
public string CongestionControl { get; set; }
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
@ -123,6 +129,8 @@ public class AddServerViewModel : MyReactiveObject
WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty;
WgReserved = protocolExtra?.WgReserved ?? string.Empty;
WgMtu = protocolExtra?.WgMtu ?? 1280;
Uot = protocolExtra?.Uot ?? false;
CongestionControl = protocolExtra?.CongestionControl ?? string.Empty;
}
private async Task SaveServerAsync()
@ -185,6 +193,8 @@ public class AddServerViewModel : MyReactiveObject
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
WgReserved = WgReserved.NullIfEmpty(),
WgMtu = WgMtu >= 576 ? WgMtu : null,
Uot = Uot ? true : null,
CongestionControl = CongestionControl.NullIfEmpty(),
});
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)

View file

@ -197,6 +197,19 @@
Width="300"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleSwitch
x:Name="togUotEnabled3"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Column="0"

View file

@ -122,6 +122,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.Shadowsocks:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
break;
@ -156,7 +157,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.WireGuard:

View file

@ -277,6 +277,20 @@
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleButton
x:Name="togUotEnabled3"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Column="0"

View file

@ -117,6 +117,7 @@ public partial class AddServerWindow
case EConfigType.Shadowsocks:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
break;
@ -151,7 +152,7 @@ public partial class AddServerWindow
case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
break;
case EConfigType.WireGuard: