261 lines
6.5 KiB
Go
261 lines
6.5 KiB
Go
// Copyright 2016 fatedier, fatedier@gmail.com
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/fatedier/frp/pkg/auth"
|
|
"github.com/fatedier/frp/pkg/util/util"
|
|
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
// GetDefaultClientConf returns a client configuration with default values.
|
|
func DefaultClientConf() ClientCommonConf {
|
|
return ClientCommonConf{
|
|
ClientConfig: auth.GetDefaultClientConf(),
|
|
ServerAddr: "0.0.0.0",
|
|
ServerPort: 7000,
|
|
HTTPProxy: os.Getenv("http_proxy"),
|
|
LogFile: "console",
|
|
LogWay: "console",
|
|
LogLevel: "info",
|
|
LogMaxDays: 3,
|
|
DisableLogColor: false,
|
|
AdminAddr: "127.0.0.1",
|
|
AdminPort: 0,
|
|
AdminUser: "",
|
|
AdminPwd: "",
|
|
AssetsDir: "",
|
|
PoolCount: 1,
|
|
TCPMux: true,
|
|
User: "",
|
|
DNSServer: "",
|
|
LoginFailExit: true,
|
|
Start: make([]string, 0),
|
|
Protocol: "tcp",
|
|
TLSEnable: false,
|
|
TLSCertFile: "",
|
|
TLSKeyFile: "",
|
|
TLSTrustedCaFile: "",
|
|
HeartbeatInterval: 30,
|
|
HeartbeatTimeout: 90,
|
|
Metas: make(map[string]string),
|
|
UDPPacketSize: 1500,
|
|
}
|
|
}
|
|
|
|
func (cfg *ClientCommonConf) Check() error {
|
|
if cfg.HeartbeatInterval <= 0 {
|
|
return fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
|
}
|
|
|
|
if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
|
|
return fmt.Errorf("Parse conf error: invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
|
|
}
|
|
|
|
if cfg.TLSEnable == false {
|
|
if cfg.TLSCertFile != "" {
|
|
fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
|
|
}
|
|
|
|
if cfg.TLSKeyFile != "" {
|
|
fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
|
|
}
|
|
|
|
if cfg.TLSTrustedCaFile != "" {
|
|
fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Supported sources including: string(file path), []byte, Reader interface.
|
|
func LoadClientCommonConf(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
|
|
}
|
|
|
|
s, err := f.GetSection("common")
|
|
if err != nil {
|
|
// TODO: add error info
|
|
return ClientCommonConf{}, err
|
|
}
|
|
|
|
common := DefaultClientConf()
|
|
err = s.MapTo(&common)
|
|
if err != nil {
|
|
return ClientCommonConf{}, err
|
|
}
|
|
|
|
common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
|
|
|
|
return common, nil
|
|
}
|
|
|
|
// if len(startProxy) is 0, start all
|
|
// otherwise just start proxies in startProxy map
|
|
func LoadClientBasicConf(
|
|
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
|
|
}
|
|
|
|
proxyConfs := make(map[string]ProxyConf)
|
|
visitorConfs := make(map[string]VisitorConf)
|
|
|
|
if prefix != "" {
|
|
prefix += "."
|
|
}
|
|
|
|
startProxy := make(map[string]struct{})
|
|
for _, s := range start {
|
|
startProxy[s] = struct{}{}
|
|
}
|
|
|
|
startAll := true
|
|
if len(startProxy) > 0 {
|
|
startAll = false
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
rangeSections = append(rangeSections, section)
|
|
}
|
|
|
|
for _, section := range rangeSections {
|
|
err = appendTemplates(f, section)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
|
|
for _, section := range f.Sections() {
|
|
name := section.Name()
|
|
|
|
if name == ini.DefaultSection || name == "common" || strings.HasPrefix(name, "range:") {
|
|
continue
|
|
}
|
|
|
|
_, shouldStart := startProxy[name]
|
|
if !startAll && !shouldStart {
|
|
continue
|
|
}
|
|
|
|
roleType := section.Key("role").String()
|
|
if roleType == "" {
|
|
roleType = "server"
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func appendTemplates(f *ini.File, section *ini.Section) error {
|
|
|
|
// 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")
|
|
}
|
|
|
|
localPorts, err := util.ParseRangeNumbers(localPortStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
remotePorts, err := util.ParseRangeNumbers(remotePortStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(localPorts) != len(remotePorts) {
|
|
return fmt.Errorf("range section [%s] local ports number should be same with remote ports number", section.Name())
|
|
}
|
|
|
|
if len(localPorts) == 0 {
|
|
return fmt.Errorf("range section [%s] local_port and remote_port is necessary", section.Name())
|
|
}
|
|
|
|
// Templates
|
|
prefix := strings.TrimSpace(strings.TrimPrefix(section.Name(), "range:"))
|
|
|
|
for i := range localPorts {
|
|
tmpname := fmt.Sprintf("%s_%d", prefix, i)
|
|
|
|
tmpsection, err := f.NewSection(tmpname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|