frp/pkg/config/v1/visitor.go
psbelejden f9e8b3a3fa Add STCP proxy and visitor support
Implement a simplified version of STCP functionality.

* **Client Proxy Configuration and Handling**
  - Add `STCPProxyConfig` to the `pxyConfs` slice in `client/proxy/general_tcp.go`.

* **Client Visitor Configuration and Handling**
  - Add `STCPVisitorConfig` to the `cfg` struct in `client/visitor/stcp.go`.

* **Command Registration and Initialization**
  - Add `STCP` to the `proxyTypes` and `visitorTypes` slices in `cmd/frpc/sub/proxy.go`.

* **Configuration Examples**
  - Add example configurations for `STCP` proxy and visitor in `conf/frpc_full_example.toml`.
  - Add example configurations for `STCP` proxy and visitor in `conf/legacy/frpc_legacy_full.ini`.

* **Configuration Structures**
  - Add `STCPProxyConfig` struct and include it in the `proxyConfigTypeMap` in `pkg/config/v1/proxy.go`.
  - Add `STCPVisitorConfig` struct and include it in the `visitorConfigTypeMap` in `pkg/config/v1/visitor.go`.

* **Configuration Validation**
  - Add validation for `STCPProxyConfig` in `pkg/config/v1/validation/proxy.go`.
  - Add validation for `STCPVisitorConfig` in `pkg/config/v1/validation/visitor.go`.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/fatedier/frp?shareId=XXXX-XXXX-XXXX-XXXX).
2024-11-21 01:23:25 +08:00

161 lines
4.1 KiB
Go

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{}),
STCPVisitorConfig: reflect.TypeOf(STCPVisitorConfig{}), // P1bb7
}
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 fmt.Errorf("unmarshal VisitorConfig error: %v", err)
}
c.VisitorConfigurer = configurer
return nil
}
func (c *TypedVisitorConfig) MarshalJSON() ([]byte, error) {
return json.Marshal(c.VisitorConfigurer)
}
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
}
}