frp/pkg/config/legacy/proxy.go

388 lines
12 KiB
Go

// 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"
)
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"
)
// Proxy
var (
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{}),
}
)
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
}
func NewConfByType(proxyType ProxyType) ProxyConf {
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.
func DefaultProxyConf(proxyType ProxyType) ProxyConf {
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.
proxyType := ProxyType(section.Key("type").String())
if proxyType == "" {
proxyType = ProxyTypeTCP
}
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
}