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