root.go 4.8 KB

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