2023-09-06 10:18:02 +08:00
|
|
|
// Copyright 2023 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.
|
|
|
|
// 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 legacy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"gopkg.in/ini.v1"
|
|
|
|
|
|
|
|
"github.com/fatedier/frp/pkg/config/types"
|
2023-09-20 15:18:50 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type ProxyType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
ProxyTypeTCP ProxyType = "tcp"
|
|
|
|
ProxyTypeUDP ProxyType = "udp"
|
|
|
|
ProxyTypeTCPMUX ProxyType = "tcpmux"
|
|
|
|
ProxyTypeHTTP ProxyType = "http"
|
|
|
|
ProxyTypeHTTPS ProxyType = "https"
|
|
|
|
ProxyTypeSTCP ProxyType = "stcp"
|
|
|
|
ProxyTypeXTCP ProxyType = "xtcp"
|
|
|
|
ProxyTypeSUDP ProxyType = "sudp"
|
2023-09-06 10:18:02 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Proxy
|
|
|
|
var (
|
2023-09-20 15:18:50 +08:00
|
|
|
proxyConfTypeMap = map[ProxyType]reflect.Type{
|
|
|
|
ProxyTypeTCP: reflect.TypeOf(TCPProxyConf{}),
|
|
|
|
ProxyTypeUDP: reflect.TypeOf(UDPProxyConf{}),
|
|
|
|
ProxyTypeTCPMUX: reflect.TypeOf(TCPMuxProxyConf{}),
|
|
|
|
ProxyTypeHTTP: reflect.TypeOf(HTTPProxyConf{}),
|
|
|
|
ProxyTypeHTTPS: reflect.TypeOf(HTTPSProxyConf{}),
|
|
|
|
ProxyTypeSTCP: reflect.TypeOf(STCPProxyConf{}),
|
|
|
|
ProxyTypeXTCP: reflect.TypeOf(XTCPProxyConf{}),
|
|
|
|
ProxyTypeSUDP: reflect.TypeOf(SUDPProxyConf{}),
|
2023-09-06 10:18:02 +08:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
type ProxyConf interface {
|
|
|
|
// GetBaseConfig returns the BaseProxyConf for this config.
|
|
|
|
GetBaseConfig() *BaseProxyConf
|
|
|
|
// UnmarshalFromIni unmarshals a ini.Section into this config. This function
|
|
|
|
// will be called on the frpc side.
|
|
|
|
UnmarshalFromIni(string, string, *ini.Section) error
|
|
|
|
}
|
|
|
|
|
2023-09-20 15:18:50 +08:00
|
|
|
func NewConfByType(proxyType ProxyType) ProxyConf {
|
2023-09-06 10:18:02 +08:00
|
|
|
v, ok := proxyConfTypeMap[proxyType]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
cfg := reflect.New(v).Interface().(ProxyConf)
|
|
|
|
return cfg
|
|
|
|
}
|
|
|
|
|
|
|
|
// Proxy Conf Loader
|
|
|
|
// DefaultProxyConf creates a empty ProxyConf object by proxyType.
|
|
|
|
// If proxyType doesn't exist, return nil.
|
2023-09-20 15:18:50 +08:00
|
|
|
func DefaultProxyConf(proxyType ProxyType) ProxyConf {
|
2023-09-06 10:18:02 +08:00
|
|
|
return NewConfByType(proxyType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Proxy loaded from ini
|
|
|
|
func NewProxyConfFromIni(prefix, name string, section *ini.Section) (ProxyConf, error) {
|
|
|
|
// section.Key: if key not exists, section will set it with default value.
|
2023-09-20 15:18:50 +08:00
|
|
|
proxyType := ProxyType(section.Key("type").String())
|
2023-09-06 10:18:02 +08:00
|
|
|
if proxyType == "" {
|
2023-09-20 15:18:50 +08:00
|
|
|
proxyType = ProxyTypeTCP
|
2023-09-06 10:18:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
conf := DefaultProxyConf(proxyType)
|
|
|
|
if conf == nil {
|
|
|
|
return nil, fmt.Errorf("invalid type [%s]", proxyType)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := conf.UnmarshalFromIni(prefix, name, section); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return conf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalSvrConf configures what location the client will to, or what
|
|
|
|
// plugin will be used.
|
|
|
|
type LocalSvrConf struct {
|
|
|
|
// LocalIP specifies the IP address or host name to to.
|
|
|
|
LocalIP string `ini:"local_ip" json:"local_ip"`
|
|
|
|
// LocalPort specifies the port to to.
|
|
|
|
LocalPort int `ini:"local_port" json:"local_port"`
|
|
|
|
|
|
|
|
// Plugin specifies what plugin should be used for ng. If this value
|
|
|
|
// is set, the LocalIp and LocalPort values will be ignored. By default,
|
|
|
|
// this value is "".
|
|
|
|
Plugin string `ini:"plugin" json:"plugin"`
|
|
|
|
// PluginParams specify parameters to be passed to the plugin, if one is
|
|
|
|
// being used. By default, this value is an empty map.
|
|
|
|
PluginParams map[string]string `ini:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// HealthCheckConf configures health checking. This can be useful for load
|
|
|
|
// balancing purposes to detect and remove proxies to failing services.
|
|
|
|
type HealthCheckConf struct {
|
|
|
|
// HealthCheckType specifies what protocol to use for health checking.
|
|
|
|
// Valid values include "tcp", "http", and "". If this value is "", health
|
|
|
|
// checking will not be performed. By default, this value is "".
|
|
|
|
//
|
|
|
|
// If the type is "tcp", a connection will be attempted to the target
|
|
|
|
// server. If a connection cannot be established, the health check fails.
|
|
|
|
//
|
|
|
|
// If the type is "http", a GET request will be made to the endpoint
|
|
|
|
// specified by HealthCheckURL. If the response is not a 200, the health
|
|
|
|
// check fails.
|
|
|
|
HealthCheckType string `ini:"health_check_type" json:"health_check_type"` // tcp | http
|
|
|
|
// HealthCheckTimeoutS specifies the number of seconds to wait for a health
|
|
|
|
// check attempt to connect. If the timeout is reached, this counts as a
|
|
|
|
// health check failure. By default, this value is 3.
|
|
|
|
HealthCheckTimeoutS int `ini:"health_check_timeout_s" json:"health_check_timeout_s"`
|
|
|
|
// HealthCheckMaxFailed specifies the number of allowed failures before the
|
|
|
|
// is stopped. By default, this value is 1.
|
|
|
|
HealthCheckMaxFailed int `ini:"health_check_max_failed" json:"health_check_max_failed"`
|
|
|
|
// HealthCheckIntervalS specifies the time in seconds between health
|
|
|
|
// checks. By default, this value is 10.
|
|
|
|
HealthCheckIntervalS int `ini:"health_check_interval_s" json:"health_check_interval_s"`
|
|
|
|
// HealthCheckURL specifies the address to send health checks to if the
|
|
|
|
// health check type is "http".
|
|
|
|
HealthCheckURL string `ini:"health_check_url" json:"health_check_url"`
|
|
|
|
// HealthCheckAddr specifies the address to connect to if the health check
|
|
|
|
// type is "tcp".
|
|
|
|
HealthCheckAddr string `ini:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BaseProxyConf provides configuration info that is common to all types.
|
|
|
|
type BaseProxyConf struct {
|
|
|
|
// ProxyName is the name of this
|
|
|
|
ProxyName string `ini:"name" json:"name"`
|
|
|
|
// ProxyType specifies the type of this Valid values include "tcp",
|
|
|
|
// "udp", "http", "https", "stcp", and "xtcp". By default, this value is
|
|
|
|
// "tcp".
|
|
|
|
ProxyType string `ini:"type" json:"type"`
|
|
|
|
|
|
|
|
// UseEncryption controls whether or not communication with the server will
|
|
|
|
// be encrypted. Encryption is done using the tokens supplied in the server
|
|
|
|
// and client configuration. By default, this value is false.
|
|
|
|
UseEncryption bool `ini:"use_encryption" json:"use_encryption"`
|
|
|
|
// UseCompression controls whether or not communication with the server
|
|
|
|
// will be compressed. By default, this value is false.
|
|
|
|
UseCompression bool `ini:"use_compression" json:"use_compression"`
|
|
|
|
// Group specifies which group the is a part of. The server will use
|
|
|
|
// this information to load balance proxies in the same group. If the value
|
|
|
|
// is "", this will not be in a group. By default, this value is "".
|
|
|
|
Group string `ini:"group" json:"group"`
|
|
|
|
// GroupKey specifies a group key, which should be the same among proxies
|
|
|
|
// of the same group. By default, this value is "".
|
|
|
|
GroupKey string `ini:"group_key" json:"group_key"`
|
|
|
|
|
|
|
|
// ProxyProtocolVersion specifies which protocol version to use. Valid
|
|
|
|
// values include "v1", "v2", and "". If the value is "", a protocol
|
|
|
|
// version will be automatically selected. By default, this value is "".
|
|
|
|
ProxyProtocolVersion string `ini:"proxy_protocol_version" json:"proxy_protocol_version"`
|
|
|
|
|
|
|
|
// BandwidthLimit limit the bandwidth
|
|
|
|
// 0 means no limit
|
|
|
|
BandwidthLimit types.BandwidthQuantity `ini:"bandwidth_limit" json:"bandwidth_limit"`
|
|
|
|
// BandwidthLimitMode specifies whether to limit the bandwidth on the
|
|
|
|
// client or server side. Valid values include "client" and "server".
|
|
|
|
// By default, this value is "client".
|
|
|
|
BandwidthLimitMode string `ini:"bandwidth_limit_mode" json:"bandwidth_limit_mode"`
|
|
|
|
|
|
|
|
// meta info for each proxy
|
|
|
|
Metas map[string]string `ini:"-" json:"metas"`
|
|
|
|
|
|
|
|
LocalSvrConf `ini:",extends"`
|
|
|
|
HealthCheckConf `ini:",extends"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Base
|
|
|
|
func (cfg *BaseProxyConf) GetBaseConfig() *BaseProxyConf {
|
|
|
|
return cfg
|
|
|
|
}
|
|
|
|
|
|
|
|
// BaseProxyConf apply custom logic changes.
|
|
|
|
func (cfg *BaseProxyConf) decorate(_ string, name string, section *ini.Section) error {
|
|
|
|
cfg.ProxyName = name
|
|
|
|
// metas_xxx
|
|
|
|
cfg.Metas = GetMapWithoutPrefix(section.KeysHash(), "meta_")
|
|
|
|
|
|
|
|
// bandwidth_limit
|
|
|
|
if bandwidth, err := section.GetKey("bandwidth_limit"); err == nil {
|
|
|
|
cfg.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidth.String())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// plugin_xxx
|
|
|
|
cfg.LocalSvrConf.PluginParams = GetMapByPrefix(section.KeysHash(), "plugin_")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type DomainConf struct {
|
|
|
|
CustomDomains []string `ini:"custom_domains" json:"custom_domains"`
|
|
|
|
SubDomain string `ini:"subdomain" json:"subdomain"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type RoleServerCommonConf struct {
|
|
|
|
Role string `ini:"role" json:"role"`
|
|
|
|
Sk string `ini:"sk" json:"sk"`
|
|
|
|
AllowUsers []string `ini:"allow_users" json:"allow_users"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTTP
|
|
|
|
type HTTPProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
DomainConf `ini:",extends"`
|
|
|
|
|
|
|
|
Locations []string `ini:"locations" json:"locations"`
|
|
|
|
HTTPUser string `ini:"http_user" json:"http_user"`
|
|
|
|
HTTPPwd string `ini:"http_pwd" json:"http_pwd"`
|
|
|
|
HostHeaderRewrite string `ini:"host_header_rewrite" json:"host_header_rewrite"`
|
|
|
|
Headers map[string]string `ini:"-" json:"headers"`
|
|
|
|
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *HTTPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
cfg.Headers = GetMapWithoutPrefix(section.KeysHash(), "header_")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTTPS
|
|
|
|
type HTTPSProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
DomainConf `ini:",extends"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *HTTPSProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TCP
|
|
|
|
type TCPProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
RemotePort int `ini:"remote_port" json:"remote_port"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *TCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UDP
|
|
|
|
type UDPProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
|
|
|
|
RemotePort int `ini:"remote_port" json:"remote_port"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *UDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TCPMux
|
|
|
|
type TCPMuxProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
DomainConf `ini:",extends"`
|
|
|
|
HTTPUser string `ini:"http_user" json:"http_user,omitempty"`
|
|
|
|
HTTPPwd string `ini:"http_pwd" json:"http_pwd,omitempty"`
|
|
|
|
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
|
|
|
|
|
|
|
|
Multiplexer string `ini:"multiplexer"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *TCPMuxProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// STCP
|
|
|
|
type STCPProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
RoleServerCommonConf `ini:",extends"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *STCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
if cfg.Role == "" {
|
|
|
|
cfg.Role = "server"
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// XTCP
|
|
|
|
type XTCPProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
RoleServerCommonConf `ini:",extends"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *XTCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
if cfg.Role == "" {
|
|
|
|
cfg.Role = "server"
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SUDP
|
|
|
|
type SUDPProxyConf struct {
|
|
|
|
BaseProxyConf `ini:",extends"`
|
|
|
|
RoleServerCommonConf `ini:",extends"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *SUDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
|
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add custom logic unmarshal if exists
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func preUnmarshalFromIni(cfg ProxyConf, prefix string, name string, section *ini.Section) error {
|
|
|
|
err := section.MapTo(cfg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cfg.GetBaseConfig().decorate(prefix, name, section)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|