root.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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/spf13/cobra"
  26. "github.com/fatedier/frp/client"
  27. "github.com/fatedier/frp/models/config"
  28. "github.com/fatedier/frp/utils/log"
  29. "github.com/fatedier/frp/utils/version"
  30. )
  31. const (
  32. CfgFileTypeIni = iota
  33. CfgFileTypeCmd
  34. )
  35. var (
  36. cfgFile string
  37. showVersion bool
  38. serverAddr string
  39. user string
  40. protocol string
  41. token string
  42. logLevel string
  43. logFile string
  44. logMaxDays int
  45. disableLogColor bool
  46. proxyName string
  47. localIp string
  48. localPort int
  49. remotePort int
  50. useEncryption bool
  51. useCompression bool
  52. customDomains string
  53. subDomain string
  54. httpUser string
  55. httpPwd string
  56. locations string
  57. hostHeaderRewrite string
  58. role string
  59. sk string
  60. serverName string
  61. bindAddr string
  62. bindPort int
  63. kcpDoneCh chan struct{}
  64. )
  65. func init() {
  66. rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
  67. rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
  68. kcpDoneCh = make(chan struct{})
  69. }
  70. var rootCmd = &cobra.Command{
  71. Use: "frpc",
  72. Short: "frpc is the client of frp (https://github.com/fatedier/frp)",
  73. RunE: func(cmd *cobra.Command, args []string) error {
  74. if showVersion {
  75. fmt.Println(version.Full())
  76. return nil
  77. }
  78. // Do not show command usage here.
  79. err := runClient(cfgFile)
  80. if err != nil {
  81. fmt.Println(err)
  82. os.Exit(1)
  83. }
  84. return nil
  85. },
  86. }
  87. func Execute() {
  88. if err := rootCmd.Execute(); err != nil {
  89. os.Exit(1)
  90. }
  91. }
  92. func handleSignal(svr *client.Service) {
  93. ch := make(chan os.Signal)
  94. signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
  95. <-ch
  96. svr.Close()
  97. time.Sleep(250 * time.Millisecond)
  98. close(kcpDoneCh)
  99. }
  100. func parseClientCommonCfg(fileType int, content string) (cfg config.ClientCommonConf, err error) {
  101. if fileType == CfgFileTypeIni {
  102. cfg, err = parseClientCommonCfgFromIni(content)
  103. } else if fileType == CfgFileTypeCmd {
  104. cfg, err = parseClientCommonCfgFromCmd()
  105. }
  106. if err != nil {
  107. return
  108. }
  109. err = cfg.Check()
  110. if err != nil {
  111. return
  112. }
  113. return
  114. }
  115. func parseClientCommonCfgFromIni(content string) (config.ClientCommonConf, error) {
  116. cfg, err := config.UnmarshalClientConfFromIni(content)
  117. if err != nil {
  118. return config.ClientCommonConf{}, err
  119. }
  120. return cfg, err
  121. }
  122. func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
  123. cfg = config.GetDefaultClientConf()
  124. strs := strings.Split(serverAddr, ":")
  125. if len(strs) < 2 {
  126. err = fmt.Errorf("invalid server_addr")
  127. return
  128. }
  129. if strs[0] != "" {
  130. cfg.ServerAddr = strs[0]
  131. }
  132. cfg.ServerPort, err = strconv.Atoi(strs[1])
  133. if err != nil {
  134. err = fmt.Errorf("invalid server_addr")
  135. return
  136. }
  137. cfg.User = user
  138. cfg.Protocol = protocol
  139. cfg.Token = token
  140. cfg.LogLevel = logLevel
  141. cfg.LogFile = logFile
  142. cfg.LogMaxDays = int64(logMaxDays)
  143. if logFile == "console" {
  144. cfg.LogWay = "console"
  145. } else {
  146. cfg.LogWay = "file"
  147. }
  148. cfg.DisableLogColor = disableLogColor
  149. return
  150. }
  151. func runClient(cfgFilePath string) (err error) {
  152. var content string
  153. content, err = config.GetRenderedConfFromFile(cfgFilePath)
  154. if err != nil {
  155. return
  156. }
  157. cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
  158. if err != nil {
  159. return
  160. }
  161. pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(cfg.User, content, cfg.Start)
  162. if err != nil {
  163. return err
  164. }
  165. err = startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
  166. return
  167. }
  168. func startService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (err error) {
  169. log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
  170. cfg.LogMaxDays, cfg.DisableLogColor)
  171. if cfg.DnsServer != "" {
  172. s := cfg.DnsServer
  173. if !strings.Contains(s, ":") {
  174. s += ":53"
  175. }
  176. // Change default dns server for frpc
  177. net.DefaultResolver = &net.Resolver{
  178. PreferGo: true,
  179. Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
  180. return net.Dial("udp", s)
  181. },
  182. }
  183. }
  184. svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
  185. if errRet != nil {
  186. err = errRet
  187. return
  188. }
  189. // Capture the exit signal if we use kcp.
  190. if cfg.Protocol == "kcp" {
  191. go handleSignal(svr)
  192. }
  193. err = svr.Run()
  194. if cfg.Protocol == "kcp" {
  195. <-kcpDoneCh
  196. }
  197. return
  198. }