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