github.com/moqsien/xraycore@v1.8.5/infra/conf/trojan.go (about)

     1  package conf
     2  
     3  import (
     4  	"encoding/json"
     5  	"runtime"
     6  	"strconv"
     7  	"syscall"
     8  
     9  	"github.com/moqsien/xraycore/common/net"
    10  	"github.com/moqsien/xraycore/common/protocol"
    11  	"github.com/moqsien/xraycore/common/serial"
    12  	"github.com/moqsien/xraycore/proxy/trojan"
    13  	"google.golang.org/protobuf/proto"
    14  )
    15  
    16  // TrojanServerTarget is configuration of a single trojan server
    17  type TrojanServerTarget struct {
    18  	Address  *Address `json:"address"`
    19  	Port     uint16   `json:"port"`
    20  	Password string   `json:"password"`
    21  	Email    string   `json:"email"`
    22  	Level    byte     `json:"level"`
    23  	Flow     string   `json:"flow"`
    24  }
    25  
    26  // TrojanClientConfig is configuration of trojan servers
    27  type TrojanClientConfig struct {
    28  	Servers []*TrojanServerTarget `json:"servers"`
    29  }
    30  
    31  // Build implements Buildable
    32  func (c *TrojanClientConfig) Build() (proto.Message, error) {
    33  	if len(c.Servers) == 0 {
    34  		return nil, newError("0 Trojan server configured.")
    35  	}
    36  
    37  	config := &trojan.ClientConfig{
    38  		Server: make([]*protocol.ServerEndpoint, len(c.Servers)),
    39  	}
    40  
    41  	for idx, rec := range c.Servers {
    42  		if rec.Address == nil {
    43  			return nil, newError("Trojan server address is not set.")
    44  		}
    45  		if rec.Port == 0 {
    46  			return nil, newError("Invalid Trojan port.")
    47  		}
    48  		if rec.Password == "" {
    49  			return nil, newError("Trojan password is not specified.")
    50  		}
    51  		if rec.Flow != "" {
    52  			return nil, newError(`Trojan doesn't support "flow" anymore.`)
    53  		}
    54  
    55  		config.Server[idx] = &protocol.ServerEndpoint{
    56  			Address: rec.Address.Build(),
    57  			Port:    uint32(rec.Port),
    58  			User: []*protocol.User{
    59  				{
    60  					Level: uint32(rec.Level),
    61  					Email: rec.Email,
    62  					Account: serial.ToTypedMessage(&trojan.Account{
    63  						Password: rec.Password,
    64  					}),
    65  				},
    66  			},
    67  		}
    68  	}
    69  
    70  	return config, nil
    71  }
    72  
    73  // TrojanInboundFallback is fallback configuration
    74  type TrojanInboundFallback struct {
    75  	Name string          `json:"name"`
    76  	Alpn string          `json:"alpn"`
    77  	Path string          `json:"path"`
    78  	Type string          `json:"type"`
    79  	Dest json.RawMessage `json:"dest"`
    80  	Xver uint64          `json:"xver"`
    81  }
    82  
    83  // TrojanUserConfig is user configuration
    84  type TrojanUserConfig struct {
    85  	Password string `json:"password"`
    86  	Level    byte   `json:"level"`
    87  	Email    string `json:"email"`
    88  	Flow     string `json:"flow"`
    89  }
    90  
    91  // TrojanServerConfig is Inbound configuration
    92  type TrojanServerConfig struct {
    93  	Clients   []*TrojanUserConfig      `json:"clients"`
    94  	Fallback  *TrojanInboundFallback   `json:"fallback"`
    95  	Fallbacks []*TrojanInboundFallback `json:"fallbacks"`
    96  }
    97  
    98  // Build implements Buildable
    99  func (c *TrojanServerConfig) Build() (proto.Message, error) {
   100  	config := &trojan.ServerConfig{
   101  		Users: make([]*protocol.User, len(c.Clients)),
   102  	}
   103  
   104  	for idx, rawUser := range c.Clients {
   105  		if rawUser.Flow != "" {
   106  			return nil, newError(`Trojan doesn't support "flow" anymore.`)
   107  		}
   108  
   109  		config.Users[idx] = &protocol.User{
   110  			Level: uint32(rawUser.Level),
   111  			Email: rawUser.Email,
   112  			Account: serial.ToTypedMessage(&trojan.Account{
   113  				Password: rawUser.Password,
   114  			}),
   115  		}
   116  	}
   117  
   118  	if c.Fallback != nil {
   119  		return nil, newError(`Trojan settings: please use "fallbacks":[{}] instead of "fallback":{}`)
   120  	}
   121  	for _, fb := range c.Fallbacks {
   122  		var i uint16
   123  		var s string
   124  		if err := json.Unmarshal(fb.Dest, &i); err == nil {
   125  			s = strconv.Itoa(int(i))
   126  		} else {
   127  			_ = json.Unmarshal(fb.Dest, &s)
   128  		}
   129  		config.Fallbacks = append(config.Fallbacks, &trojan.Fallback{
   130  			Name: fb.Name,
   131  			Alpn: fb.Alpn,
   132  			Path: fb.Path,
   133  			Type: fb.Type,
   134  			Dest: s,
   135  			Xver: fb.Xver,
   136  		})
   137  	}
   138  	for _, fb := range config.Fallbacks {
   139  		/*
   140  			if fb.Alpn == "h2" && fb.Path != "" {
   141  				return nil, newError(`Trojan fallbacks: "alpn":"h2" doesn't support "path"`)
   142  			}
   143  		*/
   144  		if fb.Path != "" && fb.Path[0] != '/' {
   145  			return nil, newError(`Trojan fallbacks: "path" must be empty or start with "/"`)
   146  		}
   147  		if fb.Type == "" && fb.Dest != "" {
   148  			if fb.Dest == "serve-ws-none" {
   149  				fb.Type = "serve"
   150  			} else {
   151  				switch fb.Dest[0] {
   152  				case '@', '/':
   153  					fb.Type = "unix"
   154  					if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
   155  						fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
   156  						copy(fullAddr, fb.Dest[1:])
   157  						fb.Dest = string(fullAddr)
   158  					}
   159  				default:
   160  					if _, err := strconv.Atoi(fb.Dest); err == nil {
   161  						fb.Dest = "127.0.0.1:" + fb.Dest
   162  					}
   163  					if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
   164  						fb.Type = "tcp"
   165  					}
   166  				}
   167  			}
   168  		}
   169  		if fb.Type == "" {
   170  			return nil, newError(`Trojan fallbacks: please fill in a valid value for every "dest"`)
   171  		}
   172  		if fb.Xver > 2 {
   173  			return nil, newError(`Trojan fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`)
   174  		}
   175  	}
   176  
   177  	return config, nil
   178  }