123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- package legacy
- import (
- "fmt"
- "os"
- "path/filepath"
- "slices"
- "strings"
- "gopkg.in/ini.v1"
- legacyauth "github.com/fatedier/frp/pkg/auth/legacy"
- "github.com/fatedier/frp/pkg/util/util"
- )
- type ClientCommonConf struct {
- legacyauth.ClientConfig `ini:",extends"`
-
-
- ServerAddr string `ini:"server_addr" json:"server_addr"`
-
-
- ServerPort int `ini:"server_port" json:"server_port"`
-
- NatHoleSTUNServer string `ini:"nat_hole_stun_server" json:"nat_hole_stun_server"`
-
- DialServerTimeout int64 `ini:"dial_server_timeout" json:"dial_server_timeout"`
-
-
- DialServerKeepAlive int64 `ini:"dial_server_keepalive" json:"dial_server_keepalive"`
-
-
-
- ConnectServerLocalIP string `ini:"connect_server_local_ip" json:"connect_server_local_ip"`
-
-
-
- HTTPProxy string `ini:"http_proxy" json:"http_proxy"`
-
-
-
- LogFile string `ini:"log_file" json:"log_file"`
-
-
-
-
- LogWay string `ini:"log_way" json:"log_way"`
-
-
- LogLevel string `ini:"log_level" json:"log_level"`
-
-
-
- LogMaxDays int64 `ini:"log_max_days" json:"log_max_days"`
-
-
- DisableLogColor bool `ini:"disable_log_color" json:"disable_log_color"`
-
-
- AdminAddr string `ini:"admin_addr" json:"admin_addr"`
-
-
-
- AdminPort int `ini:"admin_port" json:"admin_port"`
-
-
- AdminUser string `ini:"admin_user" json:"admin_user"`
-
-
- AdminPwd string `ini:"admin_pwd" json:"admin_pwd"`
-
-
-
- AssetsDir string `ini:"assets_dir" json:"assets_dir"`
-
-
- PoolCount int `ini:"pool_count" json:"pool_count"`
-
-
-
-
- TCPMux bool `ini:"tcp_mux" json:"tcp_mux"`
-
-
- TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"`
-
-
-
- User string `ini:"user" json:"user"`
-
-
- DNSServer string `ini:"dns_server" json:"dns_server"`
-
-
-
- LoginFailExit bool `ini:"login_fail_exit" json:"login_fail_exit"`
-
-
-
- Start []string `ini:"start" json:"start"`
-
-
-
-
- Protocol string `ini:"protocol" json:"protocol"`
-
- QUICKeepalivePeriod int `ini:"quic_keepalive_period" json:"quic_keepalive_period"`
- QUICMaxIdleTimeout int `ini:"quic_max_idle_timeout" json:"quic_max_idle_timeout"`
- QUICMaxIncomingStreams int `ini:"quic_max_incoming_streams" json:"quic_max_incoming_streams"`
-
-
-
-
- TLSEnable bool `ini:"tls_enable" json:"tls_enable"`
-
-
- TLSCertFile string `ini:"tls_cert_file" json:"tls_cert_file"`
-
-
-
- TLSKeyFile string `ini:"tls_key_file" json:"tls_key_file"`
-
-
-
- TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
-
-
- TLSServerName string `ini:"tls_server_name" json:"tls_server_name"`
-
-
-
- DisableCustomTLSFirstByte bool `ini:"disable_custom_tls_first_byte" json:"disable_custom_tls_first_byte"`
-
-
-
- HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"`
-
-
-
- HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"`
-
- Metas map[string]string `ini:"-" json:"metas"`
-
-
- UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
-
- IncludeConfigFiles []string `ini:"includes" json:"includes"`
-
-
- PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
- }
- func UnmarshalClientConfFromIni(source any) (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 {
- return ClientCommonConf{}, fmt.Errorf("invalid configuration file, not found [common] section")
- }
- common := GetDefaultClientConf()
- err = s.MapTo(&common)
- if err != nil {
- return ClientCommonConf{}, err
- }
- common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
- common.ClientConfig.OidcAdditionalEndpointParams = GetMapWithoutPrefix(s.KeysHash(), "oidc_additional_")
- return common, nil
- }
- func LoadAllProxyConfsFromIni(
- prefix string,
- source any,
- 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
- }
-
- 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 = renderRangeProxyTemplates(f, section)
- if err != nil {
- return nil, nil, fmt.Errorf("failed to render template for proxy %s: %v", section.Name(), 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("failed to parse proxy %s, err: %v", name, newErr)
- }
- proxyConfs[prefix+name] = newConf
- case "visitor":
- newConf, newErr := NewVisitorConfFromIni(prefix, name, section)
- if newErr != nil {
- return nil, nil, fmt.Errorf("failed to parse visitor %s, err: %v", name, newErr)
- }
- visitorConfs[prefix+name] = newConf
- default:
- return nil, nil, fmt.Errorf("proxy %s role should be 'server' or 'visitor'", name)
- }
- }
- return proxyConfs, visitorConfs, nil
- }
- func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error {
-
- 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("local ports number should be same with remote ports number")
- }
- if len(localPorts) == 0 {
- return fmt.Errorf("local_port and remote_port is necessary")
- }
-
- 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)
- if _, err := tmpsection.NewKey("local_port", fmt.Sprintf("%d", localPorts[i])); err != nil {
- return fmt.Errorf("local_port new key in section error: %v", err)
- }
- if _, err := tmpsection.NewKey("remote_port", fmt.Sprintf("%d", remotePorts[i])); err != nil {
- return fmt.Errorf("remote_port new key in section error: %v", err)
- }
- }
- return nil
- }
- func copySection(source, target *ini.Section) {
- for key, value := range source.KeysHash() {
- _, _ = target.NewKey(key, value)
- }
- }
- func GetDefaultClientConf() ClientCommonConf {
- return ClientCommonConf{
- ClientConfig: legacyauth.GetDefaultClientConf(),
- TCPMux: true,
- LoginFailExit: true,
- Protocol: "tcp",
- Start: make([]string, 0),
- TLSEnable: true,
- DisableCustomTLSFirstByte: true,
- Metas: make(map[string]string),
- IncludeConfigFiles: make([]string, 0),
- }
- }
- func (cfg *ClientCommonConf) Validate() error {
- if cfg.HeartbeatTimeout > 0 && cfg.HeartbeatInterval > 0 {
- if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
- return fmt.Errorf("invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
- }
- }
- if !cfg.TLSEnable {
- 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")
- }
- }
- if !slices.Contains([]string{"tcp", "kcp", "quic", "websocket", "wss"}, cfg.Protocol) {
- return fmt.Errorf("invalid protocol")
- }
- for _, f := range cfg.IncludeConfigFiles {
- absDir, err := filepath.Abs(filepath.Dir(f))
- if err != nil {
- return fmt.Errorf("include: parse directory of %s failed: %v", f, err)
- }
- if _, err := os.Stat(absDir); os.IsNotExist(err) {
- return fmt.Errorf("include: directory of %s not exist", f)
- }
- }
- return nil
- }
|