client_common.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // Copyright 2016 fatedier, fatedier@gmail.com
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package config
  15. import (
  16. "fmt"
  17. "os"
  18. "strconv"
  19. "strings"
  20. ini "github.com/vaughan0/go-ini"
  21. "github.com/fatedier/frp/models/auth"
  22. )
  23. // ClientCommonConf contains information for a client service. It is
  24. // recommended to use GetDefaultClientConf instead of creating this object
  25. // directly, so that all unspecified fields have reasonable default values.
  26. type ClientCommonConf struct {
  27. auth.ClientConfig
  28. // ServerAddr specifies the address of the server to connect to. By
  29. // default, this value is "0.0.0.0".
  30. ServerAddr string `json:"server_addr"`
  31. // ServerPort specifies the port to connect to the server on. By default,
  32. // this value is 7000.
  33. ServerPort int `json:"server_port"`
  34. // HTTPProxy specifies a proxy address to connect to the server through. If
  35. // this value is "", the server will be connected to directly. By default,
  36. // this value is read from the "http_proxy" environment variable.
  37. HTTPProxy string `json:"http_proxy"`
  38. // LogFile specifies a file where logs will be written to. This value will
  39. // only be used if LogWay is set appropriately. By default, this value is
  40. // "console".
  41. LogFile string `json:"log_file"`
  42. // LogWay specifies the way logging is managed. Valid values are "console"
  43. // or "file". If "console" is used, logs will be printed to stdout. If
  44. // "file" is used, logs will be printed to LogFile. By default, this value
  45. // is "console".
  46. LogWay string `json:"log_way"`
  47. // LogLevel specifies the minimum log level. Valid values are "trace",
  48. // "debug", "info", "warn", and "error". By default, this value is "info".
  49. LogLevel string `json:"log_level"`
  50. // LogMaxDays specifies the maximum number of days to store log information
  51. // before deletion. This is only used if LogWay == "file". By default, this
  52. // value is 0.
  53. LogMaxDays int64 `json:"log_max_days"`
  54. // DisableLogColor disables log colors when LogWay == "console" when set to
  55. // true. By default, this value is false.
  56. DisableLogColor bool `json:"disable_log_color"`
  57. // AdminAddr specifies the address that the admin server binds to. By
  58. // default, this value is "127.0.0.1".
  59. AdminAddr string `json:"admin_addr"`
  60. // AdminPort specifies the port for the admin server to listen on. If this
  61. // value is 0, the admin server will not be started. By default, this value
  62. // is 0.
  63. AdminPort int `json:"admin_port"`
  64. // AdminUser specifies the username that the admin server will use for
  65. // login. By default, this value is "admin".
  66. AdminUser string `json:"admin_user"`
  67. // AdminPwd specifies the password that the admin server will use for
  68. // login. By default, this value is "admin".
  69. AdminPwd string `json:"admin_pwd"`
  70. // AssetsDir specifies the local directory that the admin server will load
  71. // resources from. If this value is "", assets will be loaded from the
  72. // bundled executable using statik. By default, this value is "".
  73. AssetsDir string `json:"assets_dir"`
  74. // PoolCount specifies the number of connections the client will make to
  75. // the server in advance. By default, this value is 0.
  76. PoolCount int `json:"pool_count"`
  77. // TCPMux toggles TCP stream multiplexing. This allows multiple requests
  78. // from a client to share a single TCP connection. If this value is true,
  79. // the server must have TCP multiplexing enabled as well. By default, this
  80. // value is true.
  81. TCPMux bool `json:"tcp_mux"`
  82. // User specifies a prefix for proxy names to distinguish them from other
  83. // clients. If this value is not "", proxy names will automatically be
  84. // changed to "{user}.{proxy_name}". By default, this value is "".
  85. User string `json:"user"`
  86. // DNSServer specifies a DNS server address for FRPC to use. If this value
  87. // is "", the default DNS will be used. By default, this value is "".
  88. DNSServer string `json:"dns_server"`
  89. // LoginFailExit controls whether or not the client should exit after a
  90. // failed login attempt. If false, the client will retry until a login
  91. // attempt succeeds. By default, this value is true.
  92. LoginFailExit bool `json:"login_fail_exit"`
  93. // Start specifies a set of enabled proxies by name. If this set is empty,
  94. // all supplied proxies are enabled. By default, this value is an empty
  95. // set.
  96. Start map[string]struct{} `json:"start"`
  97. // Protocol specifies the protocol to use when interacting with the server.
  98. // Valid values are "tcp", "kcp" and "websocket". By default, this value
  99. // is "tcp".
  100. Protocol string `json:"protocol"`
  101. // TLSEnable specifies whether or not TLS should be used when communicating
  102. // with the server. If "tls_cert_file" and "tls_key_file" are valid,
  103. // client will load the supplied tls configuration.
  104. TLSEnable bool `json:"tls_enable"`
  105. // ClientTLSCertPath specifies the path of the cert file that client will
  106. // load. It only works when "tls_enable" is true and "tls_key_file" is valid.
  107. TLSCertFile string `json:"tls_cert_file"`
  108. // ClientTLSKeyPath specifies the path of the secret key file that client
  109. // will load. It only works when "tls_enable" is true and "tls_cert_file"
  110. // are valid.
  111. TLSKeyFile string `json:"tls_key_file"`
  112. // TrustedCaFile specifies the path of the trusted ca file that will load.
  113. // It only works when "tls_enable" is valid and tls configuration of server
  114. // has been specified.
  115. TLSTrustedCaFile string `json:"tls_trusted_ca_file"`
  116. // HeartBeatInterval specifies at what interval heartbeats are sent to the
  117. // server, in seconds. It is not recommended to change this value. By
  118. // default, this value is 30.
  119. HeartBeatInterval int64 `json:"heartbeat_interval"`
  120. // HeartBeatTimeout specifies the maximum allowed heartbeat response delay
  121. // before the connection is terminated, in seconds. It is not recommended
  122. // to change this value. By default, this value is 90.
  123. HeartBeatTimeout int64 `json:"heartbeat_timeout"`
  124. // Client meta info
  125. Metas map[string]string `json:"metas"`
  126. // UDPPacketSize specifies the udp packet size
  127. // By default, this value is 1500
  128. UDPPacketSize int64 `json:"udp_packet_size"`
  129. }
  130. // GetDefaultClientConf returns a client configuration with default values.
  131. func GetDefaultClientConf() ClientCommonConf {
  132. return ClientCommonConf{
  133. ServerAddr: "0.0.0.0",
  134. ServerPort: 7000,
  135. HTTPProxy: os.Getenv("http_proxy"),
  136. LogFile: "console",
  137. LogWay: "console",
  138. LogLevel: "info",
  139. LogMaxDays: 3,
  140. DisableLogColor: false,
  141. AdminAddr: "127.0.0.1",
  142. AdminPort: 0,
  143. AdminUser: "",
  144. AdminPwd: "",
  145. AssetsDir: "",
  146. PoolCount: 1,
  147. TCPMux: true,
  148. User: "",
  149. DNSServer: "",
  150. LoginFailExit: true,
  151. Start: make(map[string]struct{}),
  152. Protocol: "tcp",
  153. TLSEnable: false,
  154. TLSCertFile: "",
  155. TLSKeyFile: "",
  156. TLSTrustedCaFile: "",
  157. HeartBeatInterval: 30,
  158. HeartBeatTimeout: 90,
  159. Metas: make(map[string]string),
  160. UDPPacketSize: 1500,
  161. }
  162. }
  163. func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error) {
  164. cfg = GetDefaultClientConf()
  165. conf, err := ini.Load(strings.NewReader(content))
  166. if err != nil {
  167. return ClientCommonConf{}, fmt.Errorf("parse ini conf file error: %v", err)
  168. }
  169. cfg.ClientConfig = auth.UnmarshalClientConfFromIni(conf)
  170. var (
  171. tmpStr string
  172. ok bool
  173. v int64
  174. )
  175. if tmpStr, ok = conf.Get("common", "server_addr"); ok {
  176. cfg.ServerAddr = tmpStr
  177. }
  178. if tmpStr, ok = conf.Get("common", "server_port"); ok {
  179. v, err = strconv.ParseInt(tmpStr, 10, 64)
  180. if err != nil {
  181. err = fmt.Errorf("Parse conf error: invalid server_port")
  182. return
  183. }
  184. cfg.ServerPort = int(v)
  185. }
  186. if tmpStr, ok = conf.Get("common", "disable_log_color"); ok && tmpStr == "true" {
  187. cfg.DisableLogColor = true
  188. }
  189. if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
  190. cfg.HTTPProxy = tmpStr
  191. }
  192. if tmpStr, ok = conf.Get("common", "log_file"); ok {
  193. cfg.LogFile = tmpStr
  194. if cfg.LogFile == "console" {
  195. cfg.LogWay = "console"
  196. } else {
  197. cfg.LogWay = "file"
  198. }
  199. }
  200. if tmpStr, ok = conf.Get("common", "log_level"); ok {
  201. cfg.LogLevel = tmpStr
  202. }
  203. if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
  204. if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
  205. cfg.LogMaxDays = v
  206. }
  207. }
  208. if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
  209. cfg.AdminAddr = tmpStr
  210. }
  211. if tmpStr, ok = conf.Get("common", "admin_port"); ok {
  212. if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
  213. cfg.AdminPort = int(v)
  214. } else {
  215. err = fmt.Errorf("Parse conf error: invalid admin_port")
  216. return
  217. }
  218. }
  219. if tmpStr, ok = conf.Get("common", "admin_user"); ok {
  220. cfg.AdminUser = tmpStr
  221. }
  222. if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
  223. cfg.AdminPwd = tmpStr
  224. }
  225. if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
  226. cfg.AssetsDir = tmpStr
  227. }
  228. if tmpStr, ok = conf.Get("common", "pool_count"); ok {
  229. if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
  230. cfg.PoolCount = int(v)
  231. }
  232. }
  233. if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
  234. cfg.TCPMux = false
  235. } else {
  236. cfg.TCPMux = true
  237. }
  238. if tmpStr, ok = conf.Get("common", "user"); ok {
  239. cfg.User = tmpStr
  240. }
  241. if tmpStr, ok = conf.Get("common", "dns_server"); ok {
  242. cfg.DNSServer = tmpStr
  243. }
  244. if tmpStr, ok = conf.Get("common", "start"); ok {
  245. proxyNames := strings.Split(tmpStr, ",")
  246. for _, name := range proxyNames {
  247. cfg.Start[strings.TrimSpace(name)] = struct{}{}
  248. }
  249. }
  250. if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" {
  251. cfg.LoginFailExit = false
  252. } else {
  253. cfg.LoginFailExit = true
  254. }
  255. if tmpStr, ok = conf.Get("common", "protocol"); ok {
  256. // Now it only support tcp and kcp and websocket.
  257. if tmpStr != "tcp" && tmpStr != "kcp" && tmpStr != "websocket" {
  258. err = fmt.Errorf("Parse conf error: invalid protocol")
  259. return
  260. }
  261. cfg.Protocol = tmpStr
  262. }
  263. if tmpStr, ok = conf.Get("common", "tls_enable"); ok && tmpStr == "true" {
  264. cfg.TLSEnable = true
  265. } else {
  266. cfg.TLSEnable = false
  267. }
  268. if tmpStr, ok = conf.Get("common", "tls_cert_file"); ok {
  269. cfg.TLSCertFile = tmpStr
  270. }
  271. if tmpStr, ok := conf.Get("common", "tls_key_file"); ok {
  272. cfg.TLSKeyFile = tmpStr
  273. }
  274. if tmpStr, ok := conf.Get("common", "tls_trusted_ca_file"); ok {
  275. cfg.TLSTrustedCaFile = tmpStr
  276. }
  277. if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
  278. if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
  279. err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
  280. return
  281. }
  282. cfg.HeartBeatTimeout = v
  283. }
  284. if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
  285. if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
  286. err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
  287. return
  288. }
  289. cfg.HeartBeatInterval = v
  290. }
  291. for k, v := range conf.Section("common") {
  292. if strings.HasPrefix(k, "meta_") {
  293. cfg.Metas[strings.TrimPrefix(k, "meta_")] = v
  294. }
  295. }
  296. if tmpStr, ok = conf.Get("common", "udp_packet_size"); ok {
  297. if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
  298. err = fmt.Errorf("Parse conf error: invalid udp_packet_size")
  299. return
  300. }
  301. cfg.UDPPacketSize = v
  302. }
  303. return
  304. }
  305. func (cfg *ClientCommonConf) Check() (err error) {
  306. if cfg.HeartBeatInterval <= 0 {
  307. err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
  308. return
  309. }
  310. if cfg.HeartBeatTimeout < cfg.HeartBeatInterval {
  311. err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
  312. return
  313. }
  314. if cfg.TLSEnable == false {
  315. if cfg.TLSCertFile != "" {
  316. fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
  317. }
  318. if cfg.TLSKeyFile != "" {
  319. fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
  320. }
  321. if cfg.TLSTrustedCaFile != "" {
  322. fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
  323. }
  324. }
  325. return
  326. }