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 }