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