root.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. // Copyright 2018 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 sub
  15. import (
  16. "context"
  17. "fmt"
  18. "net"
  19. "os"
  20. "os/signal"
  21. "strconv"
  22. "strings"
  23. "syscall"
  24. "time"
  25. "github.com/fatedier/frp/client"
  26. "github.com/fatedier/frp/pkg/auth"
  27. "github.com/fatedier/frp/pkg/config"
  28. "github.com/fatedier/frp/pkg/util/log"
  29. "github.com/fatedier/frp/pkg/util/version"
  30. "github.com/spf13/cobra"
  31. )
  32. const (
  33. CfgFileTypeIni = iota
  34. CfgFileTypeCmd
  35. )
  36. var (
  37. cfgFile string
  38. showVersion bool
  39. serverAddr string
  40. user string
  41. protocol string
  42. token string
  43. logLevel string
  44. logFile string
  45. logMaxDays int
  46. disableLogColor bool
  47. proxyName string
  48. localIP string
  49. localPort int
  50. remotePort int
  51. useEncryption bool
  52. useCompression bool
  53. customDomains string
  54. subDomain string
  55. httpUser string
  56. httpPwd string
  57. locations string
  58. hostHeaderRewrite string
  59. role string
  60. sk string
  61. multiplexer string
  62. serverName string
  63. bindAddr string
  64. bindPort int
  65. tlsEnable bool
  66. kcpDoneCh chan struct{}
  67. )
  68. var services = make(map[string]*client.Service)
  69. func GetUids() (uids []string) {
  70. keys := make([]string, 0, len(services))
  71. for k := range services {
  72. keys = append(keys, k)
  73. }
  74. return keys
  75. }
  76. func IsRunning(uid string) (running bool) {
  77. return services[uid] != nil
  78. }
  79. func getServiceByUid(uid string) (svr *client.Service) {
  80. return services[uid]
  81. }
  82. func putServiceByUid(uid string, svr *client.Service) {
  83. services[uid] = svr
  84. }
  85. func delServiceByUid(uid string) {
  86. delete(services, uid)
  87. }
  88. func Close(uid string) (ret bool) {
  89. svr := getServiceByUid(uid)
  90. if svr != nil {
  91. svr.Close()
  92. delServiceByUid(uid)
  93. return true
  94. }
  95. return false
  96. }
  97. func init() {
  98. rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
  99. rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
  100. kcpDoneCh = make(chan struct{})
  101. }
  102. func RegisterCommonFlags(cmd *cobra.Command) {
  103. cmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
  104. cmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
  105. cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket")
  106. cmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
  107. cmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
  108. cmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
  109. cmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
  110. cmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
  111. cmd.PersistentFlags().BoolVarP(&tlsEnable, "tls_enable", "", false, "enable frpc tls")
  112. }
  113. var rootCmd = &cobra.Command{
  114. Use: "frpc",
  115. Short: "frpc is the client of frp (https://github.com/fatedier/frp)",
  116. RunE: func(cmd *cobra.Command, args []string) error {
  117. if showVersion {
  118. fmt.Println(version.Full())
  119. return nil
  120. }
  121. // Do not show command usage here.
  122. err := runClient(cfgFile)
  123. if err != nil {
  124. fmt.Println(err)
  125. os.Exit(1)
  126. }
  127. return nil
  128. },
  129. }
  130. func Execute() {
  131. if err := rootCmd.Execute(); err != nil {
  132. os.Exit(1)
  133. }
  134. }
  135. func handleSignal(svr *client.Service) {
  136. ch := make(chan os.Signal)
  137. signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
  138. <-ch
  139. svr.GracefulClose(500 * time.Millisecond)
  140. close(kcpDoneCh)
  141. }
  142. func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
  143. cfg = config.GetDefaultClientConf()
  144. ipStr, portStr, err := net.SplitHostPort(serverAddr)
  145. if err != nil {
  146. err = fmt.Errorf("invalid server_addr: %v", err)
  147. return
  148. }
  149. cfg.ServerAddr = ipStr
  150. cfg.ServerPort, err = strconv.Atoi(portStr)
  151. if err != nil {
  152. err = fmt.Errorf("invalid server_addr: %v", err)
  153. return
  154. }
  155. cfg.User = user
  156. cfg.Protocol = protocol
  157. cfg.LogLevel = logLevel
  158. cfg.LogFile = logFile
  159. cfg.LogMaxDays = int64(logMaxDays)
  160. cfg.DisableLogColor = disableLogColor
  161. // Only token authentication is supported in cmd mode
  162. cfg.ClientConfig = auth.GetDefaultClientConf()
  163. cfg.Token = token
  164. cfg.TLSEnable = tlsEnable
  165. cfg.Complete()
  166. if err = cfg.Validate(); err != nil {
  167. err = fmt.Errorf("Parse config error: %v", err)
  168. return
  169. }
  170. return
  171. }
  172. func parseClientCommonCfg(fileType int, source []byte) (cfg config.ClientCommonConf, err error) {
  173. if fileType == CfgFileTypeIni {
  174. cfg, err = config.UnmarshalClientConfFromIni(source)
  175. } else if fileType == CfgFileTypeCmd {
  176. cfg, err = parseClientCommonCfgFromCmd()
  177. }
  178. if err != nil {
  179. return
  180. }
  181. err = cfg.Validate()
  182. if err != nil {
  183. return
  184. }
  185. return
  186. }
  187. func RunClient(cfgFilePath string) (err error, svr *client.Service) {
  188. var content []byte
  189. content, err = config.GetRenderedConfFromFile(cfgFilePath)
  190. if err != nil {
  191. return
  192. }
  193. cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
  194. if err != nil {
  195. return
  196. }
  197. pxyCfgs, visitorCfgs, err := config.LoadAllProxyConfsFromIni(cfg.User, content, cfg.Start)
  198. if err != nil {
  199. return
  200. }
  201. return startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
  202. }
  203. func RunClientWithUid(uid string, cfgFilePath string) (err error, svr *client.Service) {
  204. var content []byte
  205. content, err = config.GetRenderedConfFromFile(cfgFilePath)
  206. if err != nil {
  207. return
  208. }
  209. cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
  210. if err != nil {
  211. return
  212. }
  213. pxyCfgs, visitorCfgs, err := config.LoadAllProxyConfsFromIni(cfg.User, content, cfg.Start)
  214. if err != nil {
  215. return
  216. }
  217. return startServiceWithUid(uid, cfg, pxyCfgs, visitorCfgs)
  218. }
  219. func RunClientByContent(uid string, cfgContent string) (err error, svr *client.Service) {
  220. content, err := config.RenderContent([]byte(cfgContent))
  221. if err != nil {
  222. return
  223. }
  224. cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
  225. if err != nil {
  226. return
  227. }
  228. pxyCfgs, visitorCfgs, err := config.LoadAllProxyConfsFromIni(cfg.User, content, cfg.Start)
  229. if err != nil {
  230. return
  231. }
  232. return startServiceWithUid(uid, cfg, pxyCfgs, visitorCfgs)
  233. }
  234. func runClient(cfgFilePath string) error {
  235. cfg, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(cfgFilePath)
  236. if err != nil {
  237. return err
  238. }
  239. err, _ = startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
  240. return err
  241. }
  242. func startServiceWithUid(
  243. uid string,
  244. cfg config.ClientCommonConf,
  245. pxyCfgs map[string]config.ProxyConf,
  246. visitorCfgs map[string]config.VisitorConf,
  247. ) (err error, svr *client.Service) {
  248. defer delServiceByUid(uid)
  249. log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
  250. cfg.LogMaxDays, cfg.DisableLogColor)
  251. if cfg.DNSServer != "" {
  252. s := cfg.DNSServer
  253. if !strings.Contains(s, ":") {
  254. s += ":53"
  255. }
  256. // Change default dns server for frpc
  257. net.DefaultResolver = &net.Resolver{
  258. PreferGo: true,
  259. Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
  260. return net.Dial("udp", s)
  261. },
  262. }
  263. }
  264. svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
  265. if errRet != nil {
  266. err = errRet
  267. return
  268. }
  269. // Capture the exit signal if we use kcp.
  270. if cfg.Protocol == "kcp" {
  271. go handleSignal(svr)
  272. }
  273. putServiceByUid(uid, svr)
  274. err = svr.Run()
  275. if err == nil && cfg.Protocol == "kcp" {
  276. <-kcpDoneCh
  277. }
  278. return err, svr
  279. }
  280. func startService(
  281. cfg config.ClientCommonConf,
  282. pxyCfgs map[string]config.ProxyConf,
  283. visitorCfgs map[string]config.VisitorConf,
  284. cfgFile string,
  285. ) (err error, svr *client.Service) {
  286. log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
  287. cfg.LogMaxDays, cfg.DisableLogColor)
  288. if cfg.DNSServer != "" {
  289. s := cfg.DNSServer
  290. if !strings.Contains(s, ":") {
  291. s += ":53"
  292. }
  293. // Change default dns server for frpc
  294. net.DefaultResolver = &net.Resolver{
  295. PreferGo: true,
  296. Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
  297. return net.Dial("udp", s)
  298. },
  299. }
  300. }
  301. svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
  302. if errRet != nil {
  303. err = errRet
  304. return
  305. }
  306. // Capture the exit signal if we use kcp.
  307. if cfg.Protocol == "kcp" {
  308. go handleSignal(svr)
  309. }
  310. err = svr.Run()
  311. if err == nil && cfg.Protocol == "kcp" {
  312. <-kcpDoneCh
  313. }
  314. return err, svr
  315. }