mirror of
				https://github.com/v2fly/v2ray-core.git
				synced 2025-11-03 19:29:22 +00:00 
			
		
		
		
	v5: API commands (rebased from a17fc40651213ec841f8bd5a17f253eedd36f2b6)
This commit is contained in:
		
							parent
							
								
									54a627c443
								
							
						
					
					
						commit
						0506757caf
					
				@ -1,145 +0,0 @@
 | 
			
		||||
package all
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
 | 
			
		||||
	logService "github.com/v2fly/v2ray-core/v4/app/log/command"
 | 
			
		||||
	statsService "github.com/v2fly/v2ray-core/v4/app/stats/command"
 | 
			
		||||
	"github.com/v2fly/v2ray-core/v4/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// cmdAPI calls an API in an V2Ray process
 | 
			
		||||
var cmdAPI = &base.Command{
 | 
			
		||||
	UsageLine: "{{.Exec}} api [-server 127.0.0.1:8080] <action> <parameter>",
 | 
			
		||||
	Short:     "Call V2Ray API",
 | 
			
		||||
	Long: `
 | 
			
		||||
Call V2Ray API, API calls in this command have a timeout to the server of 3 seconds.
 | 
			
		||||
 | 
			
		||||
The following methods are currently supported:
 | 
			
		||||
 | 
			
		||||
	LoggerService.RestartLogger
 | 
			
		||||
	StatsService.GetStats
 | 
			
		||||
	StatsService.QueryStats
 | 
			
		||||
 | 
			
		||||
Examples:
 | 
			
		||||
 | 
			
		||||
	{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 LoggerService.RestartLogger '' 
 | 
			
		||||
	{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 StatsService.QueryStats 'pattern: "" reset: false'
 | 
			
		||||
	{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 StatsService.GetStats 'name: "inbound>>>statin>>>traffic>>>downlink" reset: false'
 | 
			
		||||
	{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 StatsService.GetSysStats ''
 | 
			
		||||
	`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	cmdAPI.Run = executeAPI // break init loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	apiServerAddrPtr = cmdAPI.Flag.String("server", "127.0.0.1:8080", "")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func executeAPI(cmd *base.Command, args []string) {
 | 
			
		||||
	unnamedArgs := cmdAPI.Flag.Args()
 | 
			
		||||
	if len(unnamedArgs) < 2 {
 | 
			
		||||
		base.Fatalf("service name or request not specified.")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	service, method := getServiceMethod(unnamedArgs[0])
 | 
			
		||||
	handler, found := serivceHandlerMap[strings.ToLower(service)]
 | 
			
		||||
	if !found {
 | 
			
		||||
		base.Fatalf("unknown service: %s", service)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
 | 
			
		||||
	conn, err := grpc.DialContext(ctx, *apiServerAddrPtr, grpc.WithInsecure(), grpc.WithBlock())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		base.Fatalf("failed to dial %s", *apiServerAddrPtr)
 | 
			
		||||
	}
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	response, err := handler(ctx, conn, method, unnamedArgs[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		base.Fatalf("failed to call service %s: %s", unnamedArgs[0], err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Println(response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getServiceMethod(s string) (string, string) {
 | 
			
		||||
	ss := strings.Split(s, ".")
 | 
			
		||||
	service := ss[0]
 | 
			
		||||
	var method string
 | 
			
		||||
	if len(ss) > 1 {
 | 
			
		||||
		method = ss[1]
 | 
			
		||||
	}
 | 
			
		||||
	return service, method
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type serviceHandler func(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error)
 | 
			
		||||
 | 
			
		||||
var serivceHandlerMap = map[string]serviceHandler{
 | 
			
		||||
	"statsservice":  callStatsService,
 | 
			
		||||
	"loggerservice": callLogService,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func callLogService(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error) {
 | 
			
		||||
	client := logService.NewLoggerServiceClient(conn)
 | 
			
		||||
 | 
			
		||||
	switch strings.ToLower(method) {
 | 
			
		||||
	case "restartlogger":
 | 
			
		||||
		r := &logService.RestartLoggerRequest{}
 | 
			
		||||
		resp, err := client.RestartLogger(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return proto.MarshalTextString(resp), nil
 | 
			
		||||
	default:
 | 
			
		||||
		return "", errors.New("Unknown method: " + method)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func callStatsService(ctx context.Context, conn *grpc.ClientConn, method string, request string) (string, error) {
 | 
			
		||||
	client := statsService.NewStatsServiceClient(conn)
 | 
			
		||||
 | 
			
		||||
	switch strings.ToLower(method) {
 | 
			
		||||
	case "getstats":
 | 
			
		||||
		r := &statsService.GetStatsRequest{}
 | 
			
		||||
		if err := proto.UnmarshalText(request, r); err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := client.GetStats(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return proto.MarshalTextString(resp), nil
 | 
			
		||||
	case "querystats":
 | 
			
		||||
		r := &statsService.QueryStatsRequest{}
 | 
			
		||||
		if err := proto.UnmarshalText(request, r); err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := client.QueryStats(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return proto.MarshalTextString(resp), nil
 | 
			
		||||
	case "getsysstats":
 | 
			
		||||
		// SysStatsRequest is an empty message
 | 
			
		||||
		r := &statsService.SysStatsRequest{}
 | 
			
		||||
		resp, err := client.GetSysStats(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return proto.MarshalTextString(resp), nil
 | 
			
		||||
	default:
 | 
			
		||||
		return "", errors.New("Unknown method: " + method)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								commands/all/api/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								commands/all/api/api.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdAPI calls an API in an V2Ray process
 | 
			
		||||
var CmdAPI = &base.Command{
 | 
			
		||||
	UsageLine: "{{.Exec}} api",
 | 
			
		||||
	Short:     "Call V2Ray API",
 | 
			
		||||
	Long: `{{.Exec}} {{.LongName}} provides tools to manipulate V2Ray via its API.
 | 
			
		||||
`,
 | 
			
		||||
	Commands: []*base.Command{
 | 
			
		||||
		cmdRestartLogger,
 | 
			
		||||
		cmdGetStats,
 | 
			
		||||
		cmdQueryStats,
 | 
			
		||||
		cmdSysStats,
 | 
			
		||||
		cmdAddInbounds,
 | 
			
		||||
		cmdAddOutbounds,
 | 
			
		||||
		cmdRemoveInbounds,
 | 
			
		||||
		cmdRemoveOutbounds,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								commands/all/api/inbounds_add.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								commands/all/api/inbounds_add.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	handlerService "v2ray.com/core/app/proxyman/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
	"v2ray.com/core/infra/conf"
 | 
			
		||||
	"v2ray.com/core/infra/conf/serial"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdAddInbounds = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api adi [--server=127.0.0.1:8080] <c1.json> [c2.json]...",
 | 
			
		||||
	Short:       "Add inbounds",
 | 
			
		||||
	Long: `
 | 
			
		||||
Add inbounds to V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
    {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeAddInbounds,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeAddInbounds(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
	unnamedArgs := cmd.Flag.Args()
 | 
			
		||||
	if len(unnamedArgs) == 0 {
 | 
			
		||||
		fmt.Println("reading from stdin:")
 | 
			
		||||
		unnamedArgs = []string{"stdin:"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ins := make([]conf.InboundDetourConfig, 0)
 | 
			
		||||
	for _, arg := range unnamedArgs {
 | 
			
		||||
		r, err := loadArg(arg)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to load %s: %s", arg, err)
 | 
			
		||||
		}
 | 
			
		||||
		conf, err := serial.DecodeJSONConfig(r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to decode %s: %s", arg, err)
 | 
			
		||||
		}
 | 
			
		||||
		ins = append(ins, conf.InboundConfigs...)
 | 
			
		||||
	}
 | 
			
		||||
	if len(ins) == 0 {
 | 
			
		||||
		base.Fatalf("no valid inbound found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := handlerService.NewHandlerServiceClient(conn)
 | 
			
		||||
	for _, in := range ins {
 | 
			
		||||
		fmt.Println("adding:", in.Tag)
 | 
			
		||||
		i, err := in.Build()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to build conf: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		r := &handlerService.AddInboundRequest{
 | 
			
		||||
			Inbound: i,
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := client.AddInbound(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to add inbound: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		showResponese(resp)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								commands/all/api/inbounds_remove.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								commands/all/api/inbounds_remove.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	handlerService "v2ray.com/core/app/proxyman/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
	"v2ray.com/core/infra/conf/serial"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdRemoveInbounds = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api rmi [--server=127.0.0.1:8080] <json_file|tag> [json_file] [tag]...",
 | 
			
		||||
	Short:       "Remove inbounds",
 | 
			
		||||
	Long: `
 | 
			
		||||
Remove inbounds from V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
    {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeRemoveInbounds,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeRemoveInbounds(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
	unnamedArgs := cmd.Flag.Args()
 | 
			
		||||
	if len(unnamedArgs) == 0 {
 | 
			
		||||
		fmt.Println("reading from stdin:")
 | 
			
		||||
		unnamedArgs = []string{"stdin:"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tags := make([]string, 0)
 | 
			
		||||
	for _, arg := range unnamedArgs {
 | 
			
		||||
		if r, err := loadArg(arg); err == nil {
 | 
			
		||||
			conf, err := serial.DecodeJSONConfig(r)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				base.Fatalf("failed to decode %s: %s", arg, err)
 | 
			
		||||
			}
 | 
			
		||||
			ins := conf.InboundConfigs
 | 
			
		||||
			for _, i := range ins {
 | 
			
		||||
				tags = append(tags, i.Tag)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// take request as tag
 | 
			
		||||
			tags = append(tags, arg)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(tags) == 0 {
 | 
			
		||||
		base.Fatalf("no inbound to remove")
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println("removing inbounds:", tags)
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := handlerService.NewHandlerServiceClient(conn)
 | 
			
		||||
	for _, tag := range tags {
 | 
			
		||||
		fmt.Println("removing:", tag)
 | 
			
		||||
		r := &handlerService.RemoveInboundRequest{
 | 
			
		||||
			Tag: tag,
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := client.RemoveInbound(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to remove inbound: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		showResponese(resp)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								commands/all/api/logger_restart.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								commands/all/api/logger_restart.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	logService "v2ray.com/core/app/log/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdRestartLogger = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api restartlogger [--server=127.0.0.1:8080]",
 | 
			
		||||
	Short:       "Restart the logger",
 | 
			
		||||
	Long: `
 | 
			
		||||
Restart the logger of V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeRestartLogger,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeRestartLogger(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := logService.NewLoggerServiceClient(conn)
 | 
			
		||||
	r := &logService.RestartLoggerRequest{}
 | 
			
		||||
	resp, err := client.RestartLogger(ctx, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		base.Fatalf("failed to restart logger: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	showResponese(resp)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								commands/all/api/outbounds_add.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								commands/all/api/outbounds_add.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	handlerService "v2ray.com/core/app/proxyman/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
	"v2ray.com/core/infra/conf"
 | 
			
		||||
	"v2ray.com/core/infra/conf/serial"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdAddOutbounds = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api ado [--server=127.0.0.1:8080] <c1.json> [c2.json]...",
 | 
			
		||||
	Short:       "Add outbounds",
 | 
			
		||||
	Long: `
 | 
			
		||||
Add outbounds to V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
    {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeAddOutbounds,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeAddOutbounds(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
	unnamedArgs := cmd.Flag.Args()
 | 
			
		||||
	if len(unnamedArgs) == 0 {
 | 
			
		||||
		fmt.Println("Reading from STDIN")
 | 
			
		||||
		unnamedArgs = []string{"stdin:"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	outs := make([]conf.OutboundDetourConfig, 0)
 | 
			
		||||
	for _, arg := range unnamedArgs {
 | 
			
		||||
		r, err := loadArg(arg)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to load %s: %s", arg, err)
 | 
			
		||||
		}
 | 
			
		||||
		conf, err := serial.DecodeJSONConfig(r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to decode %s: %s", arg, err)
 | 
			
		||||
		}
 | 
			
		||||
		outs = append(outs, conf.OutboundConfigs...)
 | 
			
		||||
	}
 | 
			
		||||
	if len(outs) == 0 {
 | 
			
		||||
		base.Fatalf("no valid outbound found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := handlerService.NewHandlerServiceClient(conn)
 | 
			
		||||
	for _, out := range outs {
 | 
			
		||||
		fmt.Println("adding:", out.Tag)
 | 
			
		||||
		o, err := out.Build()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to build conf: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		r := &handlerService.AddOutboundRequest{
 | 
			
		||||
			Outbound: o,
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := client.AddOutbound(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to add outbound: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		showResponese(resp)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								commands/all/api/outbounds_remove.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								commands/all/api/outbounds_remove.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	handlerService "v2ray.com/core/app/proxyman/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
	"v2ray.com/core/infra/conf/serial"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdRemoveOutbounds = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api rmo [--server=127.0.0.1:8080] <json_file|tag> [json_file] [tag]...",
 | 
			
		||||
	Short:       "Remove outbounds",
 | 
			
		||||
	Long: `
 | 
			
		||||
Remove outbounds from V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
    {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json "tag name"
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeRemoveOutbounds,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeRemoveOutbounds(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
	unnamedArgs := cmd.Flag.Args()
 | 
			
		||||
	if len(unnamedArgs) == 0 {
 | 
			
		||||
		fmt.Println("reading from stdin:")
 | 
			
		||||
		unnamedArgs = []string{"stdin:"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tags := make([]string, 0)
 | 
			
		||||
	for _, arg := range unnamedArgs {
 | 
			
		||||
		if r, err := loadArg(arg); err == nil {
 | 
			
		||||
			conf, err := serial.DecodeJSONConfig(r)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				base.Fatalf("failed to decode %s: %s", arg, err)
 | 
			
		||||
			}
 | 
			
		||||
			outs := conf.OutboundConfigs
 | 
			
		||||
			for _, o := range outs {
 | 
			
		||||
				tags = append(tags, o.Tag)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// take request as tag
 | 
			
		||||
			tags = append(tags, arg)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(tags) == 0 {
 | 
			
		||||
		base.Fatalf("no outbound to remove")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := handlerService.NewHandlerServiceClient(conn)
 | 
			
		||||
	for _, tag := range tags {
 | 
			
		||||
		fmt.Println("removing:", tag)
 | 
			
		||||
		r := &handlerService.RemoveOutboundRequest{
 | 
			
		||||
			Tag: tag,
 | 
			
		||||
		}
 | 
			
		||||
		resp, err := client.RemoveOutbound(ctx, r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			base.Fatalf("failed to remove outbound: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		showResponese(resp)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								commands/all/api/shared.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								commands/all/api/shared.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,118 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/grpc"
 | 
			
		||||
	"google.golang.org/protobuf/proto"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
	"v2ray.com/core/common/buf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type serviceHandler func(ctx context.Context, conn *grpc.ClientConn, cmd *base.Command, args []string) string
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	apiServerAddrPtr string
 | 
			
		||||
	apiTimeout       int
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func setSharedFlags(cmd *base.Command) {
 | 
			
		||||
	cmd.Flag.StringVar(&apiServerAddrPtr, "s", "127.0.0.1:8080", "")
 | 
			
		||||
	cmd.Flag.StringVar(&apiServerAddrPtr, "server", "127.0.0.1:8080", "")
 | 
			
		||||
	cmd.Flag.IntVar(&apiTimeout, "t", 3, "")
 | 
			
		||||
	cmd.Flag.IntVar(&apiTimeout, "timeout", 3, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dialAPIServer() (conn *grpc.ClientConn, ctx context.Context, close func()) {
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(apiTimeout)*time.Second)
 | 
			
		||||
	conn, err := grpc.DialContext(ctx, apiServerAddrPtr, grpc.WithInsecure(), grpc.WithBlock())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		base.Fatalf("failed to dial %s", apiServerAddrPtr)
 | 
			
		||||
	}
 | 
			
		||||
	close = func() {
 | 
			
		||||
		cancel()
 | 
			
		||||
		conn.Close()
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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 = ioutil.ReadAll(os.Stdin)
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		data, err = ioutil.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, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if s := strings.ToLower(parsedTarget.Scheme); s != "http" && s != "https" {
 | 
			
		||||
		return nil, fmt.Errorf("invalid scheme: %s", 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, fmt.Errorf("failed to dial to %s", target)
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
		return nil, fmt.Errorf("unexpected HTTP status code: %d", resp.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	content, err := buf.ReadAllToBytes(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to read HTTP response")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return content, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func showResponese(m proto.Message) {
 | 
			
		||||
	msg := ""
 | 
			
		||||
	bs, err := proto.Marshal(m)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		msg = err.Error()
 | 
			
		||||
	} else {
 | 
			
		||||
		msg = string(bs)
 | 
			
		||||
		msg = strings.TrimSpace(msg)
 | 
			
		||||
	}
 | 
			
		||||
	if msg == "" {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println(msg)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								commands/all/api/stats_get.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								commands/all/api/stats_get.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	statsService "v2ray.com/core/app/stats/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdGetStats = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api stats [--server=127.0.0.1:8080] [-name '']",
 | 
			
		||||
	Short:       "Get statistics",
 | 
			
		||||
	Long: `
 | 
			
		||||
Get statistics from V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
 | 
			
		||||
	-name
 | 
			
		||||
		Name of the stat counter.
 | 
			
		||||
 | 
			
		||||
	-reset
 | 
			
		||||
		Reset the counter to fetching its value.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
	{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -name "inbound>>>statin>>>traffic>>>downlink"
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeGetStats,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeGetStats(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	statName := cmd.Flag.String("name", "", "")
 | 
			
		||||
	reset := cmd.Flag.Bool("reset", false, "")
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := statsService.NewStatsServiceClient(conn)
 | 
			
		||||
	r := &statsService.GetStatsRequest{
 | 
			
		||||
		Name:   *statName,
 | 
			
		||||
		Reset_: *reset,
 | 
			
		||||
	}
 | 
			
		||||
	resp, err := client.GetStats(ctx, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		base.Fatalf("failed to get stats: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	showResponese(resp)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								commands/all/api/stats_query.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								commands/all/api/stats_query.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	statsService "v2ray.com/core/app/stats/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdQueryStats = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api statsquery [--server=127.0.0.1:8080] [-pattern '']",
 | 
			
		||||
	Short:       "Query statistics",
 | 
			
		||||
	Long: `
 | 
			
		||||
Query statistics from V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
 | 
			
		||||
	-pattern
 | 
			
		||||
		Pattern of the query.
 | 
			
		||||
 | 
			
		||||
	-reset
 | 
			
		||||
		Reset the counter to fetching its value.
 | 
			
		||||
 | 
			
		||||
Example:
 | 
			
		||||
 | 
			
		||||
	{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -pattern "counter_"
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeQueryStats,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeQueryStats(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	pattern := cmd.Flag.String("pattern", "", "")
 | 
			
		||||
	reset := cmd.Flag.Bool("reset", false, "")
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := statsService.NewStatsServiceClient(conn)
 | 
			
		||||
	r := &statsService.QueryStatsRequest{
 | 
			
		||||
		Pattern: *pattern,
 | 
			
		||||
		Reset_:  *reset,
 | 
			
		||||
	}
 | 
			
		||||
	resp, err := client.QueryStats(ctx, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		base.Fatalf("failed to query stats: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	showResponese(resp)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								commands/all/api/stats_sys.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								commands/all/api/stats_sys.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	statsService "v2ray.com/core/app/stats/command"
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdSysStats = &base.Command{
 | 
			
		||||
	CustomFlags: true,
 | 
			
		||||
	UsageLine:   "{{.Exec}} api statssys [--server=127.0.0.1:8080]",
 | 
			
		||||
	Short:       "Get system statistics",
 | 
			
		||||
	Long: `
 | 
			
		||||
Get system statistics from V2Ray.
 | 
			
		||||
 | 
			
		||||
Arguments:
 | 
			
		||||
 | 
			
		||||
	-s, -server 
 | 
			
		||||
		The API server address. Default 127.0.0.1:8080
 | 
			
		||||
 | 
			
		||||
	-t, -timeout
 | 
			
		||||
		Timeout seconds to call API. Default 3
 | 
			
		||||
`,
 | 
			
		||||
	Run: executeSysStats,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func executeSysStats(cmd *base.Command, args []string) {
 | 
			
		||||
	setSharedFlags(cmd)
 | 
			
		||||
	cmd.Flag.Parse(args)
 | 
			
		||||
 | 
			
		||||
	conn, ctx, close := dialAPIServer()
 | 
			
		||||
	defer close()
 | 
			
		||||
 | 
			
		||||
	client := statsService.NewStatsServiceClient(conn)
 | 
			
		||||
	r := &statsService.SysStatsRequest{}
 | 
			
		||||
	resp, err := client.GetSysStats(ctx, r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		base.Fatalf("failed to get sys stats: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	showResponese(resp)
 | 
			
		||||
}
 | 
			
		||||
@ -1,16 +1,20 @@
 | 
			
		||||
package all
 | 
			
		||||
 | 
			
		||||
import "github.com/v2fly/v2ray-core/v4/commands/base"
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/v2fly/v2ray-core/v4/commands/all/api"
 | 
			
		||||
	"github.com/v2fly/v2ray-core/v4/commands/all/tls"
 | 
			
		||||
	"github.com/v2fly/v2ray-core/v4/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// go:generate go run v2ray.com/core/common/errors/errorgen
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	base.RootCommand.Commands = append(
 | 
			
		||||
		base.RootCommand.Commands,
 | 
			
		||||
		cmdAPI,
 | 
			
		||||
		api.CmdAPI,
 | 
			
		||||
		cmdConvert,
 | 
			
		||||
		cmdLove,
 | 
			
		||||
		cmdTLS,
 | 
			
		||||
		tls.CmdTLS,
 | 
			
		||||
		cmdUUID,
 | 
			
		||||
		cmdVerify,
 | 
			
		||||
		cmdMerge,
 | 
			
		||||
 | 
			
		||||
@ -1,18 +0,0 @@
 | 
			
		||||
package all
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/v2fly/v2ray-core/v4/commands/all/tlscmd"
 | 
			
		||||
	"github.com/v2fly/v2ray-core/v4/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdTLS = &base.Command{
 | 
			
		||||
	UsageLine: "{{.Exec}} tls",
 | 
			
		||||
	Short:     "TLS tools",
 | 
			
		||||
	Long: `{{.Exec}} tls provides tools for TLS.
 | 
			
		||||
	`,
 | 
			
		||||
 | 
			
		||||
	Commands: []*base.Command{
 | 
			
		||||
		tlscmd.CmdCert,
 | 
			
		||||
		tlscmd.CmdPing,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package tlscmd
 | 
			
		||||
package tls
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
@ -14,8 +14,8 @@ import (
 | 
			
		||||
	"github.com/v2fly/v2ray-core/v4/common/task"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdCert is the tls cert command
 | 
			
		||||
var CmdCert = &base.Command{
 | 
			
		||||
// cmdCert is the tls cert command
 | 
			
		||||
var cmdCert = &base.Command{
 | 
			
		||||
	UsageLine: "{{.Exec}} tls cert [--ca] [--domain=v2ray.com] [--expire=240h]",
 | 
			
		||||
	Short:     "Generate TLS certificates",
 | 
			
		||||
	Long: `
 | 
			
		||||
@ -44,22 +44,22 @@ Arguments:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	CmdCert.Run = executeCert // break init loop
 | 
			
		||||
	cmdCert.Run = executeCert // break init loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	certDomainNames stringList
 | 
			
		||||
	_               = func() bool {
 | 
			
		||||
		CmdCert.Flag.Var(&certDomainNames, "domain", "Domain name for the certificate")
 | 
			
		||||
		cmdCert.Flag.Var(&certDomainNames, "domain", "Domain name for the certificate")
 | 
			
		||||
		return true
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	certCommonName   = CmdCert.Flag.String("name", "V2Ray Inc", "The common name of this certificate")
 | 
			
		||||
	certOrganization = CmdCert.Flag.String("org", "V2Ray Inc", "Organization of the certificate")
 | 
			
		||||
	certIsCA         = CmdCert.Flag.Bool("ca", false, "Whether this certificate is a CA")
 | 
			
		||||
	certJSONOutput   = CmdCert.Flag.Bool("json", true, "Print certificate in JSON format")
 | 
			
		||||
	certFileOutput   = CmdCert.Flag.String("file", "", "Save certificate in file.")
 | 
			
		||||
	certExpire       = CmdCert.Flag.Duration("expire", time.Hour*24*90 /* 90 days */, "Time until the certificate expires. Default value 3 months.")
 | 
			
		||||
	certCommonName   = cmdCert.Flag.String("name", "V2Ray Inc", "The common name of this certificate")
 | 
			
		||||
	certOrganization = cmdCert.Flag.String("org", "V2Ray Inc", "Organization of the certificate")
 | 
			
		||||
	certIsCA         = cmdCert.Flag.Bool("ca", false, "Whether this certificate is a CA")
 | 
			
		||||
	certJSONOutput   = cmdCert.Flag.Bool("json", true, "Print certificate in JSON format")
 | 
			
		||||
	certFileOutput   = cmdCert.Flag.String("file", "", "Save certificate in file.")
 | 
			
		||||
	certExpire       = cmdCert.Flag.Duration("expire", time.Hour*24*90 /* 90 days */, "Time until the certificate expires. Default value 3 months.")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func executeCert(cmd *base.Command, args []string) {
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package tlscmd
 | 
			
		||||
package tls
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
@ -11,8 +11,8 @@ import (
 | 
			
		||||
	v2tls "github.com/v2fly/v2ray-core/v4/transport/internet/tls"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdPing is the tls ping command
 | 
			
		||||
var CmdPing = &base.Command{
 | 
			
		||||
// cmdPing is the tls ping command
 | 
			
		||||
var cmdPing = &base.Command{
 | 
			
		||||
	UsageLine: "{{.Exec}} tls ping [-ip <ip>] <domain>",
 | 
			
		||||
	Short:     "Ping the domain with TLS handshake",
 | 
			
		||||
	Long: `
 | 
			
		||||
@ -26,19 +26,19 @@ Arguments:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	CmdPing.Run = executePing // break init loop
 | 
			
		||||
	cmdPing.Run = executePing // break init loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	pingIPStr = CmdPing.Flag.String("ip", "", "")
 | 
			
		||||
	pingIPStr = cmdPing.Flag.String("ip", "", "")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func executePing(cmd *base.Command, args []string) {
 | 
			
		||||
	if CmdPing.Flag.NArg() < 1 {
 | 
			
		||||
	if cmdPing.Flag.NArg() < 1 {
 | 
			
		||||
		base.Fatalf("domain not specified")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	domain := CmdPing.Flag.Arg(0)
 | 
			
		||||
	domain := cmdPing.Flag.Arg(0)
 | 
			
		||||
	fmt.Println("Tls ping: ", domain)
 | 
			
		||||
 | 
			
		||||
	var ip net.IP
 | 
			
		||||
							
								
								
									
										18
									
								
								commands/all/tls/tls.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								commands/all/tls/tls.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
package tls
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"v2ray.com/core/commands/base"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdTLS holds all tls sub commands
 | 
			
		||||
var CmdTLS = &base.Command{
 | 
			
		||||
	UsageLine: "{{.Exec}} tls",
 | 
			
		||||
	Short:     "TLS tools",
 | 
			
		||||
	Long: `{{.Exec}} {{.LongName}} provides tools for TLS.
 | 
			
		||||
	`,
 | 
			
		||||
 | 
			
		||||
	Commands: []*base.Command{
 | 
			
		||||
		cmdCert,
 | 
			
		||||
		cmdPing,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
@ -22,13 +22,19 @@ type Command struct {
 | 
			
		||||
	Run func(cmd *Command, args []string)
 | 
			
		||||
 | 
			
		||||
	// UsageLine is the one-line usage message.
 | 
			
		||||
	// The words between "go" and the first flag or argument in the line are taken to be the command name.
 | 
			
		||||
	// The words between the first word (the "executable name") and the first flag or argument in the line are taken to be the command name.
 | 
			
		||||
	//
 | 
			
		||||
	// UsageLine supports go template syntax. It's recommended to use "{{.Exec}}" instead of hardcoding name
 | 
			
		||||
	UsageLine string
 | 
			
		||||
 | 
			
		||||
	// Short is the short description shown in the 'go help' output.
 | 
			
		||||
	//
 | 
			
		||||
	// Note: Short does not support go template syntax.
 | 
			
		||||
	Short string
 | 
			
		||||
 | 
			
		||||
	// Long is the long message shown in the 'go help <this-command>' output.
 | 
			
		||||
	//
 | 
			
		||||
	// Long supports go template syntax. It's recommended to use "{{.Exec}}", "{{.LongName}}" instead of hardcoding strings
 | 
			
		||||
	Long string
 | 
			
		||||
 | 
			
		||||
	// Flag is a set of flags specific to this command.
 | 
			
		||||
@ -44,16 +50,18 @@ type Command struct {
 | 
			
		||||
	Commands []*Command
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument,
 | 
			
		||||
// LongName returns the command's long name: all the words in the usage line between first word (e.g. "v2ray") and a flag or argument,
 | 
			
		||||
func (c *Command) LongName() string {
 | 
			
		||||
	name := c.UsageLine
 | 
			
		||||
	if i := strings.Index(name, " ["); i >= 0 {
 | 
			
		||||
		name = name[:i]
 | 
			
		||||
		name = strings.TrimSpace(name[:i])
 | 
			
		||||
	}
 | 
			
		||||
	if name == CommandEnv.Exec {
 | 
			
		||||
		return ""
 | 
			
		||||
	if i := strings.Index(name, " "); i >= 0 {
 | 
			
		||||
		name = name[i+1:]
 | 
			
		||||
	} else {
 | 
			
		||||
		name = ""
 | 
			
		||||
	}
 | 
			
		||||
	return strings.TrimPrefix(name, CommandEnv.Exec+" ")
 | 
			
		||||
	return strings.TrimSpace(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name returns the command's short name: the last word in the usage line before a flag or argument.
 | 
			
		||||
@ -62,11 +70,12 @@ func (c *Command) Name() string {
 | 
			
		||||
	if i := strings.LastIndex(name, " "); i >= 0 {
 | 
			
		||||
		name = name[i+1:]
 | 
			
		||||
	}
 | 
			
		||||
	return name
 | 
			
		||||
	return strings.TrimSpace(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Usage prints usage of the Command
 | 
			
		||||
func (c *Command) Usage() {
 | 
			
		||||
	buildCommandText(c)
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
 | 
			
		||||
	fmt.Fprintf(os.Stderr, "Run '%s help %s' for details.\n", CommandEnv.Exec, c.LongName())
 | 
			
		||||
	SetExitStatus(2)
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,6 @@ import (
 | 
			
		||||
 | 
			
		||||
// Execute excute the commands
 | 
			
		||||
func Execute() {
 | 
			
		||||
	buildCommandsText(RootCommand)
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
	args := flag.Args()
 | 
			
		||||
	if len(args) < 1 {
 | 
			
		||||
@ -61,6 +60,7 @@ BigCmdLoop:
 | 
			
		||||
				args = cmd.Flag.Args()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			buildCommandText(cmd)
 | 
			
		||||
			cmd.Run(cmd, args)
 | 
			
		||||
			Exit()
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,7 @@ Args:
 | 
			
		||||
	if len(cmd.Commands) > 0 {
 | 
			
		||||
		PrintUsage(os.Stdout, cmd)
 | 
			
		||||
	} else {
 | 
			
		||||
		buildCommandText(cmd)
 | 
			
		||||
		tmpl(os.Stdout, helpTemplate, makeTmplData(cmd))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -119,24 +120,21 @@ func width(width int, value string) string {
 | 
			
		||||
 | 
			
		||||
// PrintUsage prints usage of cmd to w
 | 
			
		||||
func PrintUsage(w io.Writer, cmd *Command) {
 | 
			
		||||
	buildCommandText(cmd)
 | 
			
		||||
	bw := bufio.NewWriter(w)
 | 
			
		||||
	tmpl(bw, usageTemplate, makeTmplData(cmd))
 | 
			
		||||
	bw.Flush()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// buildCommandsText build text of command and its children as template
 | 
			
		||||
func buildCommandsText(cmd *Command) {
 | 
			
		||||
	buildCommandText(cmd)
 | 
			
		||||
	for _, cmd := range cmd.Commands {
 | 
			
		||||
		buildCommandsText(cmd)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// buildCommandText build command text as template
 | 
			
		||||
func buildCommandText(cmd *Command) {
 | 
			
		||||
	cmd.UsageLine = buildText(cmd.UsageLine, makeTmplData(cmd))
 | 
			
		||||
	cmd.Short = buildText(cmd.Short, makeTmplData(cmd))
 | 
			
		||||
	cmd.Long = buildText(cmd.Long, makeTmplData(cmd))
 | 
			
		||||
	data := makeTmplData(cmd)
 | 
			
		||||
	cmd.UsageLine = buildText(cmd.UsageLine, data)
 | 
			
		||||
	// DO NOT SUPPORT ".Short":
 | 
			
		||||
	// - It's not necessary
 | 
			
		||||
	// - Or, we have to build text for all sub commands of current command, like "v2ray help api"
 | 
			
		||||
	// cmd.Short = buildText(cmd.Short, data)
 | 
			
		||||
	cmd.Long = buildText(cmd.Long, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildText(text string, data interface{}) string {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user