"Fossies" - the Fresh Open Source Software Archive

Member "frp-0.36.2/pkg/config/client.go" (22 Mar 2021, 13480 Bytes) of package /linux/misc/frp-0.36.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "client.go": 0.36.1_vs_0.36.2.

    1 // Copyright 2020 The frp Authors
    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 
   15 package config
   16 
   17 import (
   18     "fmt"
   19     "os"
   20     "strings"
   21 
   22     "github.com/fatedier/frp/pkg/auth"
   23     "github.com/fatedier/frp/pkg/util/util"
   24 
   25     "gopkg.in/ini.v1"
   26 )
   27 
   28 // ClientCommonConf contains information for a client service. It is
   29 // recommended to use GetDefaultClientConf instead of creating this object
   30 // directly, so that all unspecified fields have reasonable default values.
   31 type ClientCommonConf struct {
   32     auth.ClientConfig `ini:",extends"`
   33 
   34     // ServerAddr specifies the address of the server to connect to. By
   35     // default, this value is "0.0.0.0".
   36     ServerAddr string `ini:"server_addr" josn:"server_addr"`
   37     // ServerPort specifies the port to connect to the server on. By default,
   38     // this value is 7000.
   39     ServerPort int `ini:"server_port" json:"server_port"`
   40     // HTTPProxy specifies a proxy address to connect to the server through. If
   41     // this value is "", the server will be connected to directly. By default,
   42     // this value is read from the "http_proxy" environment variable.
   43     HTTPProxy string `ini:"http_proxy" json:"http_proxy"`
   44     // LogFile specifies a file where logs will be written to. This value will
   45     // only be used if LogWay is set appropriately. By default, this value is
   46     // "console".
   47     LogFile string `ini:"log_file" json:"log_file"`
   48     // LogWay specifies the way logging is managed. Valid values are "console"
   49     // or "file". If "console" is used, logs will be printed to stdout. If
   50     // "file" is used, logs will be printed to LogFile. By default, this value
   51     // is "console".
   52     LogWay string `ini:"log_way" json:"log_way"`
   53     // LogLevel specifies the minimum log level. Valid values are "trace",
   54     // "debug", "info", "warn", and "error". By default, this value is "info".
   55     LogLevel string `ini:"log_level" json:"log_level"`
   56     // LogMaxDays specifies the maximum number of days to store log information
   57     // before deletion. This is only used if LogWay == "file". By default, this
   58     // value is 0.
   59     LogMaxDays int64 `ini:"log_max_days" json:"log_max_days"`
   60     // DisableLogColor disables log colors when LogWay == "console" when set to
   61     // true. By default, this value is false.
   62     DisableLogColor bool `ini:"disable_log_color" json:"disable_log_color"`
   63     // AdminAddr specifies the address that the admin server binds to. By
   64     // default, this value is "127.0.0.1".
   65     AdminAddr string `ini:"admin_addr" json:"admin_addr"`
   66     // AdminPort specifies the port for the admin server to listen on. If this
   67     // value is 0, the admin server will not be started. By default, this value
   68     // is 0.
   69     AdminPort int `ini:"admin_port" json:"admin_port"`
   70     // AdminUser specifies the username that the admin server will use for
   71     // login. By default, this value is "admin".
   72     AdminUser string `ini:"admin_user" json:"admin_user"`
   73     // AdminPwd specifies the password that the admin server will use for
   74     // login. By default, this value is "admin".
   75     AdminPwd string `ini:"admin_pwd" json:"admin_pwd"`
   76     // AssetsDir specifies the local directory that the admin server will load
   77     // resources from. If this value is "", assets will be loaded from the
   78     // bundled executable using statik. By default, this value is "".
   79     AssetsDir string `ini:"assets_dir" json:"assets_dir"`
   80     // PoolCount specifies the number of connections the client will make to
   81     // the server in advance. By default, this value is 0.
   82     PoolCount int `ini:"pool_count" json:"pool_count"`
   83     // TCPMux toggles TCP stream multiplexing. This allows multiple requests
   84     // from a client to share a single TCP connection. If this value is true,
   85     // the server must have TCP multiplexing enabled as well. By default, this
   86     // value is true.
   87     TCPMux bool `ini:"tcp_mux" json:"tcp_mux"`
   88     // User specifies a prefix for proxy names to distinguish them from other
   89     // clients. If this value is not "", proxy names will automatically be
   90     // changed to "{user}.{proxy_name}". By default, this value is "".
   91     User string `ini:"user" json:"user"`
   92     // DNSServer specifies a DNS server address for FRPC to use. If this value
   93     // is "", the default DNS will be used. By default, this value is "".
   94     DNSServer string `ini:"dns_server" json:"dns_server"`
   95     // LoginFailExit controls whether or not the client should exit after a
   96     // failed login attempt. If false, the client will retry until a login
   97     // attempt succeeds. By default, this value is true.
   98     LoginFailExit bool `ini:"login_fail_exit" json:"login_fail_exit"`
   99     // Start specifies a set of enabled proxies by name. If this set is empty,
  100     // all supplied proxies are enabled. By default, this value is an empty
  101     // set.
  102     Start []string `ini:"start" json:"start"`
  103     //Start map[string]struct{} `json:"start"`
  104     // Protocol specifies the protocol to use when interacting with the server.
  105     // Valid values are "tcp", "kcp" and "websocket". By default, this value
  106     // is "tcp".
  107     Protocol string `ini:"protocol" json:"protocol"`
  108     // TLSEnable specifies whether or not TLS should be used when communicating
  109     // with the server. If "tls_cert_file" and "tls_key_file" are valid,
  110     // client will load the supplied tls configuration.
  111     TLSEnable bool `ini:"tls_enable" json:"tls_enable"`
  112     // TLSCertPath specifies the path of the cert file that client will
  113     // load. It only works when "tls_enable" is true and "tls_key_file" is valid.
  114     TLSCertFile string `ini:"tls_cert_file" json:"tls_cert_file"`
  115     // TLSKeyPath specifies the path of the secret key file that client
  116     // will load. It only works when "tls_enable" is true and "tls_cert_file"
  117     // are valid.
  118     TLSKeyFile string `ini:"tls_key_file" json:"tls_key_file"`
  119     // TLSTrustedCaFile specifies the path of the trusted ca file that will load.
  120     // It only works when "tls_enable" is valid and tls configuration of server
  121     // has been specified.
  122     TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
  123     // TLSServerName specifices the custom server name of tls certificate. By
  124     // default, server name if same to ServerAddr.
  125     TLSServerName string `ini:"tls_server_name" json:"tls_server_name"`
  126     // HeartBeatInterval specifies at what interval heartbeats are sent to the
  127     // server, in seconds. It is not recommended to change this value. By
  128     // default, this value is 30.
  129     HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"`
  130     // HeartBeatTimeout specifies the maximum allowed heartbeat response delay
  131     // before the connection is terminated, in seconds. It is not recommended
  132     // to change this value. By default, this value is 90.
  133     HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"`
  134     // Client meta info
  135     Metas map[string]string `ini:"-" json:"metas"`
  136     // UDPPacketSize specifies the udp packet size
  137     // By default, this value is 1500
  138     UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
  139 }
  140 
  141 // GetDefaultClientConf returns a client configuration with default values.
  142 func GetDefaultClientConf() ClientCommonConf {
  143     return ClientCommonConf{
  144         ClientConfig:      auth.GetDefaultClientConf(),
  145         ServerAddr:        "0.0.0.0",
  146         ServerPort:        7000,
  147         HTTPProxy:         os.Getenv("http_proxy"),
  148         LogFile:           "console",
  149         LogWay:            "console",
  150         LogLevel:          "info",
  151         LogMaxDays:        3,
  152         DisableLogColor:   false,
  153         AdminAddr:         "127.0.0.1",
  154         AdminPort:         0,
  155         AdminUser:         "",
  156         AdminPwd:          "",
  157         AssetsDir:         "",
  158         PoolCount:         1,
  159         TCPMux:            true,
  160         User:              "",
  161         DNSServer:         "",
  162         LoginFailExit:     true,
  163         Start:             make([]string, 0),
  164         Protocol:          "tcp",
  165         TLSEnable:         false,
  166         TLSCertFile:       "",
  167         TLSKeyFile:        "",
  168         TLSTrustedCaFile:  "",
  169         HeartbeatInterval: 30,
  170         HeartbeatTimeout:  90,
  171         Metas:             make(map[string]string),
  172         UDPPacketSize:     1500,
  173     }
  174 }
  175 
  176 func (cfg *ClientCommonConf) Complete() {
  177     if cfg.LogFile == "console" {
  178         cfg.LogWay = "console"
  179     } else {
  180         cfg.LogWay = "file"
  181     }
  182 }
  183 
  184 func (cfg *ClientCommonConf) Validate() error {
  185     if cfg.HeartbeatInterval <= 0 {
  186         return fmt.Errorf("invalid heartbeat_interval")
  187     }
  188 
  189     if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
  190         return fmt.Errorf("invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
  191     }
  192 
  193     if cfg.TLSEnable == false {
  194         if cfg.TLSCertFile != "" {
  195             fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
  196         }
  197 
  198         if cfg.TLSKeyFile != "" {
  199             fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
  200         }
  201 
  202         if cfg.TLSTrustedCaFile != "" {
  203             fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
  204         }
  205     }
  206 
  207     if cfg.Protocol != "tcp" && cfg.Protocol != "kcp" && cfg.Protocol != "websocket" {
  208         return fmt.Errorf("invalid protocol")
  209     }
  210 
  211     return nil
  212 }
  213 
  214 // Supported sources including: string(file path), []byte, Reader interface.
  215 func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
  216     f, err := ini.LoadSources(ini.LoadOptions{
  217         Insensitive:         false,
  218         InsensitiveSections: false,
  219         InsensitiveKeys:     false,
  220         IgnoreInlineComment: true,
  221         AllowBooleanKeys:    true,
  222     }, source)
  223     if err != nil {
  224         return ClientCommonConf{}, err
  225     }
  226 
  227     s, err := f.GetSection("common")
  228     if err != nil {
  229         return ClientCommonConf{}, fmt.Errorf("invalid configuration file, not found [common] section")
  230     }
  231 
  232     common := GetDefaultClientConf()
  233     err = s.MapTo(&common)
  234     if err != nil {
  235         return ClientCommonConf{}, err
  236     }
  237 
  238     common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
  239 
  240     return common, nil
  241 }
  242 
  243 // if len(startProxy) is 0, start all
  244 // otherwise just start proxies in startProxy map
  245 func LoadAllProxyConfsFromIni(
  246     prefix string,
  247     source interface{},
  248     start []string,
  249 ) (map[string]ProxyConf, map[string]VisitorConf, error) {
  250 
  251     f, err := ini.LoadSources(ini.LoadOptions{
  252         Insensitive:         false,
  253         InsensitiveSections: false,
  254         InsensitiveKeys:     false,
  255         IgnoreInlineComment: true,
  256         AllowBooleanKeys:    true,
  257     }, source)
  258     if err != nil {
  259         return nil, nil, err
  260     }
  261 
  262     proxyConfs := make(map[string]ProxyConf)
  263     visitorConfs := make(map[string]VisitorConf)
  264 
  265     if prefix != "" {
  266         prefix += "."
  267     }
  268 
  269     startProxy := make(map[string]struct{})
  270     for _, s := range start {
  271         startProxy[s] = struct{}{}
  272     }
  273 
  274     startAll := true
  275     if len(startProxy) > 0 {
  276         startAll = false
  277     }
  278 
  279     // Build template sections from range section And append to ini.File.
  280     rangeSections := make([]*ini.Section, 0)
  281     for _, section := range f.Sections() {
  282 
  283         if !strings.HasPrefix(section.Name(), "range:") {
  284             continue
  285         }
  286 
  287         rangeSections = append(rangeSections, section)
  288     }
  289 
  290     for _, section := range rangeSections {
  291         err = renderRangeProxyTemplates(f, section)
  292         if err != nil {
  293             return nil, nil, fmt.Errorf("fail to render range-section[%s] with error: %v", section.Name(), err)
  294         }
  295     }
  296 
  297     for _, section := range f.Sections() {
  298         name := section.Name()
  299 
  300         if name == ini.DefaultSection || name == "common" || strings.HasPrefix(name, "range:") {
  301             continue
  302         }
  303 
  304         _, shouldStart := startProxy[name]
  305         if !startAll && !shouldStart {
  306             continue
  307         }
  308 
  309         roleType := section.Key("role").String()
  310         if roleType == "" {
  311             roleType = "server"
  312         }
  313 
  314         switch roleType {
  315         case "server":
  316             newConf, newErr := NewProxyConfFromIni(prefix, name, section)
  317             if newErr != nil {
  318                 return nil, nil, fmt.Errorf("fail to parse section[%s], err: %v", name, newErr)
  319             }
  320             proxyConfs[prefix+name] = newConf
  321         case "visitor":
  322             newConf, newErr := NewVisitorConfFromIni(prefix, name, section)
  323             if newErr != nil {
  324                 return nil, nil, newErr
  325             }
  326             visitorConfs[prefix+name] = newConf
  327         default:
  328             return nil, nil, fmt.Errorf("section[%s] role should be 'server' or 'visitor'", name)
  329         }
  330     }
  331     return proxyConfs, visitorConfs, nil
  332 }
  333 
  334 func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error {
  335 
  336     // Validation
  337     localPortStr := section.Key("local_port").String()
  338     remotePortStr := section.Key("remote_port").String()
  339     if localPortStr == "" || remotePortStr == "" {
  340         return fmt.Errorf("local_port or remote_port is empty")
  341     }
  342 
  343     localPorts, err := util.ParseRangeNumbers(localPortStr)
  344     if err != nil {
  345         return err
  346     }
  347 
  348     remotePorts, err := util.ParseRangeNumbers(remotePortStr)
  349     if err != nil {
  350         return err
  351     }
  352 
  353     if len(localPorts) != len(remotePorts) {
  354         return fmt.Errorf("local ports number should be same with remote ports number")
  355     }
  356 
  357     if len(localPorts) == 0 {
  358         return fmt.Errorf("local_port and remote_port is necessary")
  359     }
  360 
  361     // Templates
  362     prefix := strings.TrimSpace(strings.TrimPrefix(section.Name(), "range:"))
  363 
  364     for i := range localPorts {
  365         tmpname := fmt.Sprintf("%s_%d", prefix, i)
  366 
  367         tmpsection, err := f.NewSection(tmpname)
  368         if err != nil {
  369             return err
  370         }
  371 
  372         copySection(section, tmpsection)
  373         tmpsection.NewKey("local_port", fmt.Sprintf("%d", localPorts[i]))
  374         tmpsection.NewKey("remote_port", fmt.Sprintf("%d", remotePorts[i]))
  375     }
  376 
  377     return nil
  378 }
  379 
  380 func copySection(source, target *ini.Section) {
  381     for key, value := range source.KeysHash() {
  382         target.NewKey(key, value)
  383     }
  384 }