github.com/xraypb/xray-core@v1.6.6/infra/conf/trojan.go (about)

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