|
|
|
// 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 v1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"github.com/samber/lo"
|
|
|
|
|
|
|
|
"github.com/fatedier/frp/pkg/util/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
type VisitorTransport struct {
|
|
|
|
UseEncryption bool `json:"useEncryption,omitempty"`
|
|
|
|
UseCompression bool `json:"useCompression,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type VisitorBaseConfig struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
Transport VisitorTransport `json:"transport,omitempty"`
|
|
|
|
SecretKey string `json:"secretKey,omitempty"`
|
|
|
|
// if the server user is not set, it defaults to the current user
|
|
|
|
ServerUser string `json:"serverUser,omitempty"`
|
|
|
|
ServerName string `json:"serverName,omitempty"`
|
|
|
|
BindAddr string `json:"bindAddr,omitempty"`
|
|
|
|
// BindPort is the port that visitor listens on.
|
|
|
|
// It can be less than 0, it means don't bind to the port and only receive connections redirected from
|
|
|
|
// other visitors. (This is not supported for SUDP now)
|
|
|
|
BindPort int `json:"bindPort,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *VisitorBaseConfig) GetBaseConfig() *VisitorBaseConfig {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *VisitorBaseConfig) Complete(g *ClientCommonConfig) {
|
|
|
|
if c.BindAddr == "" {
|
|
|
|
c.BindAddr = "127.0.0.1"
|
|
|
|
}
|
|
|
|
|
|
|
|
namePrefix := ""
|
|
|
|
if g.User != "" {
|
|
|
|
namePrefix = g.User + "."
|
|
|
|
}
|
|
|
|
c.Name = namePrefix + c.Name
|
|
|
|
|
|
|
|
if c.ServerUser != "" {
|
|
|
|
c.ServerName = c.ServerUser + "." + c.ServerName
|
|
|
|
} else {
|
|
|
|
c.ServerName = namePrefix + c.ServerName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type VisitorConfigurer interface {
|
|
|
|
Complete(*ClientCommonConfig)
|
|
|
|
GetBaseConfig() *VisitorBaseConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
type VisitorType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
VisitorTypeSTCP VisitorType = "stcp"
|
|
|
|
VisitorTypeXTCP VisitorType = "xtcp"
|
|
|
|
VisitorTypeSUDP VisitorType = "sudp"
|
|
|
|
)
|
|
|
|
|
|
|
|
var visitorConfigTypeMap = map[VisitorType]reflect.Type{
|
|
|
|
VisitorTypeSTCP: reflect.TypeOf(STCPVisitorConfig{}),
|
|
|
|
VisitorTypeXTCP: reflect.TypeOf(XTCPVisitorConfig{}),
|
|
|
|
VisitorTypeSUDP: reflect.TypeOf(SUDPVisitorConfig{}),
|
|
|
|
}
|
|
|
|
|
|
|
|
type TypedVisitorConfig struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
VisitorConfigurer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *TypedVisitorConfig) UnmarshalJSON(b []byte) error {
|
|
|
|
if len(b) == 4 && string(b) == "null" {
|
|
|
|
return errors.New("type is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
typeStruct := struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
}{}
|
|
|
|
if err := json.Unmarshal(b, &typeStruct); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Type = typeStruct.Type
|
|
|
|
configurer := NewVisitorConfigurerByType(VisitorType(typeStruct.Type))
|
|
|
|
if configurer == nil {
|
|
|
|
return fmt.Errorf("unknown visitor type: %s", typeStruct.Type)
|
|
|
|
}
|
|
|
|
decoder := json.NewDecoder(bytes.NewBuffer(b))
|
|
|
|
if DisallowUnknownFields {
|
|
|
|
decoder.DisallowUnknownFields()
|
|
|
|
}
|
|
|
|
if err := decoder.Decode(configurer); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.VisitorConfigurer = configurer
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewVisitorConfigurerByType(t VisitorType) VisitorConfigurer {
|
|
|
|
v, ok := visitorConfigTypeMap[t]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
vc := reflect.New(v).Interface().(VisitorConfigurer)
|
|
|
|
vc.GetBaseConfig().Type = string(t)
|
|
|
|
return vc
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ VisitorConfigurer = &STCPVisitorConfig{}
|
|
|
|
|
|
|
|
type STCPVisitorConfig struct {
|
|
|
|
VisitorBaseConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ VisitorConfigurer = &SUDPVisitorConfig{}
|
|
|
|
|
|
|
|
type SUDPVisitorConfig struct {
|
|
|
|
VisitorBaseConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ VisitorConfigurer = &XTCPVisitorConfig{}
|
|
|
|
|
|
|
|
type XTCPVisitorConfig struct {
|
|
|
|
VisitorBaseConfig
|
|
|
|
|
|
|
|
Protocol string `json:"protocol,omitempty"`
|
|
|
|
KeepTunnelOpen bool `json:"keepTunnelOpen,omitempty"`
|
|
|
|
MaxRetriesAnHour int `json:"maxRetriesAnHour,omitempty"`
|
|
|
|
MinRetryInterval int `json:"minRetryInterval,omitempty"`
|
|
|
|
FallbackTo string `json:"fallbackTo,omitempty"`
|
|
|
|
FallbackTimeoutMs int `json:"fallbackTimeoutMs,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *XTCPVisitorConfig) Complete(g *ClientCommonConfig) {
|
|
|
|
c.VisitorBaseConfig.Complete(g)
|
|
|
|
|
|
|
|
c.Protocol = util.EmptyOr(c.Protocol, "quic")
|
|
|
|
c.MaxRetriesAnHour = util.EmptyOr(c.MaxRetriesAnHour, 8)
|
|
|
|
c.MinRetryInterval = util.EmptyOr(c.MinRetryInterval, 90)
|
|
|
|
c.FallbackTimeoutMs = util.EmptyOr(c.FallbackTimeoutMs, 1000)
|
|
|
|
|
|
|
|
if c.FallbackTo != "" {
|
|
|
|
c.FallbackTo = lo.Ternary(g.User == "", "", g.User+".") + c.FallbackTo
|
|
|
|
}
|
|
|
|
}
|