mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Thu Dec 12 11:41:03 CET 2024
This commit is contained in:
@@ -848,3 +848,4 @@ Update On Wed Dec 4 19:40:09 CET 2024
|
||||
Update On Thu Dec 5 19:36:56 CET 2024
|
||||
Update On Fri Dec 6 19:37:02 CET 2024
|
||||
Update On Sat Dec 7 19:36:20 CET 2024
|
||||
Update On Thu Dec 12 11:40:54 CET 2024
|
||||
|
||||
@@ -366,3 +366,11 @@ MigrationBackup/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# debug log
|
||||
debug_*.json
|
||||
|
||||
# dotnet run in `BBDown/` sub folder
|
||||
/BBDown/*.mp4
|
||||
/BBDown/*.xml
|
||||
/BBDown/*.ass
|
||||
|
||||
@@ -9,6 +9,7 @@ using static BBDown.Core.Util.SubUtil;
|
||||
using static BBDown.Core.Logger;
|
||||
using System.IO;
|
||||
using BBDown.Core;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BBDown;
|
||||
|
||||
@@ -96,7 +97,7 @@ static partial class BBDownMuxer
|
||||
return RunExe(MP4BOX, arguments, MP4BOX != "mp4box");
|
||||
}
|
||||
|
||||
public static int MuxAV(bool useMp4box, string bvid, string videoPath, string audioPath, List<AudioMaterial> audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List<Subtitle>? subs = null, bool audioOnly = false, bool videoOnly = false, List<ViewPoint>? points = null, long pubTime = 0, bool simplyMux = false)
|
||||
public static int MuxAV(bool useMp4box, string bvid, string videoPath, string audioPath, List<AudioMaterial> audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List<Subtitle>? subs = null, bool audioOnly = false, bool videoOnly = false, List<ViewPoint>? points = null, long pubTime = 0, bool simplyMux = false, bool isHevc = false)
|
||||
{
|
||||
if (audioOnly && audioPath != "")
|
||||
videoPath = "";
|
||||
@@ -191,6 +192,8 @@ static partial class BBDownMuxer
|
||||
argsBuilder.Append("-c copy ");
|
||||
if (audioOnly && audioPath == "") argsBuilder.Append("-vn ");
|
||||
if (subs != null) argsBuilder.Append("-c:s mov_text ");
|
||||
// fix macOS hev1, see https://discussions.apple.com/thread/253081863?sortBy=rank
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && isHevc) argsBuilder.Append("-tag:v:0 hvc1 ");
|
||||
argsBuilder.Append($"-movflags faststart -strict unofficial -strict -2 -f mp4 -- \"{outPath}\"");
|
||||
|
||||
string arguments = argsBuilder.ToString();
|
||||
|
||||
@@ -669,6 +669,8 @@ partial class Program
|
||||
Log($"开始合并音视频{(subtitleInfo.Any() ? "和字幕" : "")}...");
|
||||
if (myOption.AudioOnly)
|
||||
savePath = savePath[..^4] + ".m4a";
|
||||
|
||||
var isHevc = selectedVideo?.codecs == "HEVC";
|
||||
int code = BBDownMuxer.MuxAV(myOption.UseMP4box, p.bvid, videoPath, audioPath, audioMaterial, savePath,
|
||||
desc,
|
||||
title,
|
||||
@@ -676,7 +678,7 @@ partial class Program
|
||||
(pagesCount > 1 || (bangumi && !vInfo.IsBangumiEnd)) ? p.title : "",
|
||||
File.Exists(coverPath) ? coverPath : "",
|
||||
lang,
|
||||
subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux);
|
||||
subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux, isHevc);
|
||||
if (code != 0 || !File.Exists(savePath) || new FileInfo(savePath).Length == 0)
|
||||
{
|
||||
LogError("合并失败"); return;
|
||||
|
||||
@@ -53,7 +53,7 @@ func main() {
|
||||
df := func() {}
|
||||
app := cli.NewApp()
|
||||
app.Name = "Brook"
|
||||
app.Version = "20240606"
|
||||
app.Version = "20250202"
|
||||
app.Usage = "A cross-platform programmable network tool"
|
||||
app.Authors = []*cli.Author{
|
||||
{
|
||||
|
||||
+15
-13
@@ -1,6 +1,8 @@
|
||||
module github.com/txthinking/brook
|
||||
|
||||
go 1.21.5
|
||||
go 1.22
|
||||
|
||||
toolchain go1.23.0
|
||||
|
||||
require (
|
||||
github.com/gorilla/mux v1.8.1
|
||||
@@ -9,8 +11,8 @@ require (
|
||||
github.com/miekg/dns v1.1.57
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/phuslu/iploc v1.0.20240501
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/quic-go/quic-go v0.42.0
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/quic-go/quic-go v0.48.2
|
||||
github.com/refraction-networking/utls v1.5.4
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/txthinking/runnergroup v0.0.0-20230325130830-408dc5853f86
|
||||
@@ -18,8 +20,8 @@ require (
|
||||
github.com/txthinking/x v0.0.0-20240301021728-6f68aba84c87
|
||||
github.com/urfave/cli/v2 v2.25.7
|
||||
github.com/urfave/negroni v1.0.0
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/net v0.28.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -35,18 +37,18 @@ require (
|
||||
github.com/klauspost/compress v1.16.7 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.11.1 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.10 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
)
|
||||
|
||||
@@ -57,14 +57,24 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
|
||||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
|
||||
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
||||
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o=
|
||||
github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
@@ -72,6 +82,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
||||
@@ -100,12 +111,18 @@ golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
@@ -114,12 +131,15 @@ golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -131,6 +151,8 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
@@ -140,6 +162,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -148,6 +172,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "20240606",
|
||||
"text": "brook-store: an open source store for some people",
|
||||
"link": "https://github.com/TxThinkingInc/brook-store",
|
||||
"text_zh": "YouTube: 为什么当我们谈到种族歧视时,普遍被歧视的是指黑色或黄色人种?",
|
||||
"link_zh": "https://www.youtube.com/watch?v=7T_jwMGuZ_I"
|
||||
"text": "Zhi - A Truely End-to-End Encrypted Instant Messaging App",
|
||||
"link": "https://www.txthinking.com/zhi.html",
|
||||
"text_zh": "纸,一个真正的端到端加密的聊天应用",
|
||||
"link_zh": "https://www.txthinking.com/zhi.html"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
mieruclient "github.com/enfein/mieru/v3/apis/client"
|
||||
mierumodel "github.com/enfein/mieru/v3/apis/model"
|
||||
mierupb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/proxydialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type Mieru struct {
|
||||
*Base
|
||||
option *MieruOption
|
||||
client mieruclient.Client
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
type MieruOption struct {
|
||||
BasicOption
|
||||
Name string `proxy:"name"`
|
||||
Server string `proxy:"server"`
|
||||
Port int `proxy:"port,omitempty"`
|
||||
PortRange string `proxy:"port-range,omitempty"`
|
||||
Transport string `proxy:"transport"`
|
||||
UserName string `proxy:"username"`
|
||||
Password string `proxy:"password"`
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
func (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
if err := m.ensureClientIsRunning(opts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr := metadataToMieruNetAddrSpec(metadata)
|
||||
c, err := m.client.DialContext(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial to %s failed: %w", addr, err)
|
||||
}
|
||||
return NewConn(c, m), nil
|
||||
}
|
||||
|
||||
// ProxyInfo implements C.ProxyAdapter
|
||||
func (m *Mieru) ProxyInfo() C.ProxyInfo {
|
||||
info := m.Base.ProxyInfo()
|
||||
info.DialerProxy = m.option.DialerProxy
|
||||
return info
|
||||
}
|
||||
|
||||
func (m *Mieru) ensureClientIsRunning(opts ...dialer.Option) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.client.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a dialer and add it to the client config, before starting the client.
|
||||
var dialer C.Dialer = dialer.NewDialer(m.Base.DialOptions(opts...)...)
|
||||
var err error
|
||||
if len(m.option.DialerProxy) > 0 {
|
||||
dialer, err = proxydialer.NewByName(m.option.DialerProxy, dialer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
config, err := m.client.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Dialer = dialer
|
||||
if err := m.client.Store(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.client.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start mieru client: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewMieru(option MieruOption) (*Mieru, error) {
|
||||
config, err := buildMieruClientConfig(option)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build mieru client config: %w", err)
|
||||
}
|
||||
c := mieruclient.NewClient()
|
||||
if err := c.Store(config); err != nil {
|
||||
return nil, fmt.Errorf("failed to store mieru client config: %w", err)
|
||||
}
|
||||
// Client is started lazily on the first use.
|
||||
|
||||
var addr string
|
||||
if option.Port != 0 {
|
||||
addr = net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
} else {
|
||||
beginPort, _, _ := beginAndEndPortFromPortRange(option.PortRange)
|
||||
addr = net.JoinHostPort(option.Server, strconv.Itoa(beginPort))
|
||||
}
|
||||
outbound := &Mieru{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
iface: option.Interface,
|
||||
tp: C.Mieru,
|
||||
udp: false,
|
||||
xudp: false,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
option: &option,
|
||||
client: c,
|
||||
}
|
||||
runtime.SetFinalizer(outbound, closeMieru)
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func closeMieru(m *Mieru) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.client != nil && m.client.IsRunning() {
|
||||
m.client.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func metadataToMieruNetAddrSpec(metadata *C.Metadata) mierumodel.NetAddrSpec {
|
||||
if metadata.Host != "" {
|
||||
return mierumodel.NetAddrSpec{
|
||||
AddrSpec: mierumodel.AddrSpec{
|
||||
FQDN: metadata.Host,
|
||||
Port: int(metadata.DstPort),
|
||||
},
|
||||
Net: "tcp",
|
||||
}
|
||||
} else {
|
||||
return mierumodel.NetAddrSpec{
|
||||
AddrSpec: mierumodel.AddrSpec{
|
||||
IP: metadata.DstIP.AsSlice(),
|
||||
Port: int(metadata.DstPort),
|
||||
},
|
||||
Net: "tcp",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildMieruClientConfig(option MieruOption) (*mieruclient.ClientConfig, error) {
|
||||
if err := validateMieruOption(option); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate mieru option: %w", err)
|
||||
}
|
||||
|
||||
transportProtocol := mierupb.TransportProtocol_TCP.Enum()
|
||||
var server *mierupb.ServerEndpoint
|
||||
if net.ParseIP(option.Server) != nil {
|
||||
// server is an IP address
|
||||
if option.PortRange != "" {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
IpAddress: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
PortRange: proto.String(option.PortRange),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
IpAddress: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
Port: proto.Int32(int32(option.Port)),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// server is a domain name
|
||||
if option.PortRange != "" {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
DomainName: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
PortRange: proto.String(option.PortRange),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
DomainName: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
Port: proto.Int32(int32(option.Port)),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return &mieruclient.ClientConfig{
|
||||
Profile: &mierupb.ClientProfile{
|
||||
ProfileName: proto.String(option.Name),
|
||||
User: &mierupb.User{
|
||||
Name: proto.String(option.UserName),
|
||||
Password: proto.String(option.Password),
|
||||
},
|
||||
Servers: []*mierupb.ServerEndpoint{server},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateMieruOption(option MieruOption) error {
|
||||
if option.Name == "" {
|
||||
return fmt.Errorf("name is empty")
|
||||
}
|
||||
if option.Server == "" {
|
||||
return fmt.Errorf("server is empty")
|
||||
}
|
||||
if option.Port == 0 && option.PortRange == "" {
|
||||
return fmt.Errorf("either port or port-range must be set")
|
||||
}
|
||||
if option.Port != 0 && option.PortRange != "" {
|
||||
return fmt.Errorf("port and port-range cannot be set at the same time")
|
||||
}
|
||||
if option.Port != 0 && (option.Port < 1 || option.Port > 65535) {
|
||||
return fmt.Errorf("port must be between 1 and 65535")
|
||||
}
|
||||
if option.PortRange != "" {
|
||||
begin, end, err := beginAndEndPortFromPortRange(option.PortRange)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid port-range format")
|
||||
}
|
||||
if begin < 1 || begin > 65535 {
|
||||
return fmt.Errorf("begin port must be between 1 and 65535")
|
||||
}
|
||||
if end < 1 || end > 65535 {
|
||||
return fmt.Errorf("end port must be between 1 and 65535")
|
||||
}
|
||||
if begin > end {
|
||||
return fmt.Errorf("begin port must be less than or equal to end port")
|
||||
}
|
||||
}
|
||||
|
||||
if option.Transport != "TCP" {
|
||||
return fmt.Errorf("transport must be TCP")
|
||||
}
|
||||
if option.UserName == "" {
|
||||
return fmt.Errorf("username is empty")
|
||||
}
|
||||
if option.Password == "" {
|
||||
return fmt.Errorf("password is empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func beginAndEndPortFromPortRange(portRange string) (int, int, error) {
|
||||
var begin, end int
|
||||
_, err := fmt.Sscanf(portRange, "%d-%d", &begin, &end)
|
||||
return begin, end, err
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package outbound
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNewMieru(t *testing.T) {
|
||||
testCases := []struct {
|
||||
option MieruOption
|
||||
wantBaseAddr string
|
||||
}{
|
||||
{
|
||||
option: MieruOption{
|
||||
Name: "test",
|
||||
Server: "1.2.3.4",
|
||||
Port: 10000,
|
||||
Transport: "TCP",
|
||||
UserName: "test",
|
||||
Password: "test",
|
||||
},
|
||||
wantBaseAddr: "1.2.3.4:10000",
|
||||
},
|
||||
{
|
||||
option: MieruOption{
|
||||
Name: "test",
|
||||
Server: "2001:db8::1",
|
||||
PortRange: "10001-10002",
|
||||
Transport: "TCP",
|
||||
UserName: "test",
|
||||
Password: "test",
|
||||
},
|
||||
wantBaseAddr: "[2001:db8::1]:10001",
|
||||
},
|
||||
{
|
||||
option: MieruOption{
|
||||
Name: "test",
|
||||
Server: "example.com",
|
||||
Port: 10003,
|
||||
Transport: "TCP",
|
||||
UserName: "test",
|
||||
Password: "test",
|
||||
},
|
||||
wantBaseAddr: "example.com:10003",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
mieru, err := NewMieru(testCase.option)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if mieru.addr != testCase.wantBaseAddr {
|
||||
t.Errorf("got addr %q, want %q", mieru.addr, testCase.wantBaseAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeginAndEndPortFromPortRange(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
begin int
|
||||
end int
|
||||
hasErr bool
|
||||
}{
|
||||
{"1-10", 1, 10, false},
|
||||
{"1000-2000", 1000, 2000, false},
|
||||
{"65535-65535", 65535, 65535, false},
|
||||
{"1", 0, 0, true},
|
||||
{"1-", 0, 0, true},
|
||||
{"-10", 0, 0, true},
|
||||
{"a-b", 0, 0, true},
|
||||
{"1-b", 0, 0, true},
|
||||
{"a-10", 0, 0, true},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
begin, end, err := beginAndEndPortFromPortRange(testCase.input)
|
||||
if testCase.hasErr {
|
||||
if err == nil {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) should return an error", testCase.input)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) should not return an error, but got %v", testCase.input, err)
|
||||
}
|
||||
if begin != testCase.begin {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) begin port mismatch, got %d, want %d", testCase.input, begin, testCase.begin)
|
||||
}
|
||||
if end != testCase.end {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) end port mismatch, got %d, want %d", testCase.input, end, testCase.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
||||
break
|
||||
}
|
||||
proxy, err = outbound.NewSsh(*sshOption)
|
||||
case "mieru":
|
||||
mieruOption := &outbound.MieruOption{}
|
||||
err = decoder.Decode(mapping, mieruOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = outbound.NewMieru(*mieruOption)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ const (
|
||||
WireGuard
|
||||
Tuic
|
||||
Ssh
|
||||
Mieru
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -226,7 +227,8 @@ func (at AdapterType) String() string {
|
||||
return "Tuic"
|
||||
case Ssh:
|
||||
return "Ssh"
|
||||
|
||||
case Mieru:
|
||||
return "Mieru"
|
||||
case Relay:
|
||||
return "Relay"
|
||||
case Selector:
|
||||
|
||||
@@ -846,6 +846,16 @@ proxies: # socks5
|
||||
password: password
|
||||
privateKey: path
|
||||
|
||||
# mieru
|
||||
- name: mieru
|
||||
type: mieru
|
||||
server: 1.2.3.4
|
||||
port: 2999
|
||||
# port-range: 2090-2099 #(不可同时填写 port 和 port-range)
|
||||
transport: TCP # 只支持 TCP
|
||||
username: user
|
||||
password: password
|
||||
|
||||
# dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理
|
||||
- name: "dns-out"
|
||||
type: dns
|
||||
@@ -1202,4 +1212,4 @@ listeners:
|
||||
# authentication-timeout: 1000
|
||||
# alpn:
|
||||
# - h3
|
||||
# max-udp-relay-packet-size: 1500
|
||||
# max-udp-relay-packet-size: 1500
|
||||
|
||||
@@ -7,6 +7,7 @@ require (
|
||||
github.com/bahlo/generic-list-go v0.2.0
|
||||
github.com/coreos/go-iptables v0.8.0
|
||||
github.com/dlclark/regexp2 v1.11.4
|
||||
github.com/enfein/mieru/v3 v3.8.3
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gobwas/ws v1.4.0
|
||||
@@ -114,6 +115,8 @@ require (
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect
|
||||
google.golang.org/grpc v1.64.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000
|
||||
|
||||
@@ -27,6 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/enfein/mieru/v3 v3.8.3 h1:s4K0hMFDg6LHltokR8/nBTVCq15XnnxPsvc1LrHwpoo=
|
||||
github.com/enfein/mieru/v3 v3.8.3/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||
@@ -59,7 +61,7 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr
|
||||
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
||||
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -274,6 +276,10 @@ golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -15,6 +15,7 @@ require (
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/coreos/go-iptables v0.8.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/enfein/mieru/v3 v3.8.3 // indirect
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
|
||||
@@ -106,6 +107,8 @@ require (
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect
|
||||
google.golang.org/grpc v1.64.1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
|
||||
@@ -26,6 +26,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/enfein/mieru/v3 v3.8.3 h1:s4K0hMFDg6LHltokR8/nBTVCq15XnnxPsvc1LrHwpoo=
|
||||
github.com/enfein/mieru/v3 v3.8.3/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||
@@ -58,7 +60,7 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr
|
||||
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
||||
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -267,6 +269,10 @@ golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
@@ -23,6 +23,7 @@ require (
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/coreos/go-iptables v0.8.0 // indirect
|
||||
github.com/enfein/mieru/v3 v3.8.3 // indirect
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
|
||||
@@ -112,6 +113,8 @@ require (
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect
|
||||
google.golang.org/grpc v1.64.1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
@@ -26,6 +26,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/enfein/mieru/v3 v3.8.3 h1:s4K0hMFDg6LHltokR8/nBTVCq15XnnxPsvc1LrHwpoo=
|
||||
github.com/enfein/mieru/v3 v3.8.3/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||
@@ -58,7 +60,7 @@ github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakr
|
||||
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
|
||||
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@@ -267,6 +269,10 @@ golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/proxydialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
mieruclient "github.com/enfein/mieru/v3/apis/client"
|
||||
mierumodel "github.com/enfein/mieru/v3/apis/model"
|
||||
mierupb "github.com/enfein/mieru/v3/pkg/appctl/appctlpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type Mieru struct {
|
||||
*Base
|
||||
option *MieruOption
|
||||
client mieruclient.Client
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
type MieruOption struct {
|
||||
BasicOption
|
||||
Name string `proxy:"name"`
|
||||
Server string `proxy:"server"`
|
||||
Port int `proxy:"port,omitempty"`
|
||||
PortRange string `proxy:"port-range,omitempty"`
|
||||
Transport string `proxy:"transport"`
|
||||
UserName string `proxy:"username"`
|
||||
Password string `proxy:"password"`
|
||||
Multiplexing string `proxy:"multiplexing,omitempty"`
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
func (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
if err := m.ensureClientIsRunning(opts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr := metadataToMieruNetAddrSpec(metadata)
|
||||
c, err := m.client.DialContext(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial to %s failed: %w", addr, err)
|
||||
}
|
||||
return NewConn(c, m), nil
|
||||
}
|
||||
|
||||
// ProxyInfo implements C.ProxyAdapter
|
||||
func (m *Mieru) ProxyInfo() C.ProxyInfo {
|
||||
info := m.Base.ProxyInfo()
|
||||
info.DialerProxy = m.option.DialerProxy
|
||||
return info
|
||||
}
|
||||
|
||||
func (m *Mieru) ensureClientIsRunning(opts ...dialer.Option) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.client.IsRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a dialer and add it to the client config, before starting the client.
|
||||
var dialer C.Dialer = dialer.NewDialer(m.Base.DialOptions(opts...)...)
|
||||
var err error
|
||||
if len(m.option.DialerProxy) > 0 {
|
||||
dialer, err = proxydialer.NewByName(m.option.DialerProxy, dialer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
config, err := m.client.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Dialer = dialer
|
||||
if err := m.client.Store(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.client.Start(); err != nil {
|
||||
return fmt.Errorf("failed to start mieru client: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewMieru(option MieruOption) (*Mieru, error) {
|
||||
config, err := buildMieruClientConfig(option)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build mieru client config: %w", err)
|
||||
}
|
||||
c := mieruclient.NewClient()
|
||||
if err := c.Store(config); err != nil {
|
||||
return nil, fmt.Errorf("failed to store mieru client config: %w", err)
|
||||
}
|
||||
// Client is started lazily on the first use.
|
||||
|
||||
var addr string
|
||||
if option.Port != 0 {
|
||||
addr = net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
} else {
|
||||
beginPort, _, _ := beginAndEndPortFromPortRange(option.PortRange)
|
||||
addr = net.JoinHostPort(option.Server, strconv.Itoa(beginPort))
|
||||
}
|
||||
outbound := &Mieru{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
iface: option.Interface,
|
||||
tp: C.Mieru,
|
||||
udp: false,
|
||||
xudp: false,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
option: &option,
|
||||
client: c,
|
||||
}
|
||||
runtime.SetFinalizer(outbound, closeMieru)
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func closeMieru(m *Mieru) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.client != nil && m.client.IsRunning() {
|
||||
m.client.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func metadataToMieruNetAddrSpec(metadata *C.Metadata) mierumodel.NetAddrSpec {
|
||||
if metadata.Host != "" {
|
||||
return mierumodel.NetAddrSpec{
|
||||
AddrSpec: mierumodel.AddrSpec{
|
||||
FQDN: metadata.Host,
|
||||
Port: int(metadata.DstPort),
|
||||
},
|
||||
Net: "tcp",
|
||||
}
|
||||
} else {
|
||||
return mierumodel.NetAddrSpec{
|
||||
AddrSpec: mierumodel.AddrSpec{
|
||||
IP: metadata.DstIP.AsSlice(),
|
||||
Port: int(metadata.DstPort),
|
||||
},
|
||||
Net: "tcp",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildMieruClientConfig(option MieruOption) (*mieruclient.ClientConfig, error) {
|
||||
if err := validateMieruOption(option); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate mieru option: %w", err)
|
||||
}
|
||||
|
||||
transportProtocol := mierupb.TransportProtocol_TCP.Enum()
|
||||
var server *mierupb.ServerEndpoint
|
||||
if net.ParseIP(option.Server) != nil {
|
||||
// server is an IP address
|
||||
if option.PortRange != "" {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
IpAddress: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
PortRange: proto.String(option.PortRange),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
IpAddress: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
Port: proto.Int32(int32(option.Port)),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// server is a domain name
|
||||
if option.PortRange != "" {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
DomainName: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
PortRange: proto.String(option.PortRange),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server = &mierupb.ServerEndpoint{
|
||||
DomainName: proto.String(option.Server),
|
||||
PortBindings: []*mierupb.PortBinding{
|
||||
{
|
||||
Port: proto.Int32(int32(option.Port)),
|
||||
Protocol: transportProtocol,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
config := &mieruclient.ClientConfig{
|
||||
Profile: &mierupb.ClientProfile{
|
||||
ProfileName: proto.String(option.Name),
|
||||
User: &mierupb.User{
|
||||
Name: proto.String(option.UserName),
|
||||
Password: proto.String(option.Password),
|
||||
},
|
||||
Servers: []*mierupb.ServerEndpoint{server},
|
||||
},
|
||||
}
|
||||
if multiplexing, ok := mierupb.MultiplexingLevel_value[option.Multiplexing]; ok {
|
||||
config.Profile.Multiplexing = &mierupb.MultiplexingConfig{
|
||||
Level: mierupb.MultiplexingLevel(multiplexing).Enum(),
|
||||
}
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func validateMieruOption(option MieruOption) error {
|
||||
if option.Name == "" {
|
||||
return fmt.Errorf("name is empty")
|
||||
}
|
||||
if option.Server == "" {
|
||||
return fmt.Errorf("server is empty")
|
||||
}
|
||||
if option.Port == 0 && option.PortRange == "" {
|
||||
return fmt.Errorf("either port or port-range must be set")
|
||||
}
|
||||
if option.Port != 0 && option.PortRange != "" {
|
||||
return fmt.Errorf("port and port-range cannot be set at the same time")
|
||||
}
|
||||
if option.Port != 0 && (option.Port < 1 || option.Port > 65535) {
|
||||
return fmt.Errorf("port must be between 1 and 65535")
|
||||
}
|
||||
if option.PortRange != "" {
|
||||
begin, end, err := beginAndEndPortFromPortRange(option.PortRange)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid port-range format")
|
||||
}
|
||||
if begin < 1 || begin > 65535 {
|
||||
return fmt.Errorf("begin port must be between 1 and 65535")
|
||||
}
|
||||
if end < 1 || end > 65535 {
|
||||
return fmt.Errorf("end port must be between 1 and 65535")
|
||||
}
|
||||
if begin > end {
|
||||
return fmt.Errorf("begin port must be less than or equal to end port")
|
||||
}
|
||||
}
|
||||
|
||||
if option.Transport != "TCP" {
|
||||
return fmt.Errorf("transport must be TCP")
|
||||
}
|
||||
if option.UserName == "" {
|
||||
return fmt.Errorf("username is empty")
|
||||
}
|
||||
if option.Password == "" {
|
||||
return fmt.Errorf("password is empty")
|
||||
}
|
||||
if option.Multiplexing != "" {
|
||||
if _, ok := mierupb.MultiplexingLevel_value[option.Multiplexing]; !ok {
|
||||
return fmt.Errorf("invalid multiplexing level: %s", option.Multiplexing)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func beginAndEndPortFromPortRange(portRange string) (int, int, error) {
|
||||
var begin, end int
|
||||
_, err := fmt.Sscanf(portRange, "%d-%d", &begin, &end)
|
||||
return begin, end, err
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package outbound
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNewMieru(t *testing.T) {
|
||||
testCases := []struct {
|
||||
option MieruOption
|
||||
wantBaseAddr string
|
||||
}{
|
||||
{
|
||||
option: MieruOption{
|
||||
Name: "test",
|
||||
Server: "1.2.3.4",
|
||||
Port: 10000,
|
||||
Transport: "TCP",
|
||||
UserName: "test",
|
||||
Password: "test",
|
||||
},
|
||||
wantBaseAddr: "1.2.3.4:10000",
|
||||
},
|
||||
{
|
||||
option: MieruOption{
|
||||
Name: "test",
|
||||
Server: "2001:db8::1",
|
||||
PortRange: "10001-10002",
|
||||
Transport: "TCP",
|
||||
UserName: "test",
|
||||
Password: "test",
|
||||
},
|
||||
wantBaseAddr: "[2001:db8::1]:10001",
|
||||
},
|
||||
{
|
||||
option: MieruOption{
|
||||
Name: "test",
|
||||
Server: "example.com",
|
||||
Port: 10003,
|
||||
Transport: "TCP",
|
||||
UserName: "test",
|
||||
Password: "test",
|
||||
},
|
||||
wantBaseAddr: "example.com:10003",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
mieru, err := NewMieru(testCase.option)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if mieru.addr != testCase.wantBaseAddr {
|
||||
t.Errorf("got addr %q, want %q", mieru.addr, testCase.wantBaseAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeginAndEndPortFromPortRange(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
begin int
|
||||
end int
|
||||
hasErr bool
|
||||
}{
|
||||
{"1-10", 1, 10, false},
|
||||
{"1000-2000", 1000, 2000, false},
|
||||
{"65535-65535", 65535, 65535, false},
|
||||
{"1", 0, 0, true},
|
||||
{"1-", 0, 0, true},
|
||||
{"-10", 0, 0, true},
|
||||
{"a-b", 0, 0, true},
|
||||
{"1-b", 0, 0, true},
|
||||
{"a-10", 0, 0, true},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
begin, end, err := beginAndEndPortFromPortRange(testCase.input)
|
||||
if testCase.hasErr {
|
||||
if err == nil {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) should return an error", testCase.input)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) should not return an error, but got %v", testCase.input, err)
|
||||
}
|
||||
if begin != testCase.begin {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) begin port mismatch, got %d, want %d", testCase.input, begin, testCase.begin)
|
||||
}
|
||||
if end != testCase.end {
|
||||
t.Errorf("beginAndEndPortFromPortRange(%s) end port mismatch, got %d, want %d", testCase.input, end, testCase.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
||||
break
|
||||
}
|
||||
proxy, err = outbound.NewSsh(*sshOption)
|
||||
case "mieru":
|
||||
mieruOption := &outbound.MieruOption{}
|
||||
err = decoder.Decode(mapping, mieruOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = outbound.NewMieru(*mieruOption)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (sd *Dispatcher) Enable() bool {
|
||||
|
||||
func (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) {
|
||||
for s := range sd.sniffers {
|
||||
if s.SupportNetwork() == C.TCP {
|
||||
if s.SupportNetwork() == C.TCP && s.SupportPort(metadata.DstPort) {
|
||||
_ = conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
_, err := conn.Peek(1)
|
||||
_ = conn.SetReadDeadline(time.Time{})
|
||||
|
||||
@@ -42,6 +42,7 @@ const (
|
||||
WireGuard
|
||||
Tuic
|
||||
Ssh
|
||||
Mieru
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -226,7 +227,8 @@ func (at AdapterType) String() string {
|
||||
return "Tuic"
|
||||
case Ssh:
|
||||
return "Ssh"
|
||||
|
||||
case Mieru:
|
||||
return "Mieru"
|
||||
case Relay:
|
||||
return "Relay"
|
||||
case Selector:
|
||||
|
||||
@@ -846,6 +846,18 @@ proxies: # socks5
|
||||
password: password
|
||||
privateKey: path
|
||||
|
||||
# mieru
|
||||
- name: mieru
|
||||
type: mieru
|
||||
server: 1.2.3.4
|
||||
port: 2999
|
||||
# port-range: 2090-2099 #(不可同时填写 port 和 port-range)
|
||||
transport: TCP # 只支持 TCP
|
||||
username: user
|
||||
password: password
|
||||
# 可以使用的值包括 MULTIPLEXING_OFF, MULTIPLEXING_LOW, MULTIPLEXING_MIDDLE, MULTIPLEXING_HIGH。其中 MULTIPLEXING_OFF 会关闭多路复用功能。默认值为 MULTIPLEXING_LOW。
|
||||
# multiplexing: MULTIPLEXING_LOW
|
||||
|
||||
# dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理
|
||||
- name: "dns-out"
|
||||
type: dns
|
||||
@@ -1202,4 +1214,4 @@ listeners:
|
||||
# authentication-timeout: 1000
|
||||
# alpn:
|
||||
# - h3
|
||||
# max-udp-relay-packet-size: 1500
|
||||
# max-udp-relay-packet-size: 1500
|
||||
|
||||
@@ -7,6 +7,7 @@ require (
|
||||
github.com/bahlo/generic-list-go v0.2.0
|
||||
github.com/coreos/go-iptables v0.8.0
|
||||
github.com/dlclark/regexp2 v1.11.4
|
||||
github.com/enfein/mieru/v3 v3.8.4
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/gobwas/ws v1.4.0
|
||||
|
||||
@@ -27,6 +27,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/enfein/mieru/v3 v3.8.4 h1:PmBQykuEcl8yKcQ647pg8Qbjl433CRYgUbW6VLBgGn4=
|
||||
github.com/enfein/mieru/v3 v3.8.4/go.mod h1:YtU00qjAEt54mCBQu4WZPCey6cBdB1BUtXjvrHLEUNQ=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -99,6 +100,13 @@ func main() {
|
||||
log.Fatalln("Initial configuration error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
} else if configFile == "-" {
|
||||
var err error
|
||||
configBytes, err = io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatalln("Initial configuration error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if configFile != "" {
|
||||
if !filepath.IsAbs(configFile) {
|
||||
|
||||
Generated
+23
-23
@@ -683,7 +683,7 @@ version = "0.5.0"
|
||||
source = "git+https://github.com/libnyanpasu/auto-launch.git#729d5429dd689067047489af4a0a32f7013854c8"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"windows-registry 0.3.0",
|
||||
"windows-result 0.2.0",
|
||||
]
|
||||
@@ -1397,9 +1397,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
@@ -1573,7 +1573,7 @@ dependencies = [
|
||||
"tauri-plugin-updater",
|
||||
"tempfile",
|
||||
"test-log",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"timeago",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -6708,7 +6708,7 @@ dependencies = [
|
||||
"rustc-hash 2.1.0",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@@ -6727,7 +6727,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -7522,9 +7522,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -7552,9 +7552,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -8425,7 +8425,7 @@ dependencies = [
|
||||
"tauri-runtime",
|
||||
"tauri-runtime-wry",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"tokio",
|
||||
"tray-icon",
|
||||
"url",
|
||||
@@ -8478,7 +8478,7 @@ dependencies = [
|
||||
"sha2 0.10.8",
|
||||
"syn 2.0.90",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"time",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -8560,7 +8560,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -8581,7 +8581,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -8692,7 +8692,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
@@ -8714,7 +8714,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"url",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
@@ -8774,7 +8774,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"swift-rs",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"toml 0.8.19",
|
||||
"url",
|
||||
"urlpattern",
|
||||
@@ -8946,11 +8946,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.4"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490"
|
||||
checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.4",
|
||||
"thiserror-impl 2.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8966,9 +8966,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.4"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061"
|
||||
checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -11407,7 +11407,7 @@ dependencies = [
|
||||
"pbkdf2",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"thiserror 2.0.4",
|
||||
"thiserror 2.0.6",
|
||||
"time",
|
||||
"zeroize",
|
||||
"zopfli",
|
||||
|
||||
@@ -18,6 +18,6 @@
|
||||
"swr": "2.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "18.3.14"
|
||||
"@types/react": "18.3.16"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,15 @@
|
||||
"i18next": "24.0.5",
|
||||
"jotai": "2.10.3",
|
||||
"json-schema": "0.4.0",
|
||||
"material-react-table": "3.0.1",
|
||||
"monaco-editor": "0.52.0",
|
||||
"material-react-table": "3.0.2",
|
||||
"monaco-editor": "0.52.2",
|
||||
"mui-color-input": "5.0.1",
|
||||
"react": "rc",
|
||||
"react-dom": "rc",
|
||||
"react-error-boundary": "4.1.2",
|
||||
"react-fast-marquee": "1.6.5",
|
||||
"react-hook-form-mui": "7.4.1",
|
||||
"react-i18next": "15.1.3",
|
||||
"react-i18next": "15.1.4",
|
||||
"react-markdown": "9.0.1",
|
||||
"react-split-grid": "1.0.4",
|
||||
"react-use": "17.5.1",
|
||||
@@ -52,21 +52,21 @@
|
||||
"@csstools/normalize.css": "12.1.1",
|
||||
"@emotion/babel-plugin": "11.13.5",
|
||||
"@emotion/react": "11.13.5",
|
||||
"@iconify/json": "2.2.280",
|
||||
"@iconify/json": "2.2.281",
|
||||
"@monaco-editor/react": "4.6.0",
|
||||
"@tanstack/react-router": "1.86.1",
|
||||
"@tanstack/router-devtools": "1.86.1",
|
||||
"@tanstack/router-plugin": "1.86.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.0.1",
|
||||
"@tauri-apps/plugin-dialog": "2.0.1",
|
||||
"@tauri-apps/plugin-fs": "2.0.3",
|
||||
"@tauri-apps/plugin-notification": "2.0.0",
|
||||
"@tauri-apps/plugin-os": "2.0.0",
|
||||
"@tanstack/react-router": "1.87.9",
|
||||
"@tanstack/router-devtools": "1.87.9",
|
||||
"@tanstack/router-plugin": "1.87.7",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.2.0",
|
||||
"@tauri-apps/plugin-dialog": "2.2.0",
|
||||
"@tauri-apps/plugin-fs": "2.2.0",
|
||||
"@tauri-apps/plugin-notification": "2.2.0",
|
||||
"@tauri-apps/plugin-os": "2.2.0",
|
||||
"@tauri-apps/plugin-process": "2.0.0",
|
||||
"@tauri-apps/plugin-shell": "2.0.1",
|
||||
"@tauri-apps/plugin-updater": "2.0.0",
|
||||
"@types/react": "18.3.14",
|
||||
"@types/react-dom": "18.3.2",
|
||||
"@types/react": "18.3.16",
|
||||
"@types/react-dom": "18.3.5",
|
||||
"@types/validator": "13.12.2",
|
||||
"@vitejs/plugin-legacy": "6.0.0",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
@@ -79,7 +79,7 @@
|
||||
"monaco-yaml": "5.2.3",
|
||||
"nanoid": "5.0.9",
|
||||
"sass": "1.82.0",
|
||||
"shiki": "1.24.0",
|
||||
"shiki": "1.24.2",
|
||||
"tailwindcss-textshadow": "2.1.3",
|
||||
"unplugin-auto-import": "0.18.6",
|
||||
"unplugin-icons": "0.21.0",
|
||||
|
||||
@@ -1,183 +1,289 @@
|
||||
{
|
||||
"label_proxies": "прокси",
|
||||
"label_profiles": "профили",
|
||||
"label_connections": "связи",
|
||||
"label_logs": "журналы",
|
||||
"label_rules": "правила",
|
||||
"label_settings": "настройки",
|
||||
"label_providers": "поставщики",
|
||||
|
||||
"label_dashboard": "Панель управления",
|
||||
"label_proxies": "Прокси",
|
||||
"label_profiles": "Профили",
|
||||
"label_connections": "Соединения",
|
||||
"label_logs": "Журналы",
|
||||
"label_rules": "Правила",
|
||||
"label_settings": "Настройки",
|
||||
"label_providers": "Поставщики",
|
||||
"Connections": "Соединения",
|
||||
"Logs": "Логи",
|
||||
"Upload Traffic": "Отправка",
|
||||
"Download Traffic": "Загрузка",
|
||||
"Total": "Всего",
|
||||
"Memory": "Память",
|
||||
"Active Connections": "Активные соединения",
|
||||
"Timeout": "Тайм-аут",
|
||||
"Click to Refresh Now": "Нажмите для обновления",
|
||||
"No Proxy": "Без прокси",
|
||||
"Direct Mode": "Прямой режим",
|
||||
"Rules": "Правила",
|
||||
"No Rules": "Нет правил",
|
||||
"Logs": "Журналы",
|
||||
"No Logs": "Нет журналов",
|
||||
"Clear": "Очистить",
|
||||
"Proxies": "Прокси",
|
||||
"Proxy Groups": "Группы прокси",
|
||||
"rule": "правила",
|
||||
"rule": "правило",
|
||||
"global": "глобальный",
|
||||
"direct": "прямой",
|
||||
"script": "скриптовый",
|
||||
|
||||
"script": "скрипт",
|
||||
"Dashboard": "Панель управления",
|
||||
"Profiles": "Профили",
|
||||
"Profile URL": "URL профиля",
|
||||
"Import": "Импорт",
|
||||
"Download": "Загрузить",
|
||||
"Paste": "Вставить",
|
||||
"Import": "Импортировать",
|
||||
"New": "Новый",
|
||||
"Edit Profile": "Редактировать профиль",
|
||||
"Create Profile": "Создать профиль",
|
||||
"New Profile": "Новый профиль",
|
||||
"Choose File": "Выбрать файл",
|
||||
"Close All": "Закрыть всё",
|
||||
"Profile": "Профиль",
|
||||
"Close All": "Закрыть все",
|
||||
"Menu": "Меню",
|
||||
"Select": "Выбрать",
|
||||
"Edit Info": "Изменить информацию",
|
||||
"Applying Profile": "Применение профиля...",
|
||||
"Edit Info": "Редактировать информацию",
|
||||
"Proxy Chains": "Цепочки прокси",
|
||||
"Global Proxy Chains": "Глобальные цепочки прокси",
|
||||
"New Chain": "Новая цепочка",
|
||||
"New Script": "Новый скрипт",
|
||||
"Edit Script": "Редактировать скрипт",
|
||||
"Please fix the error before submitting": "Исправьте ошибку перед отправкой.",
|
||||
"Open": "Открыть",
|
||||
"Open File": "Открыть файл",
|
||||
"Update": "Обновить",
|
||||
"Update(Proxy)": "Обновить (прокси)",
|
||||
"Update(Proxy)": "Обновить(Прокси)",
|
||||
"Delete": "Удалить",
|
||||
"Enable": "Включить",
|
||||
"Disable": "Отключить",
|
||||
"Refresh": "Обновить",
|
||||
"To Top": "Наверх",
|
||||
"To End": "Вниз",
|
||||
"To Top": "На верх",
|
||||
"To End": "В конец",
|
||||
"Update All Profiles": "Обновить все профили",
|
||||
"View Runtime Config": "Просмотреть используемый конфиг",
|
||||
"View Runtime Config": "Посмотреть конфигурацию времени выполнения",
|
||||
"Reactivate Profiles": "Реактивировать профили",
|
||||
|
||||
"Location": "Местоположение",
|
||||
"Delay check": "Проверка задержки",
|
||||
"Sort by default": "Сортировать по умолчанию",
|
||||
"Sort by delay": "Сортировать по задержке",
|
||||
"Sort by name": "Сортировать по названию",
|
||||
"Sort by name": "Сортировать по имени",
|
||||
"Delay check URL": "URL проверки задержки",
|
||||
"Proxy detail": "Подробности о прокси",
|
||||
"Proxy detail": "Детали прокси",
|
||||
"Filter": "Фильтр",
|
||||
"Filter conditions": "Условия фильтрации",
|
||||
"Refresh profiles": "Обновить профили",
|
||||
|
||||
"Connection Columns": "Колонки соединения",
|
||||
"Actions": "Действия",
|
||||
"Host": "Хост",
|
||||
"Process": "Процесс",
|
||||
"Downloaded": "Загружено",
|
||||
"Uploaded": "Отправлено",
|
||||
"DL Speed": "Скорость загрузки",
|
||||
"UL Speed": "Скорость отправки",
|
||||
"Chains": "Цепочки",
|
||||
"Rule": "Правило",
|
||||
"Time": "Время",
|
||||
"Source": "Источник",
|
||||
"Destination IP": "IP назначения",
|
||||
"Destination ASN": "ASN назначения",
|
||||
"Type": "Тип",
|
||||
"Name": "Название",
|
||||
"Connection Detail": "Детали соединения",
|
||||
"Metadata": "Метаданные",
|
||||
"Remote": "Удаленный",
|
||||
"Local": "Локальный",
|
||||
"Remote Profile": "Удаленный профиль",
|
||||
"Local Profile": "Локальный профиль",
|
||||
"Name": "Имя",
|
||||
"Descriptions": "Описания",
|
||||
"Subscription URL": "URL подписки",
|
||||
"User Agent": "Пользовательский агент",
|
||||
"Update Interval": "Интервал обновления",
|
||||
|
||||
"Tray Icons": "Значки в трее",
|
||||
"Use System Proxy": "Использовать системный прокси",
|
||||
"Use Clash Proxy": "Использовать прокси Clash",
|
||||
"No Connections": "Нет соединений",
|
||||
"Tray Icons": "Иконки в трее",
|
||||
"Set": "Установить",
|
||||
"Edit": "Редактировать",
|
||||
"Reset": "Сбросить",
|
||||
"Hotkeys": "Горячие клавиши",
|
||||
"Feedback": "Обратная связь",
|
||||
"Settings": "Настройки",
|
||||
"Clash Setting": "Настройки Clash",
|
||||
"System Setting": "Настройки системы",
|
||||
"System Setting": "Системные настройки",
|
||||
"Nyanpasu Setting": "Настройки Nyanpasu",
|
||||
"Allow LAN": "Разрешить локальную сеть",
|
||||
"Allow LAN": "Разрешить LAN",
|
||||
"IPv6": "IPv6",
|
||||
"TUN Stack": "Туннель куча",
|
||||
"Log Level": "Уровень логов",
|
||||
"Clash Port": "Clash порт",
|
||||
"TUN Stack": "Стек TUN",
|
||||
"Log Level": "Уровень логирования",
|
||||
"Clash Port": "Порт Clash",
|
||||
"Mixed Port": "Смешанный порт",
|
||||
"Random Port": "Случайный порт",
|
||||
"After restart to take effect": "Чтобы изменения вступили в силу, необходимо перезапустить приложение",
|
||||
"After restart to take effect": "Вступит в силу после перезагрузки.",
|
||||
"Clash External Controll": "Внешнее управление Clash",
|
||||
"External Controller": "Внешний контроллер",
|
||||
"Port Strategy": "Стратегия порта",
|
||||
"Allow Fallback": "Разрешить откат",
|
||||
"Fixed": "Фиксированный",
|
||||
"Random": "Случайный",
|
||||
"Core Secret": "Секрет ядра",
|
||||
"Clash Core": "Ядро Clash",
|
||||
"TUN Mode": "Режим туннеля",
|
||||
"System Service": "Системная сервиса",
|
||||
"Service Mode": "Режим сервиса",
|
||||
"TUN Mode": "Режим TUN",
|
||||
"System Service": "Системная служба",
|
||||
"Service Mode": "Режим службы",
|
||||
"Initiating Behavior": "Инициирующее поведение",
|
||||
"Auto Start": "Автозапуск",
|
||||
"Silent Start": "Тихий запуск",
|
||||
"System Proxy": "Системный прокси",
|
||||
"Open UWP Tool": "Открыть инструмент UWP",
|
||||
"System Proxy Setting": "Настройка системного прокси",
|
||||
"Proxy Guard": "Защита прокси",
|
||||
"Guard Interval": "Период защиты",
|
||||
"Proxy Bypass": "Игнорирование прокси",
|
||||
"Proxy Guard": "Охрана прокси",
|
||||
"Guard Interval": "Интервал охраны",
|
||||
"The interval must be greater than 0 second": "Интервал должен быть больше 0 секунд.",
|
||||
"Proxy Bypass": "Обход прокси",
|
||||
"Apply": "Применить",
|
||||
"Current System Proxy": "Текущий системный прокси",
|
||||
"User Interface": "Пользовательский интерфейс",
|
||||
"Theme Mode": "Режим темы",
|
||||
"Open UWP Tool": "Открыть UWP инструмент",
|
||||
"Theme Mode": "Тематический режим",
|
||||
"Theme Blur": "Размытие темы",
|
||||
"Theme Setting": "Настройка темы",
|
||||
"Hotkey Setting": "Настройка клавиатурных сокращений",
|
||||
"Icon Navigation Bar": "Панель навигации иконок",
|
||||
"Layout Setting": "Настройка макета",
|
||||
"Miscellaneous": "Разное",
|
||||
"Hotkey Setting": "Настройка горячих клавиш",
|
||||
"Traffic Graph": "График трафика",
|
||||
"Page Transition Animation": "Анимация перехода страниц",
|
||||
"Memory Usage": "Использование памяти",
|
||||
"Page Transition Animation": "Анимация перехода страницы",
|
||||
"Page Transition Animation Slide": "Слайд",
|
||||
"Page Transition Animation Blur": "Размытие",
|
||||
"Page Transition Animation Transparent": "Прозрачность",
|
||||
"Page Transition Animation None": "Нет",
|
||||
"Page Transition Animation None": "Без анимации",
|
||||
"Language": "Язык",
|
||||
"Open Config Dir": "Открыть папку конфигурации",
|
||||
"Open Data Dir": "Открыть папку данных",
|
||||
"Open Core Dir": "Открыть папку ядра",
|
||||
"Open Logs Dir": "Открыть папку логов",
|
||||
"Path Config": "Конфигурация пути",
|
||||
"Migrate App Path": "Перенести путь приложения",
|
||||
"Open Config Dir": "Открыть каталог конфигурации",
|
||||
"Open Data Dir": "Открыть каталог данных",
|
||||
"Open Core Dir": "Открыть каталог ядра",
|
||||
"Open Logs Dir": "Открыть каталог журналов",
|
||||
"Auto Check Updates": "Автоматическая проверка обновлений",
|
||||
"Check for Updates": "Проверить обновления",
|
||||
"Nyanpasu Version": "Версия Nyanpasu",
|
||||
"theme.light": "Светлая",
|
||||
"theme.dark": "Тёмная",
|
||||
"theme.dark": "Темная",
|
||||
"theme.system": "Системная",
|
||||
"Clash Field": "Используемые настройки Clash",
|
||||
"Runtime Config": "Используемый конфиг",
|
||||
"ReadOnly": "Только для чтения",
|
||||
"Clash Field": "Поле Clash",
|
||||
"Original Config": "Исходная конфигурация",
|
||||
"Runtime Config": "Конфигурация времени выполнения",
|
||||
"Console": "Консоль",
|
||||
"ReadOnly": "Только чтение",
|
||||
"Check Updates": "Проверить обновления",
|
||||
"Restart": "Перезапуск",
|
||||
|
||||
"Restart": "Перезапустить",
|
||||
"Update Core": "Обновить ядро",
|
||||
"Tasks": "Задачи",
|
||||
"Auto Log Clean": "Автоочистка логов",
|
||||
"Never Clean": "Никогда не чистить",
|
||||
"Retain 3 Days": "Сохранять 3 дня",
|
||||
"Retain 7 Days": "Сохранять 7 дней",
|
||||
"Retain 30 Days": "Сохранять 30 дней",
|
||||
"Retain 90 Days": "Сохранять 90 дней",
|
||||
"Max Log Files": "Максимальное количество файлов логов",
|
||||
|
||||
"Collect Logs": "Собрать логи",
|
||||
|
||||
"Auto Log Clean": "Автоматическая очистка журналов",
|
||||
"Never Clean": "Никогда не очищать",
|
||||
"Retain 3 Days": "Хранить 3 дня",
|
||||
"Retain 7 Days": "Хранить 7 дней",
|
||||
"Retain 30 Days": "Хранить 30 дней",
|
||||
"Retain 90 Days": "Хранить 90 дней",
|
||||
"Max Log Files": "Максимум файлов журнала",
|
||||
"Collect Logs": "Собрать журналы",
|
||||
"Back": "Назад",
|
||||
"Save": "Сохранить",
|
||||
"Cancel": "Отмена",
|
||||
|
||||
"open_or_close_dashboard": "Открыть/закрыть панель управления",
|
||||
"Default": "По умолчанию",
|
||||
"Download Speed": "Скорость загрузки",
|
||||
"Upload Speed": "Скорость отправки",
|
||||
"open_or_close_dashboard": "Открыть/Закрыть панель управления",
|
||||
"clash_mode_rule": "Режим правил",
|
||||
"clash_mode_global": "Глобальный режим",
|
||||
"clash_mode_direct": "Прямой режим",
|
||||
"clash_mode_script": "Скриптовый режим",
|
||||
"toggle_system_proxy": "Переключить режим системного прокси",
|
||||
"clash_mode_script": "Режим скрипта",
|
||||
"toggle_system_proxy": "Переключить системный прокси",
|
||||
"enable_system_proxy": "Включить системный прокси",
|
||||
"disable_system_proxy": "Отключить системный прокси",
|
||||
"toggle_tun_mode": "Переключить режим туннеля",
|
||||
"enable_tun_mode": "Включить режим туннеля",
|
||||
"disable_tun_mode": "Отключить режим туннеля",
|
||||
|
||||
"toggle_tun_mode": "Переключить режим TUN",
|
||||
"enable_tun_mode": "Включить режим TUN",
|
||||
"disable_tun_mode": "Отключить режим TUN",
|
||||
"App Log Level": "Уровень журнала приложения",
|
||||
"Auto Close Connections": "Автоматическое закрытие соединений",
|
||||
"Enable Clash Fields Filter": "Включить фильтр полей Clash",
|
||||
"Enable Builtin Enhanced": "Включить встроенное улучшение",
|
||||
"Proxy Layout Column": "Столбец макета прокси",
|
||||
"Default Latency Test": "Тест задержки по умолчанию",
|
||||
"Error": "Ошибка",
|
||||
"Successful": "Успех",
|
||||
|
||||
"Providers": "Провайдеры",
|
||||
"Rules Providers": "Провайдеры правил",
|
||||
"Update All Rules Providers": "Обновить все провайдеры правил",
|
||||
"Rule Set rules": "{{rule}} правила",
|
||||
"Last Update": "Последнее обновление {{fromNow}}",
|
||||
"Successfully Updated Rules Providers": "Провайдеры правил успешно обновлены",
|
||||
|
||||
"Portable Update Error": "Обновление портативной версии не поддерживается",
|
||||
"Tray Proxies Selector": "Выбор прокси в трее",
|
||||
"Hidden": "Скрыть",
|
||||
"Normal": "Обычный",
|
||||
"Successful": "Успешно",
|
||||
"Occupied": "Занято",
|
||||
"Disabled": "Отключено",
|
||||
"Providers": "Поставщики",
|
||||
"Proxies Providers": "Поставщики прокси",
|
||||
"Rules Providers": "Поставщики правил",
|
||||
"Update All Rules Providers": "Обновить всех поставщиков правил",
|
||||
"Rule Set rules": "{{rule}} правил",
|
||||
"Last Update": "Последнее обновление: {{fromNow}}",
|
||||
"Successfully Updated Rules Providers": "Успешно обновлены поставщики правил",
|
||||
"Portable Update Error": "Портативное обновление не поддерживается. Пожалуйста, загрузите последнюю версию с официального сайта.",
|
||||
"Tray Proxies Selector": "Селектор прокси в трее",
|
||||
"Hidden": "Скрыто",
|
||||
"Normal": "Нормальный",
|
||||
"Submenu": "Подменю",
|
||||
"Subscription": "подписка",
|
||||
"FetchError": "Из-за проблем с сетью содержимое {{content}} невозможно получить. Пожалуйста, проверьте сетевое соединение или повторите попытку позже.",
|
||||
|
||||
"tun": "Туннель",
|
||||
"normal": "Обычный",
|
||||
"Proxy Set proxies": "{{rule}} прокси",
|
||||
"Update All Proxies Providers": "Обновить всех поставщиков прокси",
|
||||
"Lighten Up Animation Effects": "Осветлить анимационные эффекты",
|
||||
"Subscription": "Подписка",
|
||||
"FetchError": "Не удалось получить {{content}} из-за проблемы с сетью. Пожалуйста, проверьте ваше сетевое соединение и попробуйте снова позже.",
|
||||
"tun": "Режим TUN",
|
||||
"normal": "Нормальный",
|
||||
"system_proxy": "Системный прокси",
|
||||
"Subscription Expires In": "Подписка истекает через {{time}}",
|
||||
"Subscription Updated At": "Подписка обновлена {{time}}",
|
||||
"Proxy Takeover Status": "Статус захвата прокси",
|
||||
"Subscription Expires In": "Истекает {{time}}",
|
||||
"Subscription Updated At": "Обновлено в {{time}}",
|
||||
"Choose file to import or leave it blank to create new one": "Выберите файл для импорта или оставьте пустым, чтобы создать новый.",
|
||||
"updater": {
|
||||
"title": "Доступно обновление",
|
||||
"close": "Закрыть",
|
||||
"update": "Обновить",
|
||||
"title": "Доступна новая версия",
|
||||
"close": "Игнорировать",
|
||||
"update": "Обновить сейчас",
|
||||
"go": "Посмотреть на GitHub"
|
||||
},
|
||||
"not_installed": "Не установлен",
|
||||
"stopped": "Остановлен",
|
||||
"running": "Запущен",
|
||||
"stopped_reason": "Остановлен по причине: {{reason}}",
|
||||
"not_installed": "Не установлено",
|
||||
"stopped": "Остановлено",
|
||||
"running": "Работает",
|
||||
"stopped_reason": "Остановлено, Причина: {{reason}}",
|
||||
"Current Status": "Текущее состояние: {{status}}",
|
||||
"Information: To enable service mode, make sure the Clash Nyanpasu service is installed and started": "Информация: Чтобы включить режим службы, убедитесь, что служба Clash Nyanpasu установлена и запущена.",
|
||||
"install": "установить",
|
||||
"uninstall": "удалить",
|
||||
"start": "запустить",
|
||||
"stop": "остановить",
|
||||
"Failed to install": "Не удалось установить",
|
||||
"Failed to uninstall": "Не удалось удалить",
|
||||
"service_shortcuts": {
|
||||
"title": "Службы",
|
||||
"title": "Ярлыки службы",
|
||||
"core_status": "Статус ядра: ",
|
||||
"service_status": "Статус службы: ",
|
||||
"core_started_by": "Ядро запущено: ",
|
||||
"core_started_by": "Запущено {{by}}",
|
||||
"last_status_changed_since": "Последнее изменение статуса: {{time}}"
|
||||
}
|
||||
},
|
||||
"service": "Служба",
|
||||
"UI": "Пользовательский интерфейс",
|
||||
"Service Manual Tips": "Руководство по обслуживанию",
|
||||
"Unable to operation the service automatically": "Не удается автоматически {{operation}} службу. Пожалуйста, перейдите в каталог ядра, запустите PowerShell как администратор на Windows или эмулятор терминала на macOS/Linux и выполните следующие команды:",
|
||||
"Successfully switched to the clash core": "Успешно переключено на ядро {{core}}.",
|
||||
"Failed to switch. You could see the details in the log": "Не удалось переключить. Вы можете увидеть детали в журнале. \nОшибка: {{error}}",
|
||||
"Successfully restarted the core": "Ядро успешно перезапущено.",
|
||||
"Failed to restart. You could see the details in the log": "Не удалось перезапустить. Вы можете увидеть детали в журнале.\n\nОшибка:",
|
||||
"Failed to fetch. Please check your network connection": "Не удалось получить. Пожалуйста, проверьте ваше сетевое соединение.",
|
||||
"Successfully updated the core": "Ядро успешно обновлено {{core}}.",
|
||||
"Failed to update": "Не удалось обновить. {{error}}",
|
||||
"Multiple directories are not supported": "Несколько каталогов не поддерживаются.",
|
||||
"Successfully changed the app directory": "Каталог приложения успешно изменен.",
|
||||
"Failed to migrate": "Не удалось мигрировать. {{error}}",
|
||||
"Web UI": "Веб-интерфейс",
|
||||
"New Item": "Новый элемент",
|
||||
"Edit Item": "Редактировать элемент",
|
||||
"Input": "Ввод",
|
||||
"Support %host %port, and %secret": "Поддерживает %host, %port и %secret",
|
||||
"Replace host, port, and secret with": "Замените хост, порт и секрет на:",
|
||||
"Result": "Результат"
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@radix-ui/react-scroll-area": "1.2.1",
|
||||
"@tauri-apps/api": "2.1.1",
|
||||
"@types/d3": "7.4.3",
|
||||
"@types/react": "18.3.14",
|
||||
"@types/react": "18.3.16",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
"ahooks": "3.8.4",
|
||||
"d3": "7.9.0",
|
||||
@@ -32,7 +32,7 @@
|
||||
"react": "rc",
|
||||
"react-dom": "rc",
|
||||
"react-error-boundary": "4.1.2",
|
||||
"react-i18next": "15.1.3",
|
||||
"react-i18next": "15.1.4",
|
||||
"react-use": "17.5.1",
|
||||
"vite": "6.0.3",
|
||||
"vite-tsconfig-paths": "5.1.4"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"_version": 1,
|
||||
"tray": {
|
||||
"copy_env": {
|
||||
"cmd": "Копировать Env (CMD)",
|
||||
"ps": "Копировать Env (PS)",
|
||||
"sh": "Копировать Env (sh)"
|
||||
},
|
||||
"select_proxies": "Выбрать прокси",
|
||||
"dashboard": "Панель управления",
|
||||
"direct_mode": "Прямой режим",
|
||||
"global_mode": "Глобальный режим",
|
||||
"more": {
|
||||
"menu": "Еще",
|
||||
"restart_app": "Перезапустить приложение",
|
||||
"restart_clash": "Перезапустить Clash"
|
||||
},
|
||||
"open_dir": {
|
||||
"menu": "Открыть папку",
|
||||
"app_config_dir": "Папка конфигурации",
|
||||
"app_data_dir": "Папка данных",
|
||||
"core_dir": "Папка ядра",
|
||||
"log_dir": "Папка журналов"
|
||||
},
|
||||
"proxy_action": {
|
||||
"on": "Включить",
|
||||
"off": "Выключить"
|
||||
},
|
||||
"quit": "Выйти",
|
||||
"rule_mode": "Режим правил",
|
||||
"script_mode": "Режим скриптов",
|
||||
"system_proxy": "Системный прокси",
|
||||
"tun_mode": "Режим TUN"
|
||||
},
|
||||
"dialog": {
|
||||
"panic": "Пожалуйста, сообщите об этой проблеме в трекере проблем Github.",
|
||||
"migrate": "Обнаружен файл конфигурации старой версии\nМигрировать на новую версию или нет?\n ВНИМАНИЕ: Это перезапишет вашу текущую конфигурацию, если она существует",
|
||||
"custom_app_dir_migrate": "Вы установите пользовательскую папку приложения в %{path}\nПереместить ли текущую папку приложения в новую?",
|
||||
"warning": {
|
||||
"enable_tun_with_no_permission": "Режим TUN требует прав администратора или режима службы, ни один из которых не включен, режим TUN не будет работать должным образом."
|
||||
},
|
||||
"info": {
|
||||
"grant_core_permission": "Ядру Clash необходимы права администратора для корректной работы режима TUN, предоставить их?\n\nОбратите внимание: Эта операция требует ввода пароля."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"latest": {
|
||||
"mihomo": "v1.18.10",
|
||||
"mihomo_alpha": "alpha-d6b496d",
|
||||
"mihomo": "v1.19.0",
|
||||
"mihomo_alpha": "alpha-5d9d8f4",
|
||||
"clash_rs": "v0.7.3",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.7.3-alpha+sha.e711fd9"
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2024-12-04T22:21:03.586Z"
|
||||
"updated_at": "2024-12-10T22:20:57.885Z"
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
"@tauri-apps/cli": "2.1.0",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "22.10.1",
|
||||
"@types/node": "22.10.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.17.0",
|
||||
"@typescript-eslint/parser": "8.17.0",
|
||||
"autoprefixer": "10.4.20",
|
||||
@@ -82,7 +82,7 @@
|
||||
"eslint-plugin-react": "7.37.2",
|
||||
"eslint-plugin-react-compiler": "0.0.0-experimental-fcabbc1-20241106",
|
||||
"eslint-plugin-react-hooks": "4.6.2",
|
||||
"knip": "5.39.2",
|
||||
"knip": "5.39.4",
|
||||
"lint-staged": "15.2.10",
|
||||
"npm-run-all2": "7.0.1",
|
||||
"postcss": "8.4.49",
|
||||
|
||||
Generated
+326
-263
File diff suppressed because it is too large
Load Diff
+23
-8
@@ -3,20 +3,25 @@ name: main
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
tags:
|
||||
- 'v*'
|
||||
- "v*"
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
# linters
|
||||
# linters
|
||||
lint-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: "22.x"
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||
- run: make lint-frontend
|
||||
lint-backend:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -32,14 +37,19 @@ jobs:
|
||||
steps:
|
||||
- run: echo "done"
|
||||
|
||||
# tests
|
||||
# tests
|
||||
test-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: "22.x"
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||
- run: make test-frontend
|
||||
test-backend:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -55,7 +65,7 @@ jobs:
|
||||
steps:
|
||||
- run: echo "done"
|
||||
|
||||
# release
|
||||
# release
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
@@ -67,9 +77,14 @@ jobs:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.0
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
package_json_file: "frontend/package.json"
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: "22.x"
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
|
||||
@@ -10,7 +10,7 @@ build: | build-frontend build-backend ## Build binary
|
||||
|
||||
.PHONY: build-frontend
|
||||
build-frontend: ## Build frontend
|
||||
$Q cd frontend && npm ci && npm run build
|
||||
$Q cd frontend && pnpm install --frozen-lockfile && pnpm run build
|
||||
|
||||
.PHONY: build-backend
|
||||
build-backend: ## Build backend
|
||||
@@ -21,6 +21,7 @@ test: | test-frontend test-backend ## Run all tests
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: ## Run frontend tests
|
||||
$Q cd frontend && pnpm install --frozen-lockfile && pnpm run typecheck
|
||||
|
||||
.PHONY: test-backend
|
||||
test-backend: ## Run backend tests
|
||||
@@ -31,7 +32,7 @@ lint: lint-frontend lint-backend ## Run all linters
|
||||
|
||||
.PHONY: lint-frontend
|
||||
lint-frontend: ## Run frontend linters
|
||||
$Q cd frontend && npm ci && npm run lint
|
||||
$Q cd frontend && pnpm install --frozen-lockfile && pnpm run lint
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: | $(golangci-lint) ## Run backend linters
|
||||
@@ -65,4 +66,4 @@ help: ## Show this help
|
||||
@awk 'BEGIN {FS = ":.*?## "} { \
|
||||
if (/^[a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \
|
||||
else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \
|
||||
}' $(MAKEFILE_LIST)
|
||||
}' $(MAKEFILE_LIST)
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-typescript",
|
||||
"@vue/eslint-config-prettier"
|
||||
],
|
||||
"rules": {
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-mutating-props": [
|
||||
"error",
|
||||
{
|
||||
"shallowOnly": true
|
||||
}
|
||||
]
|
||||
// no-undef is already included in
|
||||
// @vue/eslint-config-typescript
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import pluginVue from "eslint-plugin-vue";
|
||||
import vueTsEslintConfig from "@vue/eslint-config-typescript";
|
||||
import prettierConfig from "@vue/eslint-config-prettier";
|
||||
|
||||
export default [
|
||||
{
|
||||
name: "app/files-to-lint",
|
||||
files: ["**/*.{ts,mts,tsx,vue}"],
|
||||
},
|
||||
|
||||
{
|
||||
name: "app/files-to-ignore",
|
||||
ignores: ["**/dist/**", "**/dist-ssr/**", "**/coverage/**"],
|
||||
},
|
||||
|
||||
...pluginVue.configs["flat/essential"],
|
||||
...vueTsEslintConfig(),
|
||||
prettierConfig,
|
||||
|
||||
{
|
||||
rules: {
|
||||
"no-var": "warn",
|
||||
"prefer-const": "warn",
|
||||
// Note: you must disable the base rule as it can report incorrect errors
|
||||
"no-unused-expressions": "off",
|
||||
"@typescript-eslint/no-unused-expressions": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/ban-ts-comment": "warn",
|
||||
"vue/block-lang": "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-mutating-props": [
|
||||
"error",
|
||||
{
|
||||
shallowOnly: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Generated
-7980
File diff suppressed because it is too large
Load Diff
@@ -4,32 +4,33 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"npm": ">=7.0.0",
|
||||
"node": ">=18.0.0"
|
||||
"node": ">=22.0.0",
|
||||
"pnpm": ">=9.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "npm run typecheck && vite build",
|
||||
"build": "pnpm run typecheck && vite build",
|
||||
"clean": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitkeep' -exec rm -r {} +",
|
||||
"typecheck": "vue-tsc -p ./tsconfig.json --noEmit",
|
||||
"lint": "npm run typecheck && eslint --ext .vue,.ts src/",
|
||||
"lint:fix": "eslint --ext .vue,.ts --fix src/",
|
||||
"typecheck": "vue-tsc -p ./tsconfig.tsc.json --noEmit",
|
||||
"lint": "eslint src/",
|
||||
"lint:fix": "eslint --fix src/",
|
||||
"format": "prettier --write .",
|
||||
"test": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chenfengyuan/vue-number-input": "^2.0.1",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/integrations": "^10.9.0",
|
||||
"@vueuse/core": "^12.0.0",
|
||||
"@vueuse/integrations": "^12.0.0",
|
||||
"ace-builds": "^1.32.9",
|
||||
"core-js": "^3.36.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"epubjs": "^0.3.93",
|
||||
"filesize": "^10.1.1",
|
||||
"js-base64": "^3.7.7",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^15.0.3",
|
||||
"material-icons": "^1.13.12",
|
||||
"marked": "^14.1.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
@@ -41,33 +42,41 @@
|
||||
"videojs-mobile-ui": "^1.1.1",
|
||||
"vue": "^3.4.21",
|
||||
"vue-final-modal": "^4.5.4",
|
||||
"vue-i18n": "^9.14.2",
|
||||
"vue-i18n": "^10.0.5",
|
||||
"vue-lazyload": "^3.0.0",
|
||||
"vue-reader": "^1.2.14",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-toastification": "^2.0.0-rc.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"@intlify/unplugin-vue-i18n": "^6.0.0",
|
||||
"@playwright/test": "^1.42.1",
|
||||
"@tsconfig/node22": "^22.0.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.12.2",
|
||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||
"@vitejs/plugin-legacy": "^5.3.2",
|
||||
"@types/node": "^22.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
||||
"@vitejs/plugin-legacy": "^6.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/eslint-config-prettier": "^10.1.0",
|
||||
"@vue/eslint-config-typescript": "^14.1.4",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^8.57.0",
|
||||
"concurrently": "^9.1.0",
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.24.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"jsdom": "^25.0.1",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"terser": "^5.30.0",
|
||||
"vite": "^5.4.6",
|
||||
"vite": "^6.0.2",
|
||||
"vite-plugin-compression2": "^1.0.0",
|
||||
"vue-tsc": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"typescript": "~5.6.3"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab"
|
||||
}
|
||||
|
||||
Generated
+5386
File diff suppressed because it is too large
Load Diff
@@ -50,7 +50,7 @@ import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { commands } from "@/api";
|
||||
import { throttle } from "lodash";
|
||||
import { throttle } from "lodash-es";
|
||||
import { theme } from "@/utils/constants";
|
||||
|
||||
export default {
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import throttle from "lodash/throttle";
|
||||
import { throttle } from "lodash-es";
|
||||
import UTIF from "utif";
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
|
||||
interface IProps {
|
||||
src: string;
|
||||
moveDisabledTime: number;
|
||||
classList: any[];
|
||||
zoomStep: number;
|
||||
moveDisabledTime?: number;
|
||||
classList?: any[];
|
||||
zoomStep?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
|
||||
@@ -120,7 +120,7 @@ const subLabel = (subUrl: string) => {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(subUrl);
|
||||
} catch (_) {
|
||||
} catch {
|
||||
// treat it as a relative url
|
||||
// we only need this for filename
|
||||
url = new URL(subUrl, window.location.origin);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SelectHTMLAttributes } from "vue";
|
||||
import type { SelectHTMLAttributes } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -4,7 +4,7 @@ import VueNumberInput from "@chenfengyuan/vue-number-input";
|
||||
import VueLazyload from "vue-lazyload";
|
||||
import { createVfm } from "vue-final-modal";
|
||||
import Toast, { POSITION, useToast } from "vue-toastification";
|
||||
import {
|
||||
import type {
|
||||
ToastOptions,
|
||||
PluginOptions,
|
||||
} from "vue-toastification/dist/types/types";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { RouteLocation, createRouter, createWebHistory } from "vue-router";
|
||||
import type { RouteLocation } from "vue-router";
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import Login from "@/views/Login.vue";
|
||||
import Layout from "@/views/Layout.vue";
|
||||
import Files from "@/views/Files.vue";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createPinia as _createPinia } from "pinia";
|
||||
import { markRaw } from "vue";
|
||||
import { Router } from "vue-router";
|
||||
import type { Router } from "vue-router";
|
||||
|
||||
export default function createPinia(router: Router) {
|
||||
const pinia = _createPinia();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useFileStore } from "./file";
|
||||
import { files as api } from "@/api";
|
||||
import throttle from "lodash/throttle";
|
||||
import { throttle } from "lodash-es";
|
||||
import buttons from "@/utils/buttons";
|
||||
|
||||
// TODO: make this into a user setting
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import router from "@/router";
|
||||
import { JwtPayload, jwtDecode } from "jwt-decode";
|
||||
import type { JwtPayload } from "jwt-decode";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { baseURL, noAuth } from "./constants";
|
||||
import { StatusError } from "@/api/utils";
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ import { users, files as api } from "@/api";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import * as upload from "@/utils/upload";
|
||||
import css from "@/utils/css";
|
||||
import throttle from "lodash/throttle";
|
||||
import { throttle } from "lodash-es";
|
||||
import { Base64 } from "js-base64";
|
||||
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
|
||||
@@ -168,7 +168,7 @@ import { files as api } from "@/api";
|
||||
import { createURL } from "@/api/utils";
|
||||
import { resizePreview } from "@/utils/constants";
|
||||
import url from "@/utils/url";
|
||||
import throttle from "lodash/throttle";
|
||||
import { throttle } from "lodash-es";
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Action from "@/components/header/Action.vue";
|
||||
import ExtendedImage from "@/components/files/ExtendedImage.vue";
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"types": ["vite/client", "@intlify/unplugin-vue-i18n/messages"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node10",
|
||||
"strict": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitReturns": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client", "@intlify/unplugin-vue-i18n/messages"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.vue"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "@tsconfig/node22/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
// vue-tsc wont shut up about error TS9005
|
||||
// in non-TS vue files so exclude them
|
||||
"exclude": [
|
||||
"src/components/Shell.vue",
|
||||
"src/components/prompts/Copy.vue",
|
||||
"src/components/prompts/Delete.vue",
|
||||
"src/components/prompts/FileList.vue",
|
||||
"src/components/prompts/Rename.vue",
|
||||
"src/components/prompts/Share.vue",
|
||||
"src/components/prompts/UploadFiles.vue"
|
||||
]
|
||||
}
|
||||
+10
-10
@@ -2,19 +2,19 @@ module github.com/Loyalsoldier/geoip
|
||||
|
||||
go 1.23
|
||||
|
||||
toolchain go1.23.2
|
||||
toolchain go1.23.4
|
||||
|
||||
require (
|
||||
github.com/klauspost/compress v1.17.11
|
||||
github.com/maxmind/mmdbwriter v1.0.0
|
||||
github.com/oschwald/geoip2-golang v1.11.0
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/sagernet/sing-box v1.10.1
|
||||
github.com/sagernet/sing-box v1.10.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/tailscale/hujson v0.0.0-20241010212012-29efb4a0184b
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
google.golang.org/protobuf v1.35.1
|
||||
google.golang.org/protobuf v1.35.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@@ -22,14 +22,14 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/miekg/dns v1.1.62 // indirect
|
||||
github.com/sagernet/sing v0.5.0-rc.2 // indirect
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2 // indirect
|
||||
github.com/sagernet/sing v0.5.1 // indirect
|
||||
github.com/sagernet/sing-dns v0.3.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
|
||||
+24
-24
@@ -33,14 +33,14 @@ github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagernet/quic-go v0.47.0-beta.2 h1:1tCGWFOSaXIeuQaHrwOMJIYvlupjTcaVInGQw5ArULU=
|
||||
github.com/sagernet/quic-go v0.47.0-beta.2/go.mod h1:bLVKvElSEMNv7pu7SZHscW02TYigzQ5lQu3Nh4wNh8Q=
|
||||
github.com/sagernet/sing v0.5.0-rc.2 h1:tIrs6pRbjJWvI0ITRSg47P1wosY+iSuHpw9t5/hBx+Q=
|
||||
github.com/sagernet/sing v0.5.0-rc.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-box v1.10.1 h1:J3H0TzF0LGC4uOR62TpSuAhhHkv3Bx5M5F5kK5JB+WA=
|
||||
github.com/sagernet/sing-box v1.10.1/go.mod h1:Nk9Ww0M1ulUsbfd5d4dfMBfJ0Audmm6m5+YYdvdpcZQ=
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2 h1:z1yROBxd/6wik5h53Sz5df1DSmbPTaOu/Z0wAmyXGoQ=
|
||||
github.com/sagernet/sing-dns v0.3.0-rc.2/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1 h1:W0plrLWa1XtOWDTdX3CJwxmQuxkya12nN5BRGZ87kEg=
|
||||
github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/+or9YMLaG5VeTk4k=
|
||||
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-box v1.10.3 h1:Ptknjvcm6IfKnezuJZWQsQGxh6ZV1pAi/GIbfRNKELQ=
|
||||
github.com/sagernet/sing-box v1.10.3/go.mod h1:u67UniOxg+rs31hx4tLwabkai0H2vL+psjAGBPz4Osc=
|
||||
github.com/sagernet/sing-dns v0.3.0 h1:uHCIlbCwBxALJwXcEK1d75d7t3vzCSVEQsPfZR1cxQE=
|
||||
github.com/sagernet/sing-dns v0.3.0/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
@@ -57,24 +57,24 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -238,7 +238,7 @@ func (s *SRSIn) generateEntries(name string, reader io.Reader, entries map[strin
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rule := range plainRuleSet.Rules {
|
||||
for _, rule := range plainRuleSet.Options.Rules {
|
||||
for _, cidrStr := range rule.DefaultOptions.IPCIDR {
|
||||
if err := entry.AddPrefix(cidrStr); err != nil {
|
||||
return err
|
||||
|
||||
@@ -194,7 +194,7 @@ func (s *SRSOut) writeFile(filename string, ruleset *option.PlainRuleSet) error
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = srs.Write(f, *ruleset, false)
|
||||
err = srs.Write(f, *ruleset, constant.RuleSetVersion1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ type serverConfigOutboundDirect struct {
|
||||
BindIPv4 string `mapstructure:"bindIPv4"`
|
||||
BindIPv6 string `mapstructure:"bindIPv6"`
|
||||
BindDevice string `mapstructure:"bindDevice"`
|
||||
FastOpen bool `mapstructure:"fastOpen"`
|
||||
}
|
||||
|
||||
type serverConfigOutboundSOCKS5 struct {
|
||||
@@ -518,18 +519,18 @@ func (c *serverConfig) fillQUICConfig(hyConfig *server.Config) error {
|
||||
}
|
||||
|
||||
func serverConfigOutboundDirectToOutbound(c serverConfigOutboundDirect) (outbounds.PluggableOutbound, error) {
|
||||
var mode outbounds.DirectOutboundMode
|
||||
opts := outbounds.DirectOutboundOptions{}
|
||||
switch strings.ToLower(c.Mode) {
|
||||
case "", "auto":
|
||||
mode = outbounds.DirectOutboundModeAuto
|
||||
opts.Mode = outbounds.DirectOutboundModeAuto
|
||||
case "64":
|
||||
mode = outbounds.DirectOutboundMode64
|
||||
opts.Mode = outbounds.DirectOutboundMode64
|
||||
case "46":
|
||||
mode = outbounds.DirectOutboundMode46
|
||||
opts.Mode = outbounds.DirectOutboundMode46
|
||||
case "6":
|
||||
mode = outbounds.DirectOutboundMode6
|
||||
opts.Mode = outbounds.DirectOutboundMode6
|
||||
case "4":
|
||||
mode = outbounds.DirectOutboundMode4
|
||||
opts.Mode = outbounds.DirectOutboundMode4
|
||||
default:
|
||||
return nil, configError{Field: "outbounds.direct.mode", Err: errors.New("unsupported mode")}
|
||||
}
|
||||
@@ -546,12 +547,14 @@ func serverConfigOutboundDirectToOutbound(c serverConfigOutboundDirect) (outboun
|
||||
if len(c.BindIPv6) > 0 && ip6 == nil {
|
||||
return nil, configError{Field: "outbounds.direct.bindIPv6", Err: errors.New("invalid IPv6 address")}
|
||||
}
|
||||
return outbounds.NewDirectOutboundBindToIPs(mode, ip4, ip6)
|
||||
opts.BindIP4 = ip4
|
||||
opts.BindIP6 = ip6
|
||||
}
|
||||
if bindDevice {
|
||||
return outbounds.NewDirectOutboundBindToDevice(mode, c.BindDevice)
|
||||
opts.DeviceName = c.BindDevice
|
||||
}
|
||||
return outbounds.NewDirectOutboundSimple(mode), nil
|
||||
opts.FastOpen = c.FastOpen
|
||||
return outbounds.NewDirectOutboundWithOptions(opts)
|
||||
}
|
||||
|
||||
func serverConfigOutboundSOCKS5ToOutbound(c serverConfigOutboundSOCKS5) (outbounds.PluggableOutbound, error) {
|
||||
|
||||
@@ -138,6 +138,7 @@ func TestServerConfig(t *testing.T) {
|
||||
BindIPv4: "2.4.6.8",
|
||||
BindIPv6: "0:0:0:0:0:ffff:0204:0608",
|
||||
BindDevice: "eth233",
|
||||
FastOpen: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -108,6 +108,7 @@ outbounds:
|
||||
bindIPv4: 2.4.6.8
|
||||
bindIPv6: 0:0:0:0:0:ffff:0204:0608
|
||||
bindDevice: eth233
|
||||
fastOpen: true
|
||||
- name: badstuff
|
||||
type: socks5
|
||||
socks5:
|
||||
|
||||
+3
-1
@@ -25,7 +25,7 @@ require (
|
||||
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301
|
||||
go.uber.org/zap v1.24.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/sys v0.23.0
|
||||
golang.org/x/sys v0.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -33,6 +33,8 @@ require (
|
||||
github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7 // indirect
|
||||
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 // indirect
|
||||
github.com/cloudflare/circl v1.3.9 // indirect
|
||||
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect
|
||||
github.com/database64128/tfo-go/v2 v2.2.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
|
||||
+6
-2
@@ -63,6 +63,10 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a h1:t4SDi0pmNkryzKdM4QF3o5vqSP4GRjeZD/6j3nyxNP0=
|
||||
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a/go.mod h1:7K2NQKbabB5mBl41vF6YayYl5g7YpDwc4dQ5iMpP3Lg=
|
||||
github.com/database64128/tfo-go/v2 v2.2.2 h1:BxynF4qGF5ct3DpPLEG62uyJZ3LQhqaf0Ken+kyy7PM=
|
||||
github.com/database64128/tfo-go/v2 v2.2.2/go.mod h1:2IW8jppdBwdVMjA08uEyMNnqiAHKUlqAA+J8NrsfktY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -463,8 +467,8 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
|
||||
@@ -27,7 +27,7 @@ require (
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
|
||||
@@ -58,8 +58,7 @@ golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
|
||||
@@ -8,6 +8,7 @@ require (
|
||||
github.com/apernet/hysteria/core/v2 v2.0.0-00010101000000-000000000000
|
||||
github.com/apernet/quic-go v0.48.2-0.20241104191913-cb103fcecfe7
|
||||
github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6
|
||||
github.com/database64128/tfo-go/v2 v2.2.2
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/refraction-networking/utls v1.6.6
|
||||
@@ -21,6 +22,7 @@ require (
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.9 // indirect
|
||||
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||
@@ -36,7 +38,7 @@ require (
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
@@ -10,6 +10,10 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
||||
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
|
||||
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a h1:t4SDi0pmNkryzKdM4QF3o5vqSP4GRjeZD/6j3nyxNP0=
|
||||
github.com/database64128/netx-go v0.0.0-20240905055117-62795b8b054a/go.mod h1:7K2NQKbabB5mBl41vF6YayYl5g7YpDwc4dQ5iMpP3Lg=
|
||||
github.com/database64128/tfo-go/v2 v2.2.2 h1:BxynF4qGF5ct3DpPLEG62uyJZ3LQhqaf0Ken+kyy7PM=
|
||||
github.com/database64128/tfo-go/v2 v2.2.2/go.mod h1:2IW8jppdBwdVMjA08uEyMNnqiAHKUlqAA+J8NrsfktY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -92,8 +96,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package outbounds
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/database64128/tfo-go/v2"
|
||||
)
|
||||
|
||||
type fastOpenDialer struct {
|
||||
dialer *tfo.Dialer
|
||||
}
|
||||
|
||||
func newFastOpenDialer(netDialer *net.Dialer) *fastOpenDialer {
|
||||
return &fastOpenDialer{
|
||||
dialer: &tfo.Dialer{
|
||||
Dialer: *netDialer,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Dial returns immediately without actually establishing a connection.
|
||||
// The connection will be established by the first Write() call.
|
||||
func (d *fastOpenDialer) Dial(network, address string) (net.Conn, error) {
|
||||
return &fastOpenConn{
|
||||
dialer: d.dialer,
|
||||
network: network,
|
||||
address: address,
|
||||
readyChan: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type fastOpenConn struct {
|
||||
dialer *tfo.Dialer
|
||||
network string
|
||||
address string
|
||||
|
||||
conn net.Conn
|
||||
connLock sync.RWMutex
|
||||
readyChan chan struct{}
|
||||
|
||||
// States before connection ready
|
||||
deadline *time.Time
|
||||
readDeadline *time.Time
|
||||
writeDeadline *time.Time
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) Read(b []byte) (n int, err error) {
|
||||
c.connLock.RLock()
|
||||
conn := c.conn
|
||||
c.connLock.RUnlock()
|
||||
|
||||
if conn != nil {
|
||||
return conn.Read(b)
|
||||
}
|
||||
|
||||
// Wait until the connection is ready or closed
|
||||
<-c.readyChan
|
||||
|
||||
if c.conn == nil {
|
||||
// This is equivalent to isClosedBeforeReady() == true
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) Write(b []byte) (n int, err error) {
|
||||
c.connLock.RLock()
|
||||
conn := c.conn
|
||||
c.connLock.RUnlock()
|
||||
|
||||
if conn != nil {
|
||||
return conn.Write(b)
|
||||
}
|
||||
|
||||
c.connLock.RLock()
|
||||
closed := c.isClosedBeforeReady()
|
||||
c.connLock.RUnlock()
|
||||
|
||||
if closed {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
|
||||
c.connLock.Lock()
|
||||
defer c.connLock.Unlock()
|
||||
|
||||
if c.isClosedBeforeReady() {
|
||||
// Closed by other goroutine
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
|
||||
conn = c.conn
|
||||
if conn != nil {
|
||||
// Established by other goroutine
|
||||
return conn.Write(b)
|
||||
}
|
||||
|
||||
conn, err = c.dialer.Dial(c.network, c.address, b)
|
||||
if err != nil {
|
||||
close(c.readyChan)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Apply pre-set states
|
||||
if c.deadline != nil {
|
||||
_ = conn.SetDeadline(*c.deadline)
|
||||
}
|
||||
if c.readDeadline != nil {
|
||||
_ = conn.SetReadDeadline(*c.readDeadline)
|
||||
}
|
||||
if c.writeDeadline != nil {
|
||||
_ = conn.SetWriteDeadline(*c.writeDeadline)
|
||||
}
|
||||
|
||||
c.conn = conn
|
||||
close(c.readyChan)
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) Close() error {
|
||||
c.connLock.RLock()
|
||||
defer c.connLock.RUnlock()
|
||||
|
||||
if c.isClosedBeforeReady() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
if c.conn != nil {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
close(c.readyChan)
|
||||
return nil
|
||||
}
|
||||
|
||||
// isClosedBeforeReady returns true if the connection is closed before the real connection is established.
|
||||
// This function should be called with connLock.RLock().
|
||||
func (c *fastOpenConn) isClosedBeforeReady() bool {
|
||||
select {
|
||||
case <-c.readyChan:
|
||||
if c.conn == nil {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) LocalAddr() net.Addr {
|
||||
c.connLock.RLock()
|
||||
defer c.connLock.RUnlock()
|
||||
|
||||
if c.conn != nil {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) RemoteAddr() net.Addr {
|
||||
c.connLock.RLock()
|
||||
conn := c.conn
|
||||
c.connLock.RUnlock()
|
||||
|
||||
if conn != nil {
|
||||
return conn.RemoteAddr()
|
||||
}
|
||||
|
||||
addr, err := net.ResolveTCPAddr(c.network, c.address)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) SetDeadline(t time.Time) error {
|
||||
c.connLock.RLock()
|
||||
defer c.connLock.RUnlock()
|
||||
|
||||
c.deadline = &t
|
||||
|
||||
if c.conn != nil {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
if c.isClosedBeforeReady() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) SetReadDeadline(t time.Time) error {
|
||||
c.connLock.RLock()
|
||||
defer c.connLock.RUnlock()
|
||||
|
||||
c.readDeadline = &t
|
||||
|
||||
if c.conn != nil {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
if c.isClosedBeforeReady() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *fastOpenConn) SetWriteDeadline(t time.Time) error {
|
||||
c.connLock.RLock()
|
||||
defer c.connLock.RUnlock()
|
||||
|
||||
c.writeDeadline = &t
|
||||
|
||||
if c.conn != nil {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
if c.isClosedBeforeReady() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ net.Conn = (*fastOpenConn)(nil)
|
||||
@@ -35,8 +35,8 @@ type directOutbound struct {
|
||||
Mode DirectOutboundMode
|
||||
|
||||
// Dialer4 and Dialer6 are used for IPv4 and IPv6 TCP connections respectively.
|
||||
Dialer4 *net.Dialer
|
||||
Dialer6 *net.Dialer
|
||||
DialFunc4 func(network, address string) (net.Conn, error)
|
||||
DialFunc6 func(network, address string) (net.Conn, error)
|
||||
|
||||
// DeviceName & BindIPs are for UDP connections. They don't use dialers, so we
|
||||
// need to bind them when creating the connection.
|
||||
@@ -45,6 +45,16 @@ type directOutbound struct {
|
||||
BindIP6 net.IP
|
||||
}
|
||||
|
||||
type DirectOutboundOptions struct {
|
||||
Mode DirectOutboundMode
|
||||
|
||||
DeviceName string
|
||||
BindIP4 net.IP
|
||||
BindIP6 net.IP
|
||||
|
||||
FastOpen bool
|
||||
}
|
||||
|
||||
type noAddressError struct {
|
||||
IPv4 bool
|
||||
IPv6 bool
|
||||
@@ -84,6 +94,57 @@ func (e resolveError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func NewDirectOutboundWithOptions(opts DirectOutboundOptions) (PluggableOutbound, error) {
|
||||
dialer4 := &net.Dialer{
|
||||
Timeout: defaultDialerTimeout,
|
||||
}
|
||||
if opts.BindIP4 != nil {
|
||||
if opts.BindIP4.To4() == nil {
|
||||
return nil, errors.New("BindIP4 must be an IPv4 address")
|
||||
}
|
||||
dialer4.LocalAddr = &net.TCPAddr{
|
||||
IP: opts.BindIP4,
|
||||
}
|
||||
}
|
||||
dialer6 := &net.Dialer{
|
||||
Timeout: defaultDialerTimeout,
|
||||
}
|
||||
if opts.BindIP6 != nil {
|
||||
if opts.BindIP6.To4() != nil {
|
||||
return nil, errors.New("BindIP6 must be an IPv6 address")
|
||||
}
|
||||
dialer6.LocalAddr = &net.TCPAddr{
|
||||
IP: opts.BindIP6,
|
||||
}
|
||||
}
|
||||
if opts.DeviceName != "" {
|
||||
err := dialerBindToDevice(dialer4, opts.DeviceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = dialerBindToDevice(dialer6, opts.DeviceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dialFunc4 := dialer4.Dial
|
||||
dialFunc6 := dialer6.Dial
|
||||
if opts.FastOpen {
|
||||
dialFunc4 = newFastOpenDialer(dialer4).Dial
|
||||
dialFunc6 = newFastOpenDialer(dialer6).Dial
|
||||
}
|
||||
|
||||
return &directOutbound{
|
||||
Mode: opts.Mode,
|
||||
DialFunc4: dialFunc4,
|
||||
DialFunc6: dialFunc6,
|
||||
DeviceName: opts.DeviceName,
|
||||
BindIP4: opts.BindIP4,
|
||||
BindIP6: opts.BindIP6,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewDirectOutboundSimple creates a new directOutbound with the given mode,
|
||||
// without binding to a specific device. Works on all platforms.
|
||||
func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound {
|
||||
@@ -91,9 +152,9 @@ func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound {
|
||||
Timeout: defaultDialerTimeout,
|
||||
}
|
||||
return &directOutbound{
|
||||
Mode: mode,
|
||||
Dialer4: d,
|
||||
Dialer6: d,
|
||||
Mode: mode,
|
||||
DialFunc4: d.Dial,
|
||||
DialFunc6: d.Dial,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,34 +163,20 @@ func NewDirectOutboundSimple(mode DirectOutboundMode) PluggableOutbound {
|
||||
// can be nil, in which case the directOutbound will not bind to a specific address
|
||||
// for that family.
|
||||
func NewDirectOutboundBindToIPs(mode DirectOutboundMode, bindIP4, bindIP6 net.IP) (PluggableOutbound, error) {
|
||||
if bindIP4 != nil && bindIP4.To4() == nil {
|
||||
return nil, errors.New("bindIP4 must be an IPv4 address")
|
||||
}
|
||||
if bindIP6 != nil && bindIP6.To4() != nil {
|
||||
return nil, errors.New("bindIP6 must be an IPv6 address")
|
||||
}
|
||||
ob := &directOutbound{
|
||||
Mode: mode,
|
||||
Dialer4: &net.Dialer{
|
||||
Timeout: defaultDialerTimeout,
|
||||
},
|
||||
Dialer6: &net.Dialer{
|
||||
Timeout: defaultDialerTimeout,
|
||||
},
|
||||
return NewDirectOutboundWithOptions(DirectOutboundOptions{
|
||||
Mode: mode,
|
||||
BindIP4: bindIP4,
|
||||
BindIP6: bindIP6,
|
||||
}
|
||||
if bindIP4 != nil {
|
||||
ob.Dialer4.LocalAddr = &net.TCPAddr{
|
||||
IP: bindIP4,
|
||||
}
|
||||
}
|
||||
if bindIP6 != nil {
|
||||
ob.Dialer6.LocalAddr = &net.TCPAddr{
|
||||
IP: bindIP6,
|
||||
}
|
||||
}
|
||||
return ob, nil
|
||||
})
|
||||
}
|
||||
|
||||
// NewDirectOutboundBindToDevice creates a new directOutbound with the given mode,
|
||||
// and binds to the given device. Only works on Linux.
|
||||
func NewDirectOutboundBindToDevice(mode DirectOutboundMode, deviceName string) (PluggableOutbound, error) {
|
||||
return NewDirectOutboundWithOptions(DirectOutboundOptions{
|
||||
Mode: mode,
|
||||
DeviceName: deviceName,
|
||||
})
|
||||
}
|
||||
|
||||
// resolve is our built-in DNS resolver for handling the case when
|
||||
@@ -201,9 +248,9 @@ func (d *directOutbound) TCP(reqAddr *AddrEx) (net.Conn, error) {
|
||||
|
||||
func (d *directOutbound) dialTCP(ip net.IP, port uint16) (net.Conn, error) {
|
||||
if ip.To4() != nil {
|
||||
return d.Dialer4.Dial("tcp4", net.JoinHostPort(ip.String(), strconv.Itoa(int(port))))
|
||||
return d.DialFunc4("tcp4", net.JoinHostPort(ip.String(), strconv.Itoa(int(port))))
|
||||
} else {
|
||||
return d.Dialer6.Dial("tcp6", net.JoinHostPort(ip.String(), strconv.Itoa(int(port))))
|
||||
return d.DialFunc6("tcp6", net.JoinHostPort(ip.String(), strconv.Itoa(int(port))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,31 +6,31 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// NewDirectOutboundBindToDevice creates a new directOutbound with the given mode,
|
||||
// and binds to the given device. Only works on Linux.
|
||||
func NewDirectOutboundBindToDevice(mode DirectOutboundMode, deviceName string) (PluggableOutbound, error) {
|
||||
func dialerBindToDevice(dialer *net.Dialer, deviceName string) error {
|
||||
if err := verifyDeviceName(deviceName); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
d := &net.Dialer{
|
||||
Timeout: defaultDialerTimeout,
|
||||
Control: func(network, address string, c syscall.RawConn) error {
|
||||
var errBind error
|
||||
err := c.Control(func(fd uintptr) {
|
||||
errBind = syscall.BindToDevice(int(fd), deviceName)
|
||||
})
|
||||
|
||||
originControl := dialer.Control
|
||||
dialer.Control = func(network, address string, c syscall.RawConn) error {
|
||||
if originControl != nil {
|
||||
// Chaining other control function
|
||||
err := originControl(network, address, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return errBind
|
||||
},
|
||||
}
|
||||
|
||||
var errBind error
|
||||
err := c.Control(func(fd uintptr) {
|
||||
errBind = syscall.BindToDevice(int(fd), deviceName)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return errBind
|
||||
}
|
||||
return &directOutbound{
|
||||
Mode: mode,
|
||||
Dialer4: d,
|
||||
Dialer6: d,
|
||||
DeviceName: deviceName,
|
||||
}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyDeviceName(deviceName string) error {
|
||||
|
||||
@@ -7,11 +7,8 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// NewDirectOutboundBindToDevice creates a new directOutbound with the given mode,
|
||||
// and binds to the given device. This doesn't work on non-Linux platforms, so this
|
||||
// is just a stub function that always returns an error.
|
||||
func NewDirectOutboundBindToDevice(mode DirectOutboundMode, deviceName string) (PluggableOutbound, error) {
|
||||
return nil, errors.New("binding to device is not supported on this platform")
|
||||
func dialerBindToDevice(dialer *net.Dialer, deviceName string) error {
|
||||
return errors.New("binding to device is not supported on this platform")
|
||||
}
|
||||
|
||||
func udpConnBindToDevice(conn *net.UDPConn, deviceName string) error {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-6.1 = .118
|
||||
LINUX_KERNEL_HASH-6.1.118 = 010784bd7161c32c4cd68a423d4dcb14e4587677d238b2825a31fe012869224c
|
||||
LINUX_VERSION-6.1 = .119
|
||||
LINUX_KERNEL_HASH-6.1.119 = aecdaf39d0a844a81ce4c67d9daff8979e938bb690df4f679fbbb494fe423278
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
LINUX_VERSION-6.6 = .63
|
||||
LINUX_KERNEL_HASH-6.6.63 = d1054ab4803413efe2850f50f1a84349c091631ec50a1cf9e891d1b1f9061835
|
||||
LINUX_VERSION-6.6 = .64
|
||||
LINUX_KERNEL_HASH-6.6.64 = 065fd93fa6cb422f650fb563f15d3e0107c85009f766405993d795fd39796ab1
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
do_sysinfo_generic() {
|
||||
[ -d /proc/device-tree ] || return
|
||||
mkdir -p /tmp/sysinfo
|
||||
[ -d /proc/device-tree ] || return
|
||||
[ -e /tmp/sysinfo/board_name ] || \
|
||||
echo "$(strings /proc/device-tree/compatible | head -1)" > /tmp/sysinfo/board_name
|
||||
[ ! -e /tmp/sysinfo/model -a -e /proc/device-tree/model ] && \
|
||||
echo "$(cat /proc/device-tree/model)" > /tmp/sysinfo/model
|
||||
[ -z "$(fw_printenv model_name)" ] || fw_printenv model_name > /tmp/sysinfo/model
|
||||
}
|
||||
|
||||
boot_hook_add preinit_main do_sysinfo_generic
|
||||
|
||||
@@ -16,7 +16,8 @@ alfa-network,ac1200rm|\
|
||||
alfa-network,awusfree1|\
|
||||
alfa-network,quad-e4g|\
|
||||
alfa-network,r36m-e4g|\
|
||||
alfa-network,tube-e4g)
|
||||
alfa-network,tube-e4g|\
|
||||
huasifei,mt7621dtu)
|
||||
ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x1000" "0x1000"
|
||||
;;
|
||||
allnet,all0256n-4m|\
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/common/edid.c
|
||||
+++ b/common/edid.c
|
||||
@@ -3579,7 +3579,7 @@ int add_cea_modes(struct hdmi_edid_data
|
||||
{
|
||||
const u8 *cea = drm_find_cea_extension(edid);
|
||||
const u8 *db, *hdmi = NULL, *video = NULL;
|
||||
- u8 dbl, hdmi_len, video_len = 0;
|
||||
+ u8 dbl, hdmi_len = 0, video_len = 0;
|
||||
int modes = 0;
|
||||
|
||||
if (cea && cea_revision(cea) >= 3) {
|
||||
--- a/include/command.h
|
||||
+++ b/include/command.h
|
||||
@@ -139,7 +139,7 @@ enum command_ret_t {
|
||||
* number of ticks the command took to complete.
|
||||
* @return 0 if the command succeeded, 1 if it failed
|
||||
*/
|
||||
-int cmd_process(int flag, int argc, char * const argv[],
|
||||
+enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
|
||||
int *repeatable, unsigned long *ticks);
|
||||
|
||||
void fixup_cmdtable(cmd_tbl_t *cmdtp, int size);
|
||||
@@ -8,12 +8,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=intel-microcode
|
||||
PKG_VERSION:=20240531
|
||||
PKG_VERSION:=20241112
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=intel-microcode_3.$(PKG_VERSION).1.tar.xz
|
||||
PKG_SOURCE_URL:=@DEBIAN/pool/non-free-firmware/i/intel-microcode/
|
||||
PKG_HASH:=808cbb57a790dab7060b59b31e70e54ac47d3798d75e9784ed57a65b9f951fc4
|
||||
PKG_HASH:=aeee844da5136ad8d9cd94661b8d6398a775780bf719f3ef28e9d9c9ee9fe819
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/intel-microcode-3.$(PKG_VERSION).1
|
||||
PKG_CPE_ID:=cpe:/a:intel:microcode
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||||
|
||||
struct vc4_hang_state *hang_state;
|
||||
|
||||
@@ -963,6 +967,9 @@ extern struct platform_driver vc4_dsi_dr
|
||||
@@ -964,6 +968,9 @@ extern struct platform_driver vc4_dsi_dr
|
||||
/* vc4_fence.c */
|
||||
extern const struct dma_fence_ops vc4_fence_ops;
|
||||
|
||||
|
||||
+6
-6
@@ -83,7 +83,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
struct drm_device;
|
||||
struct drm_gem_object;
|
||||
@@ -494,6 +495,17 @@ struct drm_encoder *vc4_find_encoder_by_
|
||||
@@ -495,6 +496,17 @@ struct drm_encoder *vc4_find_encoder_by_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
struct vc4_crtc_data {
|
||||
const char *name;
|
||||
|
||||
@@ -538,9 +550,19 @@ struct vc4_crtc {
|
||||
@@ -539,9 +551,19 @@ struct vc4_crtc {
|
||||
/* Timestamp at start of vblank irq - unaffected by lock delays. */
|
||||
ktime_t t_vblank;
|
||||
|
||||
@@ -126,7 +126,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -243,7 +243,8 @@ static void vc4_hvs_lut_load(struct vc4_
|
||||
@@ -248,7 +248,8 @@ static void vc4_hvs_lut_load(struct vc4_
|
||||
static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
|
||||
struct vc4_crtc *vc4_crtc)
|
||||
{
|
||||
@@ -136,7 +136,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
struct drm_color_lut *lut = crtc_state->gamma_lut->data;
|
||||
u32 length = drm_color_lut_size(crtc_state->gamma_lut);
|
||||
u32 i;
|
||||
@@ -257,6 +258,81 @@ static void vc4_hvs_update_gamma_lut(str
|
||||
@@ -262,6 +263,81 @@ static void vc4_hvs_update_gamma_lut(str
|
||||
vc4_hvs_lut_load(hvs, vc4_crtc);
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
|
||||
{
|
||||
struct drm_device *drm = &hvs->vc4->base;
|
||||
@@ -400,7 +476,10 @@ static int vc4_hvs_init_channel(struct v
|
||||
@@ -405,7 +481,10 @@ static int vc4_hvs_init_channel(struct v
|
||||
/* Reload the LUT, since the SRAMs would have been disabled if
|
||||
* all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
|
||||
*/
|
||||
@@ -230,7 +230,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
drm_dev_exit(idx);
|
||||
|
||||
@@ -646,7 +725,11 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
@@ -649,7 +728,11 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel));
|
||||
|
||||
if (crtc->state->gamma_lut) {
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -143,6 +143,85 @@ static int vc4_hvs_debugfs_dlist(struct
|
||||
@@ -145,6 +145,85 @@ static int vc4_hvs_debugfs_dlist(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
/* The filter kernel is composed of dwords each containing 3 9-bit
|
||||
* signed integers packed next to each other.
|
||||
*/
|
||||
@@ -850,11 +929,15 @@ int vc4_hvs_debugfs_init(struct drm_mino
|
||||
@@ -854,11 +933,15 @@ int vc4_hvs_debugfs_init(struct drm_mino
|
||||
if (!vc4->hvs)
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
+3
-3
@@ -45,7 +45,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||
--- a/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
@@ -613,6 +613,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4
|
||||
@@ -614,6 +614,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4
|
||||
return container_of_const(data, struct vc4_pv_data, base);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -596,6 +596,36 @@ out:
|
||||
@@ -599,6 +599,36 @@ out:
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
@@ -626,7 +656,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
|
||||
@@ -629,7 +659,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -548,8 +548,11 @@ static int vc4_hvs_init_channel(struct v
|
||||
@@ -553,8 +553,11 @@ static int vc4_hvs_init_channel(struct v
|
||||
dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
|
||||
dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE;
|
||||
|
||||
@@ -33,7 +33,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||||
(interlace ? SCALER_DISPBKGND_INTERLACE : 0));
|
||||
|
||||
/* Reload the LUT, since the SRAMs would have been disabled if
|
||||
@@ -834,18 +837,25 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
@@ -837,18 +840,25 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel));
|
||||
|
||||
if (crtc->state->gamma_lut) {
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -614,6 +614,16 @@ static int vc4_hvs_gamma_check(struct dr
|
||||
@@ -617,6 +617,16 @@ static int vc4_hvs_gamma_check(struct dr
|
||||
if (!crtc_state->color_mgmt_changed)
|
||||
return 0;
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ Signed-off-by: Dom Cobley <popcornmix@gmail.com>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
@@ -408,7 +408,7 @@ struct vc4_plane_state {
|
||||
@@ -409,7 +409,7 @@ struct vc4_plane_state {
|
||||
|
||||
/* Clipped coordinates of the plane on the display. */
|
||||
int crtc_x, crtc_y, crtc_w, crtc_h;
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
@@ -626,12 +626,7 @@ struct vc4_crtc_state {
|
||||
@@ -627,12 +627,7 @@ struct vc4_crtc_state {
|
||||
bool txp_armed;
|
||||
unsigned int assigned_channel;
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -976,6 +976,9 @@ int vc4_hvs_debugfs_init(struct drm_mino
|
||||
@@ -980,6 +980,9 @@ int vc4_hvs_debugfs_init(struct drm_mino
|
||||
struct vc4_dev *vc4 = to_vc4_dev(drm);
|
||||
struct vc4_hvs *hvs = vc4->hvs;
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ Signed-off-by: Dom Cobley <popcornmix@gmail.com>
|
||||
static const char * const output_format_str[] = {
|
||||
[VC4_HDMI_OUTPUT_RGB] = "RGB",
|
||||
[VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0",
|
||||
@@ -478,7 +484,9 @@ static int vc4_hdmi_connector_detect_ctx
|
||||
@@ -482,7 +488,9 @@ static int vc4_hdmi_connector_detect_ctx
|
||||
return connector_status_unknown;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ Signed-off-by: Dom Cobley <popcornmix@gmail.com>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
|
||||
@@ -2400,7 +2400,7 @@ static int vc4_hdmi_audio_startup(struct
|
||||
@@ -2404,7 +2404,7 @@ static int vc4_hdmi_audio_startup(struct
|
||||
}
|
||||
|
||||
if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) {
|
||||
|
||||
+13
-13
@@ -98,7 +98,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
}
|
||||
--- a/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
|
||||
@@ -332,6 +332,9 @@ struct vc4_hvs {
|
||||
@@ -333,6 +333,9 @@ struct vc4_hvs {
|
||||
struct drm_mm lbm_mm;
|
||||
spinlock_t mm_lock;
|
||||
|
||||
@@ -108,7 +108,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
struct drm_mm_node mitchell_netravali_filter;
|
||||
|
||||
struct debugfs_regset32 regset;
|
||||
@@ -619,10 +622,16 @@ struct drm_connector *vc4_get_crtc_conne
|
||||
@@ -620,10 +623,16 @@ struct drm_connector *vc4_get_crtc_conne
|
||||
struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state);
|
||||
|
||||
@@ -127,7 +127,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
bool txp_armed;
|
||||
unsigned int assigned_channel;
|
||||
|
||||
@@ -1032,6 +1041,8 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
|
||||
@@ -1033,6 +1042,8 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
|
||||
void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int output);
|
||||
int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output);
|
||||
u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo);
|
||||
@@ -138,7 +138,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
void vc4_hvs_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state);
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -412,6 +412,152 @@ static void vc5_hvs_update_gamma_lut(str
|
||||
@@ -417,6 +417,152 @@ static void vc5_hvs_update_gamma_lut(str
|
||||
vc5_hvs_lut_load(hvs, vc4_crtc);
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
|
||||
{
|
||||
struct drm_device *drm = &hvs->vc4->base;
|
||||
@@ -643,13 +789,12 @@ int vc4_hvs_atomic_check(struct drm_crtc
|
||||
@@ -646,13 +792,12 @@ int vc4_hvs_atomic_check(struct drm_crtc
|
||||
{
|
||||
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
|
||||
@@ -306,7 +306,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
/* The pixelvalve can only feed one encoder (and encoders are
|
||||
* 1:1 with connectors.)
|
||||
@@ -662,12 +807,11 @@ int vc4_hvs_atomic_check(struct drm_crtc
|
||||
@@ -665,12 +810,11 @@ int vc4_hvs_atomic_check(struct drm_crtc
|
||||
|
||||
dlist_count++; /* Account for SCALER_CTL0_END. */
|
||||
|
||||
@@ -324,7 +324,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
return vc4_hvs_gamma_check(crtc, state);
|
||||
}
|
||||
@@ -683,8 +827,9 @@ static void vc4_hvs_install_dlist(struct
|
||||
@@ -686,8 +830,9 @@ static void vc4_hvs_install_dlist(struct
|
||||
if (!drm_dev_enter(dev, &idx))
|
||||
return;
|
||||
|
||||
@@ -335,7 +335,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
@@ -711,8 +856,10 @@ static void vc4_hvs_update_dlist(struct
|
||||
@@ -714,8 +859,10 @@ static void vc4_hvs_update_dlist(struct
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
spin_unlock_irqrestore(&vc4_crtc->irq_lock, flags);
|
||||
}
|
||||
|
||||
@@ -769,8 +916,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
@@ -772,8 +919,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
struct vc4_plane_state *vc4_plane_state;
|
||||
bool debug_dump_regs = false;
|
||||
bool enable_bg_fill = false;
|
||||
@@ -357,7 +357,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
unsigned int zpos = 0;
|
||||
bool found = false;
|
||||
int idx;
|
||||
@@ -788,6 +934,9 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
@@ -791,6 +937,9 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
vc4_hvs_dump_state(hvs);
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
/* Copy all the active planes' dlist contents to the hardware dlist. */
|
||||
do {
|
||||
found = false;
|
||||
@@ -821,7 +970,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
@@ -824,7 +973,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
|
||||
writel(SCALER_CTL0_END, dlist_next);
|
||||
dlist_next++;
|
||||
|
||||
@@ -377,7 +377,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
if (enable_bg_fill)
|
||||
/* This sets a black background color fill, as is the case
|
||||
@@ -960,6 +1110,11 @@ static irqreturn_t vc4_hvs_irq_handler(i
|
||||
@@ -964,6 +1114,11 @@ static irqreturn_t vc4_hvs_irq_handler(i
|
||||
|
||||
irqret = IRQ_HANDLED;
|
||||
}
|
||||
@@ -389,7 +389,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
}
|
||||
|
||||
/* Clear every per-channel interrupt flag. */
|
||||
@@ -1014,6 +1169,9 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
|
||||
@@ -1018,6 +1173,9 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
|
||||
|
||||
spin_lock_init(&hvs->mm_lock);
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -452,6 +452,8 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
|
||||
@@ -457,6 +457,8 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
|
||||
if (!alloc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -466,6 +466,18 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
|
||||
@@ -471,6 +471,18 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
|
||||
return alloc;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
void vc4_hvs_mark_dlist_entry_stale(struct vc4_hvs *hvs,
|
||||
struct vc4_hvs_dlist_allocation *alloc)
|
||||
{
|
||||
@@ -553,9 +565,7 @@ static void vc4_hvs_dlist_free_work(stru
|
||||
@@ -558,9 +570,7 @@ static void vc4_hvs_dlist_free_work(stru
|
||||
if (!vc4_hvs_frcnt_lte(cur->target_frame_count, frcnt))
|
||||
continue;
|
||||
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ Signed-off-by: Maxime Ripard <maxime@cerno.tech>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
|
||||
@@ -490,6 +490,18 @@ void vc4_hvs_mark_dlist_entry_stale(stru
|
||||
@@ -495,6 +495,18 @@ void vc4_hvs_mark_dlist_entry_stale(stru
|
||||
if (!drm_mm_node_allocated(&alloc->mm_node))
|
||||
return;
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ Signed-off-by: Matthias Reichl <hias@horus.com>
|
||||
|
||||
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
|
||||
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
|
||||
@@ -2132,7 +2132,7 @@ vc4_hdmi_encoder_compute_config(const st
|
||||
@@ -2136,7 +2136,7 @@ vc4_hdmi_encoder_compute_config(const st
|
||||
{
|
||||
struct drm_device *dev = vc4_hdmi->connector.dev;
|
||||
struct drm_connector_state *conn_state = &vc4_state->base;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user