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 }