From f650d87083e86296023ec5b6b00f0b8678a5b630 Mon Sep 17 00:00:00 2001 From: nobody <59990325+vrnobody@users.noreply.github.com> Date: Sun, 11 Aug 2024 00:09:07 +0800 Subject: [PATCH] Commands: Add convert with two sub-commands (#3661) * Add back convert-configs-to-protobuf command. * Add convert-typedMessage-to-json command. * Add -debug and -type arguments into convert.pb sub-command. --------- Co-authored-by: nobody --- main/commands/all/commands.go | 3 +- main/commands/all/convert.go | 126 -------------------------- main/commands/all/convert/convert.go | 17 ++++ main/commands/all/convert/json.go | 71 +++++++++++++++ main/commands/all/convert/protobuf.go | 81 +++++++++++++++++ 5 files changed, 171 insertions(+), 127 deletions(-) delete mode 100644 main/commands/all/convert.go create mode 100644 main/commands/all/convert/convert.go create mode 100644 main/commands/all/convert/json.go create mode 100644 main/commands/all/convert/protobuf.go diff --git a/main/commands/all/commands.go b/main/commands/all/commands.go index 41d0e0f1..99f194dc 100644 --- a/main/commands/all/commands.go +++ b/main/commands/all/commands.go @@ -2,6 +2,7 @@ package all import ( "github.com/xtls/xray-core/main/commands/all/api" + "github.com/xtls/xray-core/main/commands/all/convert" "github.com/xtls/xray-core/main/commands/all/tls" "github.com/xtls/xray-core/main/commands/base" ) @@ -12,7 +13,7 @@ func init() { base.RootCommand.Commands = append( base.RootCommand.Commands, api.CmdAPI, - // cmdConvert, + convert.CmdConvert, tls.CmdTLS, cmdUUID, cmdX25519, diff --git a/main/commands/all/convert.go b/main/commands/all/convert.go deleted file mode 100644 index eab66d14..00000000 --- a/main/commands/all/convert.go +++ /dev/null @@ -1,126 +0,0 @@ -package all - -import ( - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "os" - "strings" - "time" - - "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/infra/conf" - "github.com/xtls/xray-core/infra/conf/serial" - "github.com/xtls/xray-core/main/commands/base" - "google.golang.org/protobuf/proto" -) - -var cmdConvert = &base.Command{ - UsageLine: "{{.Exec}} convert [json file] [json file] ...", - Short: "Convert multiple json config to protobuf", - Long: ` -Convert multiple json config to protobuf. - -Examples: - - {{.Exec}} convert config.json c1.json c2.json .json - `, -} - -func init() { - cmdConvert.Run = executeConvert // break init loop -} - -func executeConvert(cmd *base.Command, args []string) { - unnamedArgs := cmdConvert.Flag.Args() - if len(unnamedArgs) < 1 { - base.Fatalf("empty config list") - } - - conf := &conf.Config{} - for _, arg := range unnamedArgs { - fmt.Fprintf(os.Stderr, "Read config: %s", arg) - r, err := loadArg(arg) - common.Must(err) - c, err := serial.DecodeJSONConfig(r) - if err != nil { - base.Fatalf(err.Error()) - } - conf.Override(c, arg) - } - - pbConfig, err := conf.Build() - if err != nil { - base.Fatalf(err.Error()) - } - - bytesConfig, err := proto.Marshal(pbConfig) - if err != nil { - base.Fatalf("failed to marshal proto config: %s", err) - } - - if _, err := os.Stdout.Write(bytesConfig); err != nil { - base.Fatalf("failed to write proto config: %s", err) - } -} - -// loadArg loads one arg, maybe an remote url, or local file path -func loadArg(arg string) (out io.Reader, err error) { - var data []byte - switch { - case strings.HasPrefix(arg, "http://"), strings.HasPrefix(arg, "https://"): - data, err = FetchHTTPContent(arg) - - case arg == "stdin:": - data, err = io.ReadAll(os.Stdin) - - default: - data, err = os.ReadFile(arg) - } - - if err != nil { - return - } - out = bytes.NewBuffer(data) - return -} - -// FetchHTTPContent dials https for remote content -func FetchHTTPContent(target string) ([]byte, error) { - parsedTarget, err := url.Parse(target) - if err != nil { - return nil, errors.New("invalid URL: ", target).Base(err) - } - - if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" { - return nil, errors.New("invalid scheme: ", parsedTarget.Scheme) - } - - client := &http.Client{ - Timeout: 30 * time.Second, - } - resp, err := client.Do(&http.Request{ - Method: "GET", - URL: parsedTarget, - Close: true, - }) - if err != nil { - return nil, errors.New("failed to dial to ", target).Base(err) - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return nil, errors.New("unexpected HTTP status code: ", resp.StatusCode) - } - - content, err := buf.ReadAllToBytes(resp.Body) - if err != nil { - return nil, errors.New("failed to read HTTP response").Base(err) - } - - return content, nil -} diff --git a/main/commands/all/convert/convert.go b/main/commands/all/convert/convert.go new file mode 100644 index 00000000..3543b2e9 --- /dev/null +++ b/main/commands/all/convert/convert.go @@ -0,0 +1,17 @@ +package convert + +import ( + "github.com/xtls/xray-core/main/commands/base" +) + +// CmdConvert do config convertion +var CmdConvert = &base.Command{ + UsageLine: "{{.Exec}} convert", + Short: "Convert configs", + Long: `{{.Exec}} {{.LongName}} provides tools to convert config. +`, + Commands: []*base.Command{ + cmdProtobuf, + cmdJson, + }, +} diff --git a/main/commands/all/convert/json.go b/main/commands/all/convert/json.go new file mode 100644 index 00000000..d20634df --- /dev/null +++ b/main/commands/all/convert/json.go @@ -0,0 +1,71 @@ +package convert + +import ( + "encoding/json" + "fmt" + "io" + + creflect "github.com/xtls/xray-core/common/reflect" + cserial "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/main/commands/base" + "github.com/xtls/xray-core/main/confloader" +) + +var cmdJson = &base.Command{ + CustomFlags: true, + UsageLine: "{{.Exec}} convert json [-type] [stdin:] [typedMessage file] ", + Short: "Convert typedMessage to json", + Long: ` +Convert ONE typedMessage to json. + +Where typedMessage file need to be in the following format: + +{ + "type": "xray.proxy.shadowsocks.Account", + "value": "CgMxMTEQBg==" +} + +Arguments: + + -t, -type + Inject type infomation. + +Examples: + + {{.Exec}} convert json user.tmsg + `, + Run: executeTypedMessageToJson, +} + +func executeTypedMessageToJson(cmd *base.Command, args []string) { + + var injectTypeInfo bool + cmd.Flag.BoolVar(&injectTypeInfo, "t", false, "") + cmd.Flag.BoolVar(&injectTypeInfo, "type", false, "") + cmd.Flag.Parse(args) + + if cmd.Flag.NArg() < 1 { + base.Fatalf("empty input list") + } + + reader, err := confloader.LoadConfig(cmd.Flag.Arg(0)) + if err != nil { + base.Fatalf(err.Error()) + } + + b, err := io.ReadAll(reader) + if err != nil { + base.Fatalf(err.Error()) + } + + tm := cserial.TypedMessage{} + if err = json.Unmarshal(b, &tm); err != nil { + base.Fatalf(err.Error()) + } + + if j, ok := creflect.MarshalToJson(&tm, injectTypeInfo); ok { + fmt.Println(j) + } else { + base.Fatalf("marshal TypedMessage to json failed") + } +} diff --git a/main/commands/all/convert/protobuf.go b/main/commands/all/convert/protobuf.go new file mode 100644 index 00000000..17d6feae --- /dev/null +++ b/main/commands/all/convert/protobuf.go @@ -0,0 +1,81 @@ +package convert + +import ( + "fmt" + "os" + + "github.com/xtls/xray-core/common/cmdarg" + creflect "github.com/xtls/xray-core/common/reflect" + "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/main/commands/base" + + "google.golang.org/protobuf/proto" +) + +var cmdProtobuf = &base.Command{ + CustomFlags: true, + UsageLine: "{{.Exec}} convert pb [-debug] [-type] [json file] [json file] ...", + Short: "Convert multiple json configs to protobuf", + Long: ` +Convert multiple json configs to protobuf. + +Arguments: + + -d, -debug + Show mix.pb as json. + FOR DEBUGGING ONLY! + DO NOT PASS THIS OUTPUT TO XRAY-CORE! + + -t, -type + Inject type information into debug output. + +Examples: + + {{.Exec}} convert pb config.json c1.json c2.json c3.json > mix.pb + `, + Run: executeConvertConfigsToProtobuf, +} + +func executeConvertConfigsToProtobuf(cmd *base.Command, args []string) { + + var optDump bool + var optType bool + + cmd.Flag.BoolVar(&optDump, "d", false, "") + cmd.Flag.BoolVar(&optDump, "debug", false, "") + cmd.Flag.BoolVar(&optType, "t", false, "") + cmd.Flag.BoolVar(&optType, "type", false, "") + cmd.Flag.Parse(args) + + unnamedArgs := cmdarg.Arg{} + for _, v := range cmd.Flag.Args() { + unnamedArgs.Set(v) + } + + if len(unnamedArgs) < 1 { + base.Fatalf("empty config list") + } + + pbConfig, err := core.LoadConfig("auto", unnamedArgs) + if err != nil { + base.Fatalf(err.Error()) + } + + if optDump { + if j, ok := creflect.MarshalToJson(pbConfig, optType); ok { + fmt.Println(j) + return + } else { + base.Fatalf("failed to marshal proto config to json.") + } + } + + bytesConfig, err := proto.Marshal(pbConfig) + if err != nil { + base.Fatalf("failed to marshal proto config: %s", err) + } + + if _, err := os.Stdout.Write(bytesConfig); err != nil { + base.Fatalf("failed to write proto config: %s", err) + } +}