|
@@ -1,4 +1,4 @@
|
|
|
-// Copyright 2016 fatedier, fatedier@gmail.com
|
|
|
+// Copyright 2020 The frp Authors
|
|
|
//
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
// you may not use this file except in compliance with the License.
|
|
@@ -17,125 +17,128 @@ package config
|
|
|
import (
|
|
|
"fmt"
|
|
|
"os"
|
|
|
- "strconv"
|
|
|
"strings"
|
|
|
|
|
|
"github.com/fatedier/frp/pkg/auth"
|
|
|
+ "github.com/fatedier/frp/pkg/util/util"
|
|
|
|
|
|
- ini "github.com/vaughan0/go-ini"
|
|
|
+ "gopkg.in/ini.v1"
|
|
|
)
|
|
|
|
|
|
// ClientCommonConf contains information for a client service. It is
|
|
|
// recommended to use GetDefaultClientConf instead of creating this object
|
|
|
// directly, so that all unspecified fields have reasonable default values.
|
|
|
type ClientCommonConf struct {
|
|
|
- auth.ClientConfig
|
|
|
+ auth.ClientConfig `ini:",extends" json:"inline"`
|
|
|
+
|
|
|
// ServerAddr specifies the address of the server to connect to. By
|
|
|
// default, this value is "0.0.0.0".
|
|
|
- ServerAddr string `json:"server_addr"`
|
|
|
+ ServerAddr string `ini:"server_addr" josn:"server_addr"`
|
|
|
// ServerPort specifies the port to connect to the server on. By default,
|
|
|
// this value is 7000.
|
|
|
- ServerPort int `json:"server_port"`
|
|
|
+ ServerPort int `ini:"server_port" json:"server_port"`
|
|
|
// HTTPProxy specifies a proxy address to connect to the server through. If
|
|
|
// this value is "", the server will be connected to directly. By default,
|
|
|
// this value is read from the "http_proxy" environment variable.
|
|
|
- HTTPProxy string `json:"http_proxy"`
|
|
|
+ HTTPProxy string `ini:"http_proxy" json:"http_proxy"`
|
|
|
// LogFile specifies a file where logs will be written to. This value will
|
|
|
// only be used if LogWay is set appropriately. By default, this value is
|
|
|
// "console".
|
|
|
- LogFile string `json:"log_file"`
|
|
|
+ LogFile string `ini:"log_file" json:"log_file"`
|
|
|
// LogWay specifies the way logging is managed. Valid values are "console"
|
|
|
// or "file". If "console" is used, logs will be printed to stdout. If
|
|
|
// "file" is used, logs will be printed to LogFile. By default, this value
|
|
|
// is "console".
|
|
|
- LogWay string `json:"log_way"`
|
|
|
+ LogWay string `ini:"log_way" json:"log_way"`
|
|
|
// LogLevel specifies the minimum log level. Valid values are "trace",
|
|
|
// "debug", "info", "warn", and "error". By default, this value is "info".
|
|
|
- LogLevel string `json:"log_level"`
|
|
|
+ LogLevel string `ini:"log_level" json:"log_level"`
|
|
|
// LogMaxDays specifies the maximum number of days to store log information
|
|
|
// before deletion. This is only used if LogWay == "file". By default, this
|
|
|
// value is 0.
|
|
|
- LogMaxDays int64 `json:"log_max_days"`
|
|
|
+ LogMaxDays int64 `ini:"log_max_days" json:"log_max_days"`
|
|
|
// DisableLogColor disables log colors when LogWay == "console" when set to
|
|
|
// true. By default, this value is false.
|
|
|
- DisableLogColor bool `json:"disable_log_color"`
|
|
|
+ DisableLogColor bool `ini:"disable_log_color" json:"disable_log_color"`
|
|
|
// AdminAddr specifies the address that the admin server binds to. By
|
|
|
// default, this value is "127.0.0.1".
|
|
|
- AdminAddr string `json:"admin_addr"`
|
|
|
+ AdminAddr string `ini:"admin_addr" json:"admin_addr"`
|
|
|
// AdminPort specifies the port for the admin server to listen on. If this
|
|
|
// value is 0, the admin server will not be started. By default, this value
|
|
|
// is 0.
|
|
|
- AdminPort int `json:"admin_port"`
|
|
|
+ AdminPort int `ini:"admin_port" json:"admin_port"`
|
|
|
// AdminUser specifies the username that the admin server will use for
|
|
|
// login. By default, this value is "admin".
|
|
|
- AdminUser string `json:"admin_user"`
|
|
|
+ AdminUser string `ini:"admin_user" json:"admin_user"`
|
|
|
// AdminPwd specifies the password that the admin server will use for
|
|
|
// login. By default, this value is "admin".
|
|
|
- AdminPwd string `json:"admin_pwd"`
|
|
|
+ AdminPwd string `ini:"admin_pwd" json:"admin_pwd"`
|
|
|
// AssetsDir specifies the local directory that the admin server will load
|
|
|
// resources from. If this value is "", assets will be loaded from the
|
|
|
// bundled executable using statik. By default, this value is "".
|
|
|
- AssetsDir string `json:"assets_dir"`
|
|
|
+ AssetsDir string `ini:"assets_dir" json:"assets_dir"`
|
|
|
// PoolCount specifies the number of connections the client will make to
|
|
|
// the server in advance. By default, this value is 0.
|
|
|
- PoolCount int `json:"pool_count"`
|
|
|
+ PoolCount int `ini:"pool_count" json:"pool_count"`
|
|
|
// TCPMux toggles TCP stream multiplexing. This allows multiple requests
|
|
|
// from a client to share a single TCP connection. If this value is true,
|
|
|
// the server must have TCP multiplexing enabled as well. By default, this
|
|
|
// value is true.
|
|
|
- TCPMux bool `json:"tcp_mux"`
|
|
|
+ TCPMux bool `ini:"tcp_mux" json:"tcp_mux"`
|
|
|
// User specifies a prefix for proxy names to distinguish them from other
|
|
|
// clients. If this value is not "", proxy names will automatically be
|
|
|
// changed to "{user}.{proxy_name}". By default, this value is "".
|
|
|
- User string `json:"user"`
|
|
|
+ User string `ini:"user" json:"user"`
|
|
|
// DNSServer specifies a DNS server address for FRPC to use. If this value
|
|
|
// is "", the default DNS will be used. By default, this value is "".
|
|
|
- DNSServer string `json:"dns_server"`
|
|
|
+ DNSServer string `ini:"dns_server" json:"dns_server"`
|
|
|
// LoginFailExit controls whether or not the client should exit after a
|
|
|
// failed login attempt. If false, the client will retry until a login
|
|
|
// attempt succeeds. By default, this value is true.
|
|
|
- LoginFailExit bool `json:"login_fail_exit"`
|
|
|
+ LoginFailExit bool `ini:"login_fail_exit" json:"login_fail_exit"`
|
|
|
// Start specifies a set of enabled proxies by name. If this set is empty,
|
|
|
// all supplied proxies are enabled. By default, this value is an empty
|
|
|
// set.
|
|
|
- Start map[string]struct{} `json:"start"`
|
|
|
+ Start []string `ini:"start" json:"start"`
|
|
|
+ //Start map[string]struct{} `json:"start"`
|
|
|
// Protocol specifies the protocol to use when interacting with the server.
|
|
|
// Valid values are "tcp", "kcp" and "websocket". By default, this value
|
|
|
// is "tcp".
|
|
|
- Protocol string `json:"protocol"`
|
|
|
+ Protocol string `ini:"protocol" json:"protocol"`
|
|
|
// TLSEnable specifies whether or not TLS should be used when communicating
|
|
|
// with the server. If "tls_cert_file" and "tls_key_file" are valid,
|
|
|
// client will load the supplied tls configuration.
|
|
|
- TLSEnable bool `json:"tls_enable"`
|
|
|
+ TLSEnable bool `ini:"tls_enable" json:"tls_enable"`
|
|
|
// ClientTLSCertPath specifies the path of the cert file that client will
|
|
|
// load. It only works when "tls_enable" is true and "tls_key_file" is valid.
|
|
|
- TLSCertFile string `json:"tls_cert_file"`
|
|
|
+ TLSCertFile string `ini:"tls_cert_file" json:"tls_cert_file"`
|
|
|
// ClientTLSKeyPath specifies the path of the secret key file that client
|
|
|
// will load. It only works when "tls_enable" is true and "tls_cert_file"
|
|
|
// are valid.
|
|
|
- TLSKeyFile string `json:"tls_key_file"`
|
|
|
+ TLSKeyFile string `ini:"tls_key_file" json:"tls_key_file"`
|
|
|
// TrustedCaFile specifies the path of the trusted ca file that will load.
|
|
|
// It only works when "tls_enable" is valid and tls configuration of server
|
|
|
// has been specified.
|
|
|
- TLSTrustedCaFile string `json:"tls_trusted_ca_file"`
|
|
|
+ TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
|
|
|
// HeartBeatInterval specifies at what interval heartbeats are sent to the
|
|
|
// server, in seconds. It is not recommended to change this value. By
|
|
|
// default, this value is 30.
|
|
|
- HeartbeatInterval int64 `json:"heartbeat_interval"`
|
|
|
+ HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"`
|
|
|
// HeartBeatTimeout specifies the maximum allowed heartbeat response delay
|
|
|
// before the connection is terminated, in seconds. It is not recommended
|
|
|
// to change this value. By default, this value is 90.
|
|
|
- HeartbeatTimeout int64 `json:"heartbeat_timeout"`
|
|
|
+ HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"`
|
|
|
// Client meta info
|
|
|
- Metas map[string]string `json:"metas"`
|
|
|
+ Metas map[string]string `ini:"-" json:"metas"`
|
|
|
// UDPPacketSize specifies the udp packet size
|
|
|
// By default, this value is 1500
|
|
|
- UDPPacketSize int64 `json:"udp_packet_size"`
|
|
|
+ UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
|
|
|
}
|
|
|
|
|
|
// GetDefaultClientConf returns a client configuration with default values.
|
|
|
func GetDefaultClientConf() ClientCommonConf {
|
|
|
return ClientCommonConf{
|
|
|
+ ClientConfig: auth.GetDefaultClientConf(),
|
|
|
ServerAddr: "0.0.0.0",
|
|
|
ServerPort: 7000,
|
|
|
HTTPProxy: os.Getenv("http_proxy"),
|
|
@@ -154,7 +157,7 @@ func GetDefaultClientConf() ClientCommonConf {
|
|
|
User: "",
|
|
|
DNSServer: "",
|
|
|
LoginFailExit: true,
|
|
|
- Start: make(map[string]struct{}),
|
|
|
+ Start: make([]string, 0),
|
|
|
Protocol: "tcp",
|
|
|
TLSEnable: false,
|
|
|
TLSCertFile: "",
|
|
@@ -167,199 +170,200 @@ func GetDefaultClientConf() ClientCommonConf {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error) {
|
|
|
- cfg = GetDefaultClientConf()
|
|
|
+func (cfg *ClientCommonConf) Check() error {
|
|
|
+ if cfg.HeartbeatInterval <= 0 {
|
|
|
+ return fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
|
|
+ }
|
|
|
|
|
|
- conf, err := ini.Load(strings.NewReader(content))
|
|
|
- if err != nil {
|
|
|
- return ClientCommonConf{}, fmt.Errorf("parse ini conf file error: %v", err)
|
|
|
+ if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
|
|
|
+ return fmt.Errorf("Parse conf error: invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
|
|
|
}
|
|
|
|
|
|
- cfg.ClientConfig = auth.UnmarshalClientConfFromIni(conf)
|
|
|
+ if cfg.TLSEnable == false {
|
|
|
+ if cfg.TLSCertFile != "" {
|
|
|
+ fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
|
|
|
+ }
|
|
|
|
|
|
- var (
|
|
|
- tmpStr string
|
|
|
- ok bool
|
|
|
- v int64
|
|
|
- )
|
|
|
- if tmpStr, ok = conf.Get("common", "server_addr"); ok {
|
|
|
- cfg.ServerAddr = tmpStr
|
|
|
- }
|
|
|
+ if cfg.TLSKeyFile != "" {
|
|
|
+ fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
|
|
|
+ }
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "server_port"); ok {
|
|
|
- v, err = strconv.ParseInt(tmpStr, 10, 64)
|
|
|
- if err != nil {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid server_port")
|
|
|
- return
|
|
|
+ if cfg.TLSTrustedCaFile != "" {
|
|
|
+ fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
|
|
|
}
|
|
|
- cfg.ServerPort = int(v)
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "disable_log_color"); ok && tmpStr == "true" {
|
|
|
- cfg.DisableLogColor = true
|
|
|
- }
|
|
|
+ return nil
|
|
|
+}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
|
|
|
- cfg.HTTPProxy = tmpStr
|
|
|
+// Supported sources including: string(file path), []byte, Reader interface.
|
|
|
+func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
|
|
|
+ f, err := ini.LoadSources(ini.LoadOptions{
|
|
|
+ Insensitive: false,
|
|
|
+ InsensitiveSections: false,
|
|
|
+ InsensitiveKeys: false,
|
|
|
+ IgnoreInlineComment: true,
|
|
|
+ AllowBooleanKeys: true,
|
|
|
+ }, source)
|
|
|
+ if err != nil {
|
|
|
+ return ClientCommonConf{}, err
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
|
|
- cfg.LogFile = tmpStr
|
|
|
- if cfg.LogFile == "console" {
|
|
|
- cfg.LogWay = "console"
|
|
|
- } else {
|
|
|
- cfg.LogWay = "file"
|
|
|
- }
|
|
|
+ s, err := f.GetSection("common")
|
|
|
+ if err != nil {
|
|
|
+ return ClientCommonConf{}, fmt.Errorf("invalid configuration file, not found [common] section")
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
|
|
- cfg.LogLevel = tmpStr
|
|
|
+ common := GetDefaultClientConf()
|
|
|
+ err = s.MapTo(&common)
|
|
|
+ if err != nil {
|
|
|
+ return ClientCommonConf{}, err
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
|
|
- if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
|
|
- cfg.LogMaxDays = v
|
|
|
- }
|
|
|
- }
|
|
|
+ common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
|
|
|
- cfg.AdminAddr = tmpStr
|
|
|
- }
|
|
|
+ return common, nil
|
|
|
+}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "admin_port"); ok {
|
|
|
- if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
|
|
- cfg.AdminPort = int(v)
|
|
|
- } else {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid admin_port")
|
|
|
- return
|
|
|
- }
|
|
|
+// if len(startProxy) is 0, start all
|
|
|
+// otherwise just start proxies in startProxy map
|
|
|
+func LoadAllProxyConfsFromIni(
|
|
|
+ prefix string,
|
|
|
+ source interface{},
|
|
|
+ start []string,
|
|
|
+) (map[string]ProxyConf, map[string]VisitorConf, error) {
|
|
|
+
|
|
|
+ f, err := ini.LoadSources(ini.LoadOptions{
|
|
|
+ Insensitive: false,
|
|
|
+ InsensitiveSections: false,
|
|
|
+ InsensitiveKeys: false,
|
|
|
+ IgnoreInlineComment: true,
|
|
|
+ AllowBooleanKeys: true,
|
|
|
+ }, source)
|
|
|
+ if err != nil {
|
|
|
+ return nil, nil, err
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "admin_user"); ok {
|
|
|
- cfg.AdminUser = tmpStr
|
|
|
+ proxyConfs := make(map[string]ProxyConf)
|
|
|
+ visitorConfs := make(map[string]VisitorConf)
|
|
|
+
|
|
|
+ if prefix != "" {
|
|
|
+ prefix += "."
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
|
|
|
- cfg.AdminPwd = tmpStr
|
|
|
+ startProxy := make(map[string]struct{})
|
|
|
+ for _, s := range start {
|
|
|
+ startProxy[s] = struct{}{}
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
|
|
|
- cfg.AssetsDir = tmpStr
|
|
|
+ startAll := true
|
|
|
+ if len(startProxy) > 0 {
|
|
|
+ startAll = false
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "pool_count"); ok {
|
|
|
- if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
|
|
- cfg.PoolCount = int(v)
|
|
|
+ // Build template sections from range section And append to ini.File.
|
|
|
+ rangeSections := make([]*ini.Section, 0)
|
|
|
+ for _, section := range f.Sections() {
|
|
|
+
|
|
|
+ if !strings.HasPrefix(section.Name(), "range:") {
|
|
|
+ continue
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
|
|
- cfg.TCPMux = false
|
|
|
- } else {
|
|
|
- cfg.TCPMux = true
|
|
|
+ rangeSections = append(rangeSections, section)
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "user"); ok {
|
|
|
- cfg.User = tmpStr
|
|
|
+ for _, section := range rangeSections {
|
|
|
+ err = renderRangeProxyTemplates(f, section)
|
|
|
+ if err != nil {
|
|
|
+ return nil, nil, fmt.Errorf("fail to render range-section[%s] with error: %v", section.Name(), err)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "dns_server"); ok {
|
|
|
- cfg.DNSServer = tmpStr
|
|
|
- }
|
|
|
+ for _, section := range f.Sections() {
|
|
|
+ name := section.Name()
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "start"); ok {
|
|
|
- proxyNames := strings.Split(tmpStr, ",")
|
|
|
- for _, name := range proxyNames {
|
|
|
- cfg.Start[strings.TrimSpace(name)] = struct{}{}
|
|
|
+ if name == ini.DefaultSection || name == "common" || strings.HasPrefix(name, "range:") {
|
|
|
+ continue
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" {
|
|
|
- cfg.LoginFailExit = false
|
|
|
- } else {
|
|
|
- cfg.LoginFailExit = true
|
|
|
- }
|
|
|
+ _, shouldStart := startProxy[name]
|
|
|
+ if !startAll && !shouldStart {
|
|
|
+ continue
|
|
|
+ }
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "protocol"); ok {
|
|
|
- // Now it only support tcp and kcp and websocket.
|
|
|
- if tmpStr != "tcp" && tmpStr != "kcp" && tmpStr != "websocket" {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid protocol")
|
|
|
- return
|
|
|
+ roleType := section.Key("role").String()
|
|
|
+ if roleType == "" {
|
|
|
+ roleType = "server"
|
|
|
}
|
|
|
- cfg.Protocol = tmpStr
|
|
|
- }
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "tls_enable"); ok && tmpStr == "true" {
|
|
|
- cfg.TLSEnable = true
|
|
|
- } else {
|
|
|
- cfg.TLSEnable = false
|
|
|
+ switch roleType {
|
|
|
+ case "server":
|
|
|
+ newConf, newErr := NewProxyConfFromIni(prefix, name, section)
|
|
|
+ if newErr != nil {
|
|
|
+ return nil, nil, fmt.Errorf("fail to parse section[%s], err: %v", name, newErr)
|
|
|
+ }
|
|
|
+ proxyConfs[prefix+name] = newConf
|
|
|
+ case "visitor":
|
|
|
+ newConf, newErr := NewVisitorConfFromIni(prefix, name, section)
|
|
|
+ if newErr != nil {
|
|
|
+ return nil, nil, newErr
|
|
|
+ }
|
|
|
+ visitorConfs[prefix+name] = newConf
|
|
|
+ default:
|
|
|
+ return nil, nil, fmt.Errorf("section[%s] role should be 'server' or 'visitor'", name)
|
|
|
+ }
|
|
|
}
|
|
|
+ return proxyConfs, visitorConfs, nil
|
|
|
+}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "tls_cert_file"); ok {
|
|
|
- cfg.TLSCertFile = tmpStr
|
|
|
- }
|
|
|
+func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error {
|
|
|
|
|
|
- if tmpStr, ok := conf.Get("common", "tls_key_file"); ok {
|
|
|
- cfg.TLSKeyFile = tmpStr
|
|
|
+ // Validation
|
|
|
+ localPortStr := section.Key("local_port").String()
|
|
|
+ remotePortStr := section.Key("remote_port").String()
|
|
|
+ if localPortStr == "" || remotePortStr == "" {
|
|
|
+ return fmt.Errorf("local_port or remote_port is empty")
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok := conf.Get("common", "tls_trusted_ca_file"); ok {
|
|
|
- cfg.TLSTrustedCaFile = tmpStr
|
|
|
+ localPorts, err := util.ParseRangeNumbers(localPortStr)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
|
|
- if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
|
|
- return
|
|
|
- }
|
|
|
- cfg.HeartbeatTimeout = v
|
|
|
+ remotePorts, err := util.ParseRangeNumbers(remotePortStr)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
|
|
|
- if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
|
|
- return
|
|
|
- }
|
|
|
- cfg.HeartbeatInterval = v
|
|
|
- }
|
|
|
- for k, v := range conf.Section("common") {
|
|
|
- if strings.HasPrefix(k, "meta_") {
|
|
|
- cfg.Metas[strings.TrimPrefix(k, "meta_")] = v
|
|
|
- }
|
|
|
+ if len(localPorts) != len(remotePorts) {
|
|
|
+ return fmt.Errorf("local ports number should be same with remote ports number")
|
|
|
}
|
|
|
- if tmpStr, ok = conf.Get("common", "udp_packet_size"); ok {
|
|
|
- if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid udp_packet_size")
|
|
|
- return
|
|
|
- }
|
|
|
- cfg.UDPPacketSize = v
|
|
|
- }
|
|
|
- return
|
|
|
-}
|
|
|
|
|
|
-func (cfg *ClientCommonConf) Check() (err error) {
|
|
|
- if cfg.HeartbeatInterval <= 0 {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
|
|
- return
|
|
|
+ if len(localPorts) == 0 {
|
|
|
+ return fmt.Errorf("local_port and remote_port is necessary")
|
|
|
}
|
|
|
|
|
|
- if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
|
|
|
- err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
|
|
|
- return
|
|
|
- }
|
|
|
+ // Templates
|
|
|
+ prefix := strings.TrimSpace(strings.TrimPrefix(section.Name(), "range:"))
|
|
|
|
|
|
- if cfg.TLSEnable == false {
|
|
|
- if cfg.TLSCertFile != "" {
|
|
|
- fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
|
|
|
- }
|
|
|
+ for i := range localPorts {
|
|
|
+ tmpname := fmt.Sprintf("%s_%d", prefix, i)
|
|
|
|
|
|
- if cfg.TLSKeyFile != "" {
|
|
|
- fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
|
|
|
+ tmpsection, err := f.NewSection(tmpname)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- if cfg.TLSTrustedCaFile != "" {
|
|
|
- fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
|
|
|
- }
|
|
|
+ copySection(section, tmpsection)
|
|
|
+ tmpsection.NewKey("local_port", fmt.Sprintf("%d", localPorts[i]))
|
|
|
+ tmpsection.NewKey("remote_port", fmt.Sprintf("%d", remotePorts[i]))
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func copySection(source, target *ini.Section) {
|
|
|
+ for key, value := range source.KeysHash() {
|
|
|
+ target.NewKey(key, value)
|
|
|
}
|
|
|
- return
|
|
|
}
|