github.com/v2fly/v2ray-core/v4@v4.45.2/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/v2fly/v2ray-core/v4/common/net" 12 "github.com/v2fly/v2ray-core/v4/common/protocol" 13 "github.com/v2fly/v2ray-core/v4/common/serial" 14 "github.com/v2fly/v2ray-core/v4/infra/conf/cfgcommon" 15 "github.com/v2fly/v2ray-core/v4/proxy/trojan" 16 ) 17 18 // TrojanServerTarget is configuration of a single trojan server 19 type TrojanServerTarget struct { 20 Address *cfgcommon.Address `json:"address"` 21 Port uint16 `json:"port"` 22 Password string `json:"password"` 23 Email string `json:"email"` 24 Level byte `json:"level"` 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 } 54 trojan := &protocol.ServerEndpoint{ 55 Address: rec.Address.Build(), 56 Port: uint32(rec.Port), 57 User: []*protocol.User{ 58 { 59 Level: uint32(rec.Level), 60 Email: rec.Email, 61 Account: serial.ToTypedMessage(account), 62 }, 63 }, 64 } 65 66 serverSpecs[idx] = trojan 67 } 68 69 config.Server = serverSpecs 70 71 return config, nil 72 } 73 74 // TrojanInboundFallback is fallback configuration 75 type TrojanInboundFallback struct { 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 } 89 90 // TrojanServerConfig is Inbound configuration 91 type TrojanServerConfig struct { 92 Clients []*TrojanUserConfig `json:"clients"` 93 Fallback json.RawMessage `json:"fallback"` 94 Fallbacks []*TrojanInboundFallback `json:"fallbacks"` 95 } 96 97 // Build implements Buildable 98 func (c *TrojanServerConfig) Build() (proto.Message, error) { 99 config := new(trojan.ServerConfig) 100 config.Users = make([]*protocol.User, len(c.Clients)) 101 for idx, rawUser := range c.Clients { 102 user := new(protocol.User) 103 account := &trojan.Account{ 104 Password: rawUser.Password, 105 } 106 107 user.Email = rawUser.Email 108 user.Level = uint32(rawUser.Level) 109 user.Account = serial.ToTypedMessage(account) 110 config.Users[idx] = user 111 } 112 113 if c.Fallback != nil { 114 return nil, newError(`Trojan settings: please use "fallbacks":[{}] instead of "fallback":{}`) 115 } 116 for _, fb := range c.Fallbacks { 117 var i uint16 118 var s string 119 if err := json.Unmarshal(fb.Dest, &i); err == nil { 120 s = strconv.Itoa(int(i)) 121 } else { 122 _ = json.Unmarshal(fb.Dest, &s) 123 } 124 config.Fallbacks = append(config.Fallbacks, &trojan.Fallback{ 125 Alpn: fb.Alpn, 126 Path: fb.Path, 127 Type: fb.Type, 128 Dest: s, 129 Xver: fb.Xver, 130 }) 131 } 132 for _, fb := range config.Fallbacks { 133 /* 134 if fb.Alpn == "h2" && fb.Path != "" { 135 return nil, newError(`Trojan fallbacks: "alpn":"h2" doesn't support "path"`) 136 } 137 */ 138 if fb.Path != "" && fb.Path[0] != '/' { 139 return nil, newError(`Trojan fallbacks: "path" must be empty or start with "/"`) 140 } 141 if fb.Type == "" && fb.Dest != "" { 142 if fb.Dest == "serve-ws-none" { 143 fb.Type = "serve" 144 } else { 145 switch fb.Dest[0] { 146 case '@', '/': 147 fb.Type = "unix" 148 if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") { 149 fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy 150 copy(fullAddr, fb.Dest[1:]) 151 fb.Dest = string(fullAddr) 152 } 153 default: 154 if _, err := strconv.Atoi(fb.Dest); err == nil { 155 fb.Dest = "127.0.0.1:" + fb.Dest 156 } 157 if _, _, err := net.SplitHostPort(fb.Dest); err == nil { 158 fb.Type = "tcp" 159 } 160 } 161 } 162 } 163 if fb.Type == "" { 164 return nil, newError(`Trojan fallbacks: please fill in a valid value for every "dest"`) 165 } 166 if fb.Xver > 2 { 167 return nil, newError(`Trojan fallbacks: invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) 168 } 169 } 170 171 return config, nil 172 }