mirror of https://github.com/fatedier/frp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
5.3 KiB
215 lines
5.3 KiB
// Copyright 2018 fatedier, fatedier@gmail.com |
|
// |
|
// 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 sub |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"net" |
|
"os" |
|
"os/signal" |
|
"strconv" |
|
"strings" |
|
"syscall" |
|
"time" |
|
|
|
"github.com/fatedier/frp/client" |
|
"github.com/fatedier/frp/pkg/auth" |
|
"github.com/fatedier/frp/pkg/config" |
|
"github.com/fatedier/frp/pkg/util/log" |
|
"github.com/fatedier/frp/pkg/util/version" |
|
|
|
"github.com/spf13/cobra" |
|
) |
|
|
|
const ( |
|
CfgFileTypeIni = iota |
|
CfgFileTypeCmd |
|
) |
|
|
|
var ( |
|
cfgFile string |
|
showVersion bool |
|
|
|
serverAddr string |
|
user string |
|
protocol string |
|
token string |
|
logLevel string |
|
logFile string |
|
logMaxDays int |
|
disableLogColor bool |
|
|
|
proxyName string |
|
localIP string |
|
localPort int |
|
remotePort int |
|
useEncryption bool |
|
useCompression bool |
|
customDomains string |
|
subDomain string |
|
httpUser string |
|
httpPwd string |
|
locations string |
|
hostHeaderRewrite string |
|
role string |
|
sk string |
|
multiplexer string |
|
serverName string |
|
bindAddr string |
|
bindPort int |
|
|
|
tlsEnable bool |
|
|
|
kcpDoneCh chan struct{} |
|
) |
|
|
|
func init() { |
|
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc") |
|
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc") |
|
|
|
kcpDoneCh = make(chan struct{}) |
|
} |
|
|
|
func RegisterCommonFlags(cmd *cobra.Command) { |
|
cmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address") |
|
cmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user") |
|
cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket") |
|
cmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") |
|
cmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") |
|
cmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") |
|
cmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") |
|
cmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console") |
|
cmd.PersistentFlags().BoolVarP(&tlsEnable, "tls_enable", "", false, "enable frpc tls") |
|
} |
|
|
|
var rootCmd = &cobra.Command{ |
|
Use: "frpc", |
|
Short: "frpc is the client of frp (https://github.com/fatedier/frp)", |
|
RunE: func(cmd *cobra.Command, args []string) error { |
|
if showVersion { |
|
fmt.Println(version.Full()) |
|
return nil |
|
} |
|
|
|
// Do not show command usage here. |
|
err := runClient(cfgFile) |
|
if err != nil { |
|
fmt.Println(err) |
|
os.Exit(1) |
|
} |
|
return nil |
|
}, |
|
} |
|
|
|
func Execute() { |
|
if err := rootCmd.Execute(); err != nil { |
|
os.Exit(1) |
|
} |
|
} |
|
|
|
func handleSignal(svr *client.Service) { |
|
ch := make(chan os.Signal) |
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) |
|
<-ch |
|
svr.Close() |
|
time.Sleep(250 * time.Millisecond) |
|
close(kcpDoneCh) |
|
} |
|
|
|
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) { |
|
cfg = config.GetDefaultClientConf() |
|
|
|
ipStr, portStr, err := net.SplitHostPort(serverAddr) |
|
if err != nil { |
|
err = fmt.Errorf("invalid server_addr: %v", err) |
|
return |
|
} |
|
|
|
cfg.ServerAddr = ipStr |
|
cfg.ServerPort, err = strconv.Atoi(portStr) |
|
if err != nil { |
|
err = fmt.Errorf("invalid server_addr: %v", err) |
|
return |
|
} |
|
|
|
cfg.User = user |
|
cfg.Protocol = protocol |
|
cfg.LogLevel = logLevel |
|
cfg.LogFile = logFile |
|
cfg.LogMaxDays = int64(logMaxDays) |
|
cfg.DisableLogColor = disableLogColor |
|
|
|
// Only token authentication is supported in cmd mode |
|
cfg.ClientConfig = auth.GetDefaultClientConf() |
|
cfg.Token = token |
|
cfg.TLSEnable = tlsEnable |
|
|
|
cfg.Complete() |
|
if err = cfg.Validate(); err != nil { |
|
err = fmt.Errorf("Parse config error: %v", err) |
|
return |
|
} |
|
return |
|
} |
|
|
|
func runClient(cfgFilePath string) error { |
|
cfg, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(cfgFilePath) |
|
if err != nil { |
|
return err |
|
} |
|
return startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath) |
|
} |
|
|
|
func startService( |
|
cfg config.ClientCommonConf, |
|
pxyCfgs map[string]config.ProxyConf, |
|
visitorCfgs map[string]config.VisitorConf, |
|
cfgFile string, |
|
) (err error) { |
|
|
|
log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, |
|
cfg.LogMaxDays, cfg.DisableLogColor) |
|
|
|
if cfg.DNSServer != "" { |
|
s := cfg.DNSServer |
|
if !strings.Contains(s, ":") { |
|
s += ":53" |
|
} |
|
// Change default dns server for frpc |
|
net.DefaultResolver = &net.Resolver{ |
|
PreferGo: true, |
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) { |
|
return net.Dial("udp", s) |
|
}, |
|
} |
|
} |
|
svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile) |
|
if errRet != nil { |
|
err = errRet |
|
return |
|
} |
|
|
|
// Capture the exit signal if we use kcp. |
|
if cfg.Protocol == "kcp" { |
|
go handleSignal(svr) |
|
} |
|
|
|
err = svr.Run() |
|
if err == nil && cfg.Protocol == "kcp" { |
|
<-kcpDoneCh |
|
} |
|
return |
|
}
|
|
|