diff --git a/client/control.go b/client/control.go index eeea1285..c69d385a 100644 --- a/client/control.go +++ b/client/control.go @@ -163,9 +163,9 @@ func (ctl *Control) handleNewProxyResp(m msg.Message) { // Start a new proxy handler if no error got err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error) if err != nil { - xl.Warnf("[%s] start error: %v", inMsg.ProxyName, err) + xl.Warnf("[%s] 启动时发生错误: %v", inMsg.ProxyName, err) } else { - xl.Infof("[%s] start proxy success", inMsg.ProxyName) + xl.Infof("[%s] 隧道启动成功!", inMsg.ProxyName) } } diff --git a/client/service.go b/client/service.go index 0cbd8757..00b987ed 100644 --- a/client/service.go +++ b/client/service.go @@ -205,7 +205,7 @@ func (svr *Service) keepControllerWorking() { svr.loopLoginUntilSuccess(20*time.Second, false) if svr.ctl != nil { <-svr.ctl.Done() - return false, errors.New("control is closed and try another loop") + return false, errors.New("控制已关闭,尝试另一个循环") } // If the control is nil, it means that the login failed and the service is also closed. return false, nil @@ -281,9 +281,9 @@ func (svr *Service) login() (conn net.Conn, connector Connector, err error) { } svr.runID = loginRespMsg.RunID - xl.AddPrefix(xlog.LogPrefix{Name: "runID", Value: svr.runID}) + xl.AddPrefix(xlog.LogPrefix{Name: "运行ID", Value: svr.runID}) - xl.Infof("login to server success, get run id [%s]", loginRespMsg.RunID) + xl.Infof("登录服务器成功!获取RunID [%s]", loginRespMsg.RunID) return } @@ -291,10 +291,10 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE xl := xlog.FromContextSafe(svr.ctx) loginFunc := func() (bool, error) { - xl.Infof("try to connect to server...") + xl.Infof("尝试连接到服务器...") conn, connector, err := svr.login() if err != nil { - xl.Warnf("connect to server error: %v", err) + xl.Warnf("连接到服务器时发生错误: %v", err) if firstLoginExit { svr.cancel(cancelErr{Err: err}) } @@ -320,7 +320,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE ctl, err := NewControl(svr.ctx, sessionCtx) if err != nil { conn.Close() - xl.Errorf("NewControl error: %v", err) + xl.Errorf("新控件出现错误: %v", err) return false, err } ctl.SetInWorkConnCallback(svr.handleWorkConnCb) diff --git a/client/visitor/stcp.go b/client/visitor/stcp.go index b26faf52..f791705e 100644 --- a/client/visitor/stcp.go +++ b/client/visitor/stcp.go @@ -56,7 +56,7 @@ func (sv *STCPVisitor) worker() { for { conn, err := sv.l.Accept() if err != nil { - xl.Warnf("stcp local listener closed") + xl.Warnf("STCP本地监听关闭") return } go sv.handleConn(conn) @@ -68,7 +68,7 @@ func (sv *STCPVisitor) internalConnWorker() { for { conn, err := sv.internalLn.Accept() if err != nil { - xl.Warnf("stcp internal listener closed") + xl.Warnf("STCP互联网监听关闭") return } go sv.handleConn(conn) @@ -79,7 +79,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) { xl := xlog.FromContextSafe(sv.ctx) defer userConn.Close() - xl.Debugf("get a new stcp user connection") + xl.Debugf("获取到一个新的STCP用户链接.") visitorConn, err := sv.helper.ConnectServer() if err != nil { return @@ -97,7 +97,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) { } err = msg.WriteMsg(visitorConn, newVisitorConnMsg) if err != nil { - xl.Warnf("send newVisitorConnMsg to server error: %v", err) + xl.Warnf("发生新参与者链接信息到服务器时发生错误: %v", err) return } @@ -105,13 +105,13 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) { _ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second)) err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg) if err != nil { - xl.Warnf("get newVisitorConnRespMsg error: %v", err) + xl.Warnf("获取新参与者链接信息时发生错误: %v", err) return } _ = visitorConn.SetReadDeadline(time.Time{}) if newVisitorConnRespMsg.Error != "" { - xl.Warnf("start new visitor connection error: %s", newVisitorConnRespMsg.Error) + xl.Warnf("启动一个新的参与者链接时发生错误: %s", newVisitorConnRespMsg.Error) return } @@ -120,7 +120,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) { if sv.cfg.Transport.UseEncryption { remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey)) if err != nil { - xl.Errorf("create encryption stream error: %v", err) + xl.Errorf("创建流线型加密连接时出现错误: %v", err) return } } diff --git a/client/visitor/visitor_manager.go b/client/visitor/visitor_manager.go index 6ff65dab..2898eb51 100644 --- a/client/visitor/visitor_manager.go +++ b/client/visitor/visitor_manager.go @@ -79,14 +79,14 @@ func (vm *Manager) keepVisitorsRunning() { for { select { case <-vm.stopCh: - xl.Tracef("gracefully shutdown visitor manager") + xl.Tracef("优雅地关闭访客管理器(?)") return case <-ticker.C: vm.mu.Lock() for _, cfg := range vm.cfgs { name := cfg.GetBaseConfig().Name if _, exist := vm.visitors[name]; !exist { - xl.Infof("try to start visitor [%s]", name) + xl.Infof("尝试加入发起者 [%s]", name) _ = vm.startVisitor(cfg) } } @@ -115,10 +115,10 @@ func (vm *Manager) startVisitor(cfg v1.VisitorConfigurer) (err error) { visitor := NewVisitor(vm.ctx, cfg, vm.clientCfg, vm.helper) err = visitor.Run() if err != nil { - xl.Warnf("start error: %v", err) + xl.Warnf("启动错误: %v", err) } else { vm.visitors[name] = visitor - xl.Infof("start visitor success") + xl.Infof("参与者服务启动成功") } return } @@ -156,7 +156,7 @@ func (vm *Manager) UpdateAll(cfgs []v1.VisitorConfigurer) { } } if len(delNames) > 0 { - xl.Infof("visitor removed: %v", delNames) + xl.Infof("参与者已移除: %v", delNames) } addNames := make([]string, 0) @@ -169,7 +169,7 @@ func (vm *Manager) UpdateAll(cfgs []v1.VisitorConfigurer) { } } if len(addNames) > 0 { - xl.Infof("visitor added: %v", addNames) + xl.Infof("参与者已成功加入: %v", addNames) } } @@ -179,7 +179,7 @@ func (vm *Manager) TransferConn(name string, conn net.Conn) error { defer vm.mu.RUnlock() v, ok := vm.visitors[name] if !ok { - return fmt.Errorf("visitor [%s] not found", name) + return fmt.Errorf("发起者 [%s] 未找到", name) } return v.AcceptConn(conn) } diff --git a/cmd/frpc/main.go b/cmd/frpc/main.go index f7651bca..9c35c848 100644 --- a/cmd/frpc/main.go +++ b/cmd/frpc/main.go @@ -1,26 +1,400 @@ -// Copyright 2016 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 main import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + _ "github.com/fatedier/frp/assets/frpc" "github.com/fatedier/frp/cmd/frpc/sub" "github.com/fatedier/frp/pkg/util/system" + "github.com/fatedier/frp/pkg/util/version" ) +type Config struct { + Type string `json:"type"` + Format string `json:"format"` + Csrf string `json:"csrf"` + ProxyID string `json:"proxy"` +} + +type Response struct { + Status int `json:"status"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + func main() { + fmt.Println(` + __ __ ________ +| \ | \ | \ +| $$ | $$ ______ __ __ | $$$$$$$$______ ______ +| $$__| $$ | \ | \ | \| $$__ / \ / \ +| $$ $$ \$$$$$$\| $$ | $$| $$ \ | $$$$$$\| $$$$$$\ +| $$$$$$$$ / $$| $$ | $$| $$$$$ | $$ \$$| $$ | $$ +| $$ | $$| $$$$$$$| $$__/ $$| $$ | $$ | $$__/ $$ +| $$ | $$ \$$ $$ \$$ $$| $$ | $$ | $$ $$ + \$$ \$$ \$$$$$$$ _\$$$$$$$ \$$ \$$ | $$$$$$$ + | \__| $$ | $$ + \$$ $$ | $$ + \$$$$$$ \$$ ——— HayFrp公益项目运营&开发组 + + `) + fmt.Println("欢迎使用HayFrp!") + fmt.Println("HayFrp程序发行版本:" + version.Full()) + // 获取命令行参数 + args := os.Args[1:] + // 检查参数数量 + for _, arg := range args { + if arg == "-m" { + fmt.Println("[HayFrpOH] 捕捉到配置命令,模式已自动切换为HayFrpOH") + // 检测是否自定义API地址 + api := "https://api.hayfrp.org/" // 默认API地址 + // 解析参数 + var id, csrf string + var isS bool + for i, arg := range args { + switch arg { + case "-m": + if i+1 < len(args) { + csrf = args[i+1] + } + case "-h": + isS = true + + case "-i": + if i+1 < len(args) { + api = args[i+1] + } + } + } + + // 检查是否提供了id和csrf + if csrf == "" { + fmt.Println("[HayFrpOH] 请提供有效的CSRF") + return + } + + // 构建请求体 + config := Config{ + Type: "config", + Csrf: csrf, + ProxyID: id, // 如果id不为空,则添加到请求体中 + } + + // 如果是-h操作,则设置format为toml + if isS { + config.Format = "toml" + fmt.Println("[HayFrpOH] HayFrpOH设定模式:发起端.") + } else { + fmt.Println("[HayFrpOH] HayFrpOH设定模式:参与端.") + } + + jsonData, err := json.Marshal(config) + if err != nil { + fmt.Println("[HayFrpOH] JSON编码错误:", err) + return + } + + fmt.Println("[HayFrpOH] 拉取配置文件......") + // 发送POST请求 + client := &http.Client{} + var req *http.Request + req, err = http.NewRequest("POST", api+"p2p", bytes.NewBuffer(jsonData)) + if err != nil { + fmt.Println("[HayFrpOH] 创建请求错误:", err) + return + } + + // 设置请求头 + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "HayFrpClient/114514") + req.Header.Set("waf", "off") + + resp, err := client.Do(req) + if err != nil { + fmt.Println("[HayFrpOH] 请求错误,请尝试使用加速器:", err) + return + } + defer resp.Body.Close() + + fmt.Println("[HayFrpOH] 拉取成功.") + fmt.Println("[HayFrpOH] 解析配置文件......") + fmt.Println("[HayFrpOH] 成功,即将启动FRP进行X/S TCP") + // 读取响应 + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("[HayFrpOH] 读取响应错误:", err) + return + } + + // 解析响应的JSON内容 + var response Response + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println("[HayFrpOH] 解析响应错误:", err) + return + } + + // 检查响应状态 + if response.Status != 200 { + fmt.Println("[HayFrpOH] 请求失败:", response.Message) + return + } + + // 解析data字段 + data := response.Data.(map[string]interface{}) + // 获取当前文件的绝对路径 + exePath, err := os.Executable() + if err != nil { + fmt.Println("Error getting executable path:", err) + return + } + exeDir := filepath.Dir(exePath) + programName := filepath.Base(os.Args[0]) + goPath := filepath.Join(exeDir, programName) + os.Args = []string{} + // 构造启动命令 + cmd := exec.Command(goPath, data["proxy_type"].(string), "visitor", "-s", data["hostname"].(string), "-P", data["port"].(string), "-t", "ConnectHayFrpTokenWelcomToUseOurCloudServiceDonttellyourtokentotheotherpeoplesbecauseyouwilllostyouraccount", "-u", data["token"].(string), "--sk", data["sk"].(string), "--tls-enable", "false", "-p", "tcp", "--dns-server", "223.5.5.5", "--server-name", data["proxy_name"].(string), "-n", data["proxy_name"].(string)+"_visitor", "--bind-addr", "127.0.0.1", "--bind-port", data["remote_port"].(string), "--uc", data["use_compression"].(string), "--ue", data["use_encryption"].(string)) + if runtime.GOOS != "windows" { + cmd = exec.Command(goPath, data["proxy_type"].(string), "visitor", "-s", data["hostname"].(string), "-P", data["port"].(string), "-t", "ConnectHayFrpTokenWelcomToUseOurCloudServiceDonttellyourtokentotheotherpeoplesbecauseyouwilllostyouraccount", "-u", data["token"].(string), "--sk", data["sk"].(string), "--tls-enable", "false", "-p", "tcp", "--dns-server", "223.5.5.5", "--server-name", data["proxy_name"].(string), "-n", data["proxy_name"].(string)+"_visitor", "--bind-addr", "127.0.0.1", "--bind-port", data["remote_port"].(string), "--uc", data["use_compression"].(string), "--ue", data["use_encryption"].(string)) + } + + // 检查是否需要添加-s参数 + if isS { + // 构造启动命令 + cmd = exec.Command(goPath, data["proxy_type"].(string), "-s", data["hostname"].(string), "-P", data["port"].(string), "-t", "ConnectHayFrpTokenWelcomToUseOurCloudServiceDonttellyourtokentotheotherpeoplesbecauseyouwilllostyouraccount", "-u", data["token"].(string), "--sk", data["sk"].(string), "--tls-enable", "false", "-p", "tcp", "--dns-server", "223.5.5.5", "--proxy-name", data["proxy_name"].(string), "-i", data["local_ip"].(string), "-l", data["local_port"].(string), "--uc", data["use_compression"].(string), "--ue", data["use_encryption"].(string)) + if runtime.GOOS != "windows" { + cmd = exec.Command(goPath, data["proxy_type"].(string), "-s", data["hostname"].(string), "-P", data["port"].(string), "-t", "ConnectHayFrpTokenWelcomToUseOurCloudServiceDonttellyourtokentotheotherpeoplesbecauseyouwilllostyouraccount", "-u", data["token"].(string), "--sk", data["sk"].(string), "--tls-enable", "false", "-p", "tcp", "--dns-server", "223.5.5.5", "--proxy-name", data["proxy_name"].(string), "-i", data["local_ip"].(string), "-l", data["local_port"].(string), "--uc", data["use_compression"].(string), "--ue", data["use_encryption"].(string)) + } + } + + // 获取命令的输出和错误信息 + stdout, err := cmd.StdoutPipe() + if err != nil { + fmt.Println("[HayFrpOH] 获取命令输出错误:", err) + return + } + stderr, err := cmd.StderrPipe() + if err != nil { + fmt.Println("[HayFrpOH] 获取命令错误输出错误:", err) + return + } + + // 启动命令 + err = cmd.Start() + if err != nil { + fmt.Println("[HayFrpOH] 启动命令错误:", err) + return + } + + // 读取输出和错误信息 + go func() { + buf := make([]byte, 1024) + for { + n, err := stdout.Read(buf) + if n > 0 { + fmt.Print(string(buf[:n])) + } + if err != nil { + break + } + } + }() + + go func() { + buf := make([]byte, 1024) + for { + n, err := stderr.Read(buf) + if n > 0 { + fmt.Print(string(buf[:n])) + } + if err != nil { + break + } + } + }() + + // 等待命令完成 + err = cmd.Wait() + if err != nil { + fmt.Println("[HayFrpOH] 命令执行错误:", err) + return + } + + fmt.Println("[HayFrpOH] 任务被结束.") + os.Exit(0) + } + + if arg == "-esirun" { + fmt.Println("[HayFrpEasyRun] 捕捉到配置命令,模式已自动切换为快速启动.") + // 检测是否自定义API地址 + api := "https://api.hayfrp.org/" + // 解析参数 + var id, csrf string + for i, arg := range args { + switch arg { + case "-esirun": + if i+1 < len(args) { + csrf = args[i+1] + } + + case "-i": + if i+1 < len(args) { + api = args[i+1] + } + } + } + + // 检查是否提供了id和csrf + if csrf == "" { + fmt.Println("[HayFrpEasyRun] 请提供有效的CSRF") + return + } + + // 构建请求体 + config := Config{ + Type: "config", + Csrf: csrf, + ProxyID: id, // 如果id不为空,则添加到请求体中 + } + + jsonData, err := json.Marshal(config) + if err != nil { + fmt.Println("[HayFrpEasyRun] JSON编码错误:", err) + return + } + + fmt.Println("[HayFrpEasyRun] 拉取配置文件......") + // 发送POST请求 + client := &http.Client{} + var req *http.Request + req, err = http.NewRequest("POST", api+"esirun", bytes.NewBuffer(jsonData)) + if err != nil { + fmt.Println("[HayFrpEasyRun] 创建请求错误:", err) + return + } + + // 设置请求头 + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "HayFrpClient/114514") + req.Header.Set("waf", "off") + + resp, err := client.Do(req) + if err != nil { + fmt.Println("[HayFrpEasyRun] 请求错误,请尝试使用加速器:", err) + return + } + defer resp.Body.Close() + + fmt.Println("[HayFrpEasyRun] 拉取成功.") + fmt.Println("[HayFrpEasyRun] 解析配置文件......") + fmt.Println("[HayFrpEasyRun] 成功,即将启动FRP") + // 读取响应 + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("[HayFrpEasyRun] 读取响应错误:", err) + return + } + + // 解析响应的JSON内容 + var response Response + err = json.Unmarshal(body, &response) + if err != nil { + fmt.Println("[HayFrpEasyRun] 解析响应错误:", err) + return + } + + // 检查响应状态 + if response.Status != 200 { + fmt.Println("[HayFrpEasyRun] 请求失败:", response.Message) + return + } + + // 解析data字段 + data := response.Data.(map[string]interface{}) + // 获取当前文件的绝对路径 + exePath, err := os.Executable() + if err != nil { + fmt.Println("Error getting executable path:", err) + return + } + exeDir := filepath.Dir(exePath) + programName := filepath.Base(os.Args[0]) + goPath := filepath.Join(exeDir, programName) + os.Args = []string{} + // 构造启动命令 + + cmd := exec.Command(goPath, data["proxy_type"].(string), "-s", data["hostname"].(string), "-P", data["port"].(string), "-t", "ConnectHayFrpTokenWelcomToUseOurCloudServiceDonttellyourtokentotheotherpeoplesbecauseyouwilllostyouraccount", "-u", data["token"].(string), "--tls-enable", "false", "-p", "tcp", "--dns-server", "223.5.5.5", "-n", data["proxy_name"].(string), "-i", data["local_ip"].(string), "-l", data["local_port"].(string), "-r", data["remote_port"].(string), "--uc", data["use_compression"].(string), "--ue", data["use_encryption"].(string)) + if data["proxy_type"].(string) == "http" || data["proxy_type"].(string) == "https" { + cmd = exec.Command(goPath, data["proxy_type"].(string), "-s", data["ip"].(string), "-P", data["port"].(string), "-t", "ConnectHayFrpTokenWelcomToUseOurCloudServiceDonttellyourtokentotheotherpeoplesbecauseyouwilllostyouraccount", "-u", data["token"].(string), "--tls-enable", "false", "-p", "tcp", "--dns-server", "223.5.5.5", "-n", data["proxy_name"].(string), "-i", data["local_ip"].(string), "-l", data["local_port"].(string), "-d", data["domain"].(string), "--uc", data["use_compression"].(string), "--ue", data["use_encryption"].(string)) + } + + // 获取命令的输出和错误信息 + stdout, err := cmd.StdoutPipe() + if err != nil { + fmt.Println("[HayFrpEasyRun] 获取命令输出错误:", err) + return + } + stderr, err := cmd.StderrPipe() + if err != nil { + fmt.Println("[HayFrpEasyRun] 获取命令错误输出错误:", err) + return + } + + // 启动命令 + err = cmd.Start() + if err != nil { + fmt.Println("[HayFrpEasyRun] 启动命令错误:", err) + return + } + + // 读取输出和错误信息 + go func() { + buf := make([]byte, 1024) + for { + n, err := stdout.Read(buf) + if n > 0 { + fmt.Print(string(buf[:n])) + } + if err != nil { + break + } + } + }() + + go func() { + buf := make([]byte, 1024) + for { + n, err := stderr.Read(buf) + if n > 0 { + fmt.Print(string(buf[:n])) + } + if err != nil { + break + } + } + }() + + // 等待命令完成 + err = cmd.Wait() + if err != nil { + fmt.Println("[HayFrpEasyRun] 命令执行错误:", err) + return + } + + fmt.Println("[HayFrpEasyRun] 任务被结束.") + os.Exit(0) + } + } + system.EnableCompatibilityMode() sub.Execute() + } diff --git a/cmd/frpc/sub/proxy.go b/cmd/frpc/sub/proxy.go index c5d76b1e..ddcbd338 100644 --- a/cmd/frpc/sub/proxy.go +++ b/cmd/frpc/sub/proxy.go @@ -47,7 +47,7 @@ func init() { for _, typ := range proxyTypes { c := v1.NewProxyConfigurerByType(typ) if c == nil { - panic("proxy type: " + typ + " not support") + panic("隧道类型: " + typ + " 不支持") } clientCfg := v1.ClientCommonConfig{} cmd := NewProxyCommand(string(typ), c, &clientCfg) @@ -58,7 +58,7 @@ func init() { if slices.Contains(visitorTypes, v1.VisitorType(typ)) { vc := v1.NewVisitorConfigurerByType(v1.VisitorType(typ)) if vc == nil { - panic("visitor type: " + typ + " not support") + panic("参与者类型: " + typ + " 不支持") } visitorCmd := NewVisitorCommand(string(typ), vc, &clientCfg) config.RegisterVisitorFlags(visitorCmd, vc) @@ -71,7 +71,7 @@ func init() { func NewProxyCommand(name string, c v1.ProxyConfigurer, clientCfg *v1.ClientCommonConfig) *cobra.Command { return &cobra.Command{ Use: name, - Short: fmt.Sprintf("Run frpc with a single %s proxy", name), + Short: fmt.Sprintf("运行带有单个 %s 隧道的Frpc", name), Run: func(cmd *cobra.Command, args []string) { clientCfg.Complete() if _, err := validation.ValidateClientCommonConfig(clientCfg); err != nil { @@ -97,7 +97,7 @@ func NewProxyCommand(name string, c v1.ProxyConfigurer, clientCfg *v1.ClientComm func NewVisitorCommand(name string, c v1.VisitorConfigurer, clientCfg *v1.ClientCommonConfig) *cobra.Command { return &cobra.Command{ Use: "visitor", - Short: fmt.Sprintf("Run frpc with a single %s visitor", name), + Short: fmt.Sprintf("运行带有单个 %s 参与者的Frpc", name), Run: func(cmd *cobra.Command, args []string) { clientCfg.Complete() if _, err := validation.ValidateClientCommonConfig(clientCfg); err != nil { diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index b844ddfd..8f32a324 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -87,7 +87,7 @@ func runMultipleClients(cfgDir string) error { defer wg.Done() err := runClient(path) if err != nil { - fmt.Printf("frpc service error for config file [%s]\n", path) + fmt.Printf("Frpc发生错误在配置文件 [%s]\n", path) } }() return nil @@ -116,13 +116,12 @@ func runClient(cfgFilePath string) error { return err } if isLegacyFormat { - fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " + - "please use yaml/json/toml format instead!\n") + fmt.Printf("警告:INI文件格式在未来将会不受支持并且移除\n") } warning, err := validation.ValidateAllClientConfig(cfg, proxyCfgs, visitorCfgs) if warning != nil { - fmt.Printf("WARNING: %v\n", warning) + fmt.Printf("警告: %v\n", warning) } if err != nil { return err @@ -139,8 +138,8 @@ func startService( log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor) if cfgFile != "" { - log.Infof("start frpc service for config file [%s]", cfgFile) - defer log.Infof("frpc service for config file [%s] stopped", cfgFile) + log.Infof("启动Frpc配置文件: [%s]", cfgFile) + defer log.Infof("Frpc配置文件 [%s] 已停止", cfgFile) } svr, err := client.NewService(client.ServiceOptions{ Common: cfg, diff --git a/cmd/frps/main.go b/cmd/frps/main.go index 3cb398d8..6f1ec95b 100644 --- a/cmd/frps/main.go +++ b/cmd/frps/main.go @@ -15,12 +15,40 @@ package main import ( + "fmt" + "os" + _ "github.com/fatedier/frp/assets/frps" _ "github.com/fatedier/frp/pkg/metrics" "github.com/fatedier/frp/pkg/util/system" + "github.com/fatedier/frp/pkg/util/version" ) func main() { + fmt.Println(` + __ __ ________ +| \ | \ | \ +| $$ | $$ ______ __ __ | $$$$$$$$______ ______ +| $$__| $$ | \ | \ | \| $$__ / \ / \ +| $$ $$ \$$$$$$\| $$ | $$| $$ \ | $$$$$$\| $$$$$$\ +| $$$$$$$$ / $$| $$ | $$| $$$$$ | $$ \$$| $$ | $$ +| $$ | $$| $$$$$$$| $$__/ $$| $$ | $$ | $$__/ $$ +| $$ | $$ \$$ $$ \$$ $$| $$ | $$ | $$ $$ + \$$ \$$ \$$$$$$$ _\$$$$$$$ \$$ \$$ | $$$$$$$ + | \__| $$ | $$ + \$$ $$ | $$ + \$$$$$$ \$$ ——— HayFrp公益项目运营&开发组 + + `) + fmt.Println("欢迎使用HayFrp!") + fmt.Println("HayFrp程序发行版本:" + version.Full()) system.EnableCompatibilityMode() + + // 检查是否有frps.ini文件 + if _, err := os.Stat("frps.ini"); err == nil { + // 如果有,将-c frps.ini添加到os.Args中 + os.Args = append(os.Args, "-c", "frps.ini") + } + Execute() } diff --git a/cmd/frps/root.go b/cmd/frps/root.go index fff487d1..f081a1f0 100644 --- a/cmd/frps/root.go +++ b/cmd/frps/root.go @@ -17,7 +17,11 @@ package main import ( "context" "fmt" + "io/ioutil" + "net/http" "os" + "strconv" + "time" "github.com/spf13/cobra" @@ -66,8 +70,7 @@ var rootCmd = &cobra.Command{ os.Exit(1) } if isLegacyFormat { - fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " + - "please use yaml/json/toml format instead!\n") + fmt.Printf("警告:INI文件格式在未来将会不受支持并且移除(反正目前HayFrps支持就行,这行别管)!\n") } } else { serverCfg.Complete() @@ -76,7 +79,7 @@ var rootCmd = &cobra.Command{ warning, err := validation.ValidateServerConfig(svrCfg) if warning != nil { - fmt.Printf("WARNING: %v\n", warning) + fmt.Printf("警告: %v\n", warning) } if err != nil { fmt.Println(err) @@ -100,18 +103,63 @@ func Execute() { func runServer(cfg *v1.ServerConfig) (err error) { log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor) + log.Infof("[HayFrp] 欢迎使用全新的HayFrp服务端!") + log.Infof("[HayFrp] 服务端版本:" + version.Full() + "!") + log.Infof("[HayFrp] 本新版本在客户端链接时将介入HayFrp终端!") + log.Infof("[HayFrp] 各种链接协议已升级到现代协议!") + + // 发起 GET 请求获取 API 返回的内容(节点名称) + resp, err := http.Get("https://api.hayfrp.org/NodeAPI?type=GetNodeName&token=" + cfg.ApiToken) + if err != nil { + return err + } + defer resp.Body.Close() + + // 读取 API 返回的内容 + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + // 将 API 返回的内容添加到 loginMsg.RunId 后面 + log.Infof("[HayFrp] 欢迎节点" + string(body) + "上线!") + log.Infof("[HayFrp] 自检所有必要项中,请稍等......") + log.Infof("[HayFrp] HayFrp API启用状态: %s", strconv.FormatBool(cfg.EnableApi)) + log.Infof("[HayFrp] HayFrp API URL: %s", cfg.ApiBaseUrl) + log.Infof("[HayFrp] HayFrp API Node Key: %s", cfg.ApiToken) if cfgFile != "" { - log.Infof("frps uses config file: %s", cfgFile) + log.Infof("[HayFrp] HayFrp服务端已引用配置文件: %s", cfgFile) } else { - log.Infof("frps uses command line arguments for config") + log.Infof("[HayFrp] HayFrp服务端已引用命令行参数") } svr, err := server.NewService(cfg) if err != nil { return err } - log.Infof("frps started successfully") + log.Infof("[HayFrp] HayFrp服务端已成功启动!") + go checkonline(cfg) svr.Run(context.Background()) return } + +func checkonline(cfg *v1.ServerConfig) { + time.Sleep(2 * time.Second) // 延时2秒,确保延时函数有足够的时间运行 + log.Infof("[HayFrp] 检测到所有端口已成功启动,请稍等......") + log.Infof("[HayFrp] 即将请求HayFrp API授权本节点调用其他节点检查本节点状态......") + log.Infof("[HayFrp] 检测节点在线状态中,这将会更新云端状态......") + // 发起 GET 请求获取 API 返回的内容(节点状态) + resp, err := http.Get("https://api.hayfrp.org/NodeAPI?type=checkonline&token=" + cfg.ApiToken) + if err != nil { + return + } + defer resp.Body.Close() + + // 读取 API 返回的内容 + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return + } + // 将 API 返回的内容添加到 loginMsg.RunId 后面 + log.Infof("[HayFrp] " + string(body)) +} diff --git a/extend/api/api.go b/extend/api/api.go new file mode 100644 index 00000000..a7b5d6d4 --- /dev/null +++ b/extend/api/api.go @@ -0,0 +1,249 @@ +package api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/fatedier/frp/pkg/msg" +) + +// Service sakurafrp api servie +type Service struct { + Host url.URL +} + +// NewService crate sakurafrp api servie +func NewService(host string) (s *Service, err error) { + u, err := url.Parse(host) + if err != nil { + return + } + return &Service{*u}, nil +} + +// CheckToken 校验客户端 token +func (s Service) CheckToken(user string, token string, timestamp int64, stk string) (ok bool, err error) { + values := url.Values{} + values.Set("action", "checktoken") + values.Set("user", user) + values.Set("token", token) + values.Set("timestamp", fmt.Sprintf("%d", timestamp)) + values.Set("apitoken", stk) + s.Host.RawQuery = values.Encode() + defer func(u *url.URL) { + u.RawQuery = "" + }(&s.Host) + resp, err := http.Get(s.Host.String()) + if err != nil { + return false, err + } + if resp.StatusCode != http.StatusOK { + return false, ErrHTTPStatus{ + Status: resp.StatusCode, + Text: resp.Status, + } + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return false, err + } + response := ResponseCheckToken{} + if err = json.Unmarshal(body, &response); err != nil { + return false, err + } + if !response.Success { + return false, ErrCheckTokenFail{response.Message} + } + return true, nil +} + +// CheckProxy 校验客户端代理 +func (s Service) CheckProxy(user string, pMsg *msg.NewProxy, timestamp int64, stk string) (ok bool, err error) { + + domains, err := json.Marshal(pMsg.CustomDomains) + if err != nil { + return false, err + } + + headers, err := json.Marshal(pMsg.Headers) + if err != nil { + return false, err + } + + locations, err := json.Marshal(pMsg.Locations) + if err != nil { + return false, err + } + + values := url.Values{} + + // API Basic + values.Set("action", "checkproxy") + values.Set("user", user) + values.Set("timestamp", fmt.Sprintf("%d", timestamp)) + values.Set("apitoken", stk) + + // Proxies basic info + values.Set("proxy_name", pMsg.ProxyName) + values.Set("proxy_type", pMsg.ProxyType) + values.Set("use_encryption", BoolToString(pMsg.UseEncryption)) + values.Set("use_compression", BoolToString(pMsg.UseCompression)) + + // Http Proxies + values.Set("domain", string(domains)) + values.Set("subdomain", pMsg.SubDomain) + + // Headers + values.Set("locations", string(locations)) + values.Set("http_user", pMsg.HTTPUser) + values.Set("http_pwd", pMsg.HTTPPwd) + values.Set("host_header_rewrite", pMsg.HostHeaderRewrite) + values.Set("headers", string(headers)) + + // Tcp & Udp & Stcp + values.Set("remote_port", strconv.Itoa(pMsg.RemotePort)) + + // Stcp & Xtcp + values.Set("sk", pMsg.Sk) + + // Load balance + values.Set("group", pMsg.Group) + values.Set("group_key", pMsg.GroupKey) + + s.Host.RawQuery = values.Encode() + defer func(u *url.URL) { + u.RawQuery = "" + }(&s.Host) + resp, err := http.Get(s.Host.String()) + if err != nil { + return false, err + } + if resp.StatusCode != http.StatusOK { + return false, ErrHTTPStatus{ + Status: resp.StatusCode, + Text: resp.Status, + } + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return false, err + } + response := ResponseCheckProxy{} + if err = json.Unmarshal(body, &response); err != nil { + return false, err + } + if !response.Success { + return false, ErrCheckProxyFail{response.Message} + } + return true, nil + +} + +// GetProxyLimit 获取隧道限速信息 +func (s Service) GetProxyLimit(user string, timestamp int64, stk string) (inLimit, outLimit uint64, err error) { + // 这部分就照之前的搬过去了,能跑就行x + values := url.Values{} + values.Set("action", "getlimit") + values.Set("user", user) + values.Set("timestamp", fmt.Sprintf("%d", timestamp)) + values.Set("apitoken", stk) + s.Host.RawQuery = values.Encode() + defer func(u *url.URL) { + u.RawQuery = "" + }(&s.Host) + resp, err := http.Get(s.Host.String()) + if err != nil { + return 0, 0, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return 0, 0, err + } + + er := &ErrHTTPStatus{} + if err = json.Unmarshal(body, er); err != nil { + return 0, 0, err + } + if er.Status != 200 { + return 0, 0, er + } + + response := &ResponseGetLimit{} + if err = json.Unmarshal(body, response); err != nil { + return 0, 0, err + } + + // 这里直接返回 uint64 应该问题不大 + return response.MaxIn, response.MaxOut, nil + +} + +func BoolToString(val bool) (str string) { + if val { + return "true" + } + return "false" + +} + +type ErrHTTPStatus struct { + Status int `json:"status"` + Text string `json:"message"` +} + +func (e ErrHTTPStatus) Error() string { + t := time.Now() + layout := "2006-01-02 15:04:05" + str := t.Format(layout) + switch e.Status { + case 403: + return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:465] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:470] [HayFrp] 根据状态码判定,问题为隧道被禁止或者无权启用隧道\n"+str+" [W] [hayfrp.go:475] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:480] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:485] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:490] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text) + case 404: + return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:466] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:471] [HayFrp] 根据状态码判定,问题为没有找到隧道或者API出现故障\n"+str+" [W] [hayfrp.go:476] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:481] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:486] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:491] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text) + case 520: + return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:467] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:472] [HayFrp] 根据状态码判定,问题为API网关出现故障\n"+str+" [W] [hayfrp.go] [HayFrp:477] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:482] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:487] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:492] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text) + case 503: + return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:468] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:473] [HayFrp] 根据状态码判定,问题为API请求了过大进行限流\n"+str+" [W] [hayfrp.go:478] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:483] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:488] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:493] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text) + default: + return fmt.Sprintf("HayFrpAPI return error.\n"+str+" [E] [api/hayfrp.go:469] [HayFrp] API返回错误,状态码:%d 文本:%s\n"+str+" [E] [hayfrp.go:474] [HayFrp] 目前HayFrp无法状态码判定问题......\n"+str+" [W] [hayfrp.go:479] [HayFrp] 客户端可能会在20s后重新尝试链接......\n"+str+" [W] [hayfrp.go:484] [HayFrp] 如果无法链接,请到面板检查隧道状态\n"+str+" [W] [hayfrp.go:489] [HayFrp] 若仍然出现问题,请截图至QQ群或和谐论坛反馈\n"+str+" [W] [hayfrp.go:494] [HayFrp] 注意,截图时请为Token打码,否则有泄露风险!", e.Status, e.Text) + } +} + +type ResponseGetLimit struct { + MaxIn uint64 `json:"max-in"` + MaxOut uint64 `json:"max-out"` +} + +type ResponseCheckToken struct { + Success bool `json:"success"` + Message string `json:"message"` +} + +type ResponseCheckProxy struct { + Success bool `json:"success"` + Message string `json:"message"` +} + +type ErrCheckTokenFail struct { + Message string +} + +type ErrCheckProxyFail struct { + Message string +} + +func (e ErrCheckTokenFail) Error() string { + return e.Message +} + +func (e ErrCheckProxyFail) Error() string { + return e.Message +} diff --git a/extend/cumu/cumu.go b/extend/cumu/cumu.go new file mode 100644 index 00000000..aa469f25 --- /dev/null +++ b/extend/cumu/cumu.go @@ -0,0 +1,66 @@ +package cumu + +import ( + "net" + "sync" +) + +// Conn 速度累计 +type Conn struct { + net.Conn + + inCount int64 + outCount int64 + + inCountLock sync.Mutex + outCountLock sync.Mutex +} + +// NewCumuConn ... +func NewCumuConn(conn net.Conn) *Conn { + return &Conn{ + Conn: conn, + inCount: 0, + outCount: 0, + } +} + +func (c *Conn) Read(p []byte) (n int, err error) { + n, err = c.Conn.Read(p) + if err != nil { + return + } + c.outCountLock.Lock() + defer c.outCountLock.Unlock() + c.outCount += int64(n) + return +} + +func (c *Conn) Write(p []byte) (n int, err error) { + n, err = c.Conn.Write(p) + if err != nil { + return + } + c.inCountLock.Lock() + defer c.inCountLock.Unlock() + c.inCount += int64(n) + return +} + +// InCount get in bound byte count +func (c *Conn) InCount() int64 { + c.inCountLock.Lock() + defer c.inCountLock.Unlock() + in := c.inCount + c.inCount = 0 + return in +} + +// OutCount get out bound byte count +func (c *Conn) OutCount() int64 { + c.outCountLock.Lock() + defer c.outCountLock.Unlock() + out := c.outCount + c.outCount = 0 + return out +} diff --git a/extend/limit/limit.go b/extend/limit/limit.go new file mode 100644 index 00000000..d5f93592 --- /dev/null +++ b/extend/limit/limit.go @@ -0,0 +1,43 @@ +package limit + +import ( + "io" + "net" +) + +const ( + B uint64 = 1 << (10 * (iota)) + KB + MB + GB + TB + PB + EB +) + +const burstLimit = 1024 * 1024 * 1024 + +type LimitConn struct { + net.Conn + + lr io.Reader + lw io.Writer +} + +func NewLimitConn(maxread, maxwrite uint64, c net.Conn) LimitConn { + // 这里不知道为什么要 49 才能对的上真实速度 + // 49 是根据 wget 速度来取的,测试了 512、1024、2048、4096、8192 等多种速度下都很准确 + return LimitConn{ + lr: NewReaderWithLimit(c, maxread*49), + lw: NewWriterWithLimit(c, maxwrite*49), + Conn: c, + } +} + +func (c LimitConn) Read(p []byte) (n int, err error) { + return c.lr.Read(p) +} + +func (c LimitConn) Write(p []byte) (n int, err error) { + return c.lw.Write(p) +} diff --git a/extend/limit/reader.go b/extend/limit/reader.go new file mode 100644 index 00000000..cac8fb30 --- /dev/null +++ b/extend/limit/reader.go @@ -0,0 +1,63 @@ +package limit + +import ( + "context" + "io" + "sync" + "time" + + "golang.org/x/time/rate" +) + +type Reader struct { + r io.Reader + limiter *rate.Limiter + ctx context.Context + mux sync.Mutex +} + +// NewReader returns a reader that implements io.Reader with rate limiting. +func NewReader(r io.Reader) *Reader { + return &Reader{ + r: r, + ctx: context.Background(), + mux: sync.Mutex{}, + } +} + +func NewReaderWithLimit(r io.Reader, speed uint64) *Reader { + rr := &Reader{ + r: r, + ctx: context.Background(), + mux: sync.Mutex{}, + } + rr.SetRateLimit(speed) + return rr +} + +// SetRateLimit sets rate limit (bytes/sec) to the reader. +func (s *Reader) SetRateLimit(bytesPerSec uint64) { + s.mux.Lock() + defer s.mux.Unlock() + + s.limiter = rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit) + s.limiter.AllowN(time.Now(), burstLimit) // spend initial burst +} + +// Read reads bytes into p. +func (s *Reader) Read(p []byte) (int, error) { + s.mux.Lock() + defer s.mux.Unlock() + + if s.limiter == nil { + return s.r.Read(p) + } + n, err := s.r.Read(p) + if err != nil { + return n, err + } + if err := s.limiter.WaitN(s.ctx, n); err != nil { + return n, err + } + return n, nil +} diff --git a/extend/limit/w_test.go b/extend/limit/w_test.go new file mode 100644 index 00000000..88c22c9a --- /dev/null +++ b/extend/limit/w_test.go @@ -0,0 +1,17 @@ +package limit + +import ( + "fmt" + "net/http" + "testing" +) + +func TestHttp(t *testing.T) { + http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { + lw := NewWriterWithLimit(w, 10*KB) + for { + fmt.Fprintf(lw, "x") + } + }) + http.ListenAndServe(":62542", nil) +} diff --git a/extend/limit/writer.go b/extend/limit/writer.go new file mode 100644 index 00000000..628e2233 --- /dev/null +++ b/extend/limit/writer.go @@ -0,0 +1,63 @@ +package limit + +import ( + "context" + "io" + "sync" + "time" + + "golang.org/x/time/rate" +) + +type Writer struct { + w io.Writer + limiter *rate.Limiter + ctx context.Context + mux sync.Mutex +} + +// NewWriter returns a writer that implements io.Writer with rate limiting. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + ctx: context.Background(), + mux: sync.Mutex{}, + } +} + +func NewWriterWithLimit(w io.Writer, speed uint64) *Writer { + ww := &Writer{ + w: w, + ctx: context.Background(), + mux: sync.Mutex{}, + } + ww.SetRateLimit(speed) + return ww +} + +// SetRateLimit sets rate limit (bytes/sec) to the writer. +func (s *Writer) SetRateLimit(bytesPerSec uint64) { + s.mux.Lock() + defer s.mux.Unlock() + + s.limiter = rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit) + s.limiter.AllowN(time.Now(), burstLimit) // spend initial burst +} + +// Write writes bytes from p. +func (s *Writer) Write(p []byte) (int, error) { + s.mux.Lock() + defer s.mux.Unlock() + + if s.limiter == nil { + return s.w.Write(p) + } + n, err := s.w.Write(p) + if err != nil { + return n, err + } + if err := s.limiter.WaitN(s.ctx, n); err != nil { + return n, err + } + return n, err +} diff --git a/g/g.go b/g/g.go new file mode 100644 index 00000000..d2a69ddf --- /dev/null +++ b/g/g.go @@ -0,0 +1,32 @@ +package g + +import ( + config "github.com/fatedier/frp/pkg/config/legacy" +) + +var ( + GlbClientCfg *ClientCfg + GlbServerCfg *ServerCfg +) + +func init() { + GlbClientCfg = &ClientCfg{ + ClientCommonConf: config.GetDefaultClientConf(), + } + GlbServerCfg = &ServerCfg{ + ServerCommonConf: config.GetDefaultServerConf(), + } +} + +type ClientCfg struct { + config.ClientCommonConf + + CfgFile string + ServerUdpPort int // this is configured by login response from frps +} + +type ServerCfg struct { + config.ServerCommonConf + + CfgFile string +} diff --git a/go.mod b/go.mod index 72bbc0ac..8967ce06 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/logr v1.4.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -60,11 +61,15 @@ require ( github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/templexxx/cpu v0.1.0 // indirect github.com/templexxx/xorsimd v0.4.2 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/mod v0.14.0 // indirect diff --git a/go.sum b/go.sum index efcb18ee..e868c361 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWq github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -121,6 +123,8 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -148,11 +152,17 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/xtaci/kcp-go/v5 v5.6.8 h1:jlI/0jAyjoOjT/SaGB58s4bQMJiNS41A2RKzR6TMWeI= github.com/xtaci/kcp-go/v5 v5.6.8/go.mod h1:oE9j2NVqAkuKO5o8ByKGch3vgVX3BNf8zqP8JiGq0bM= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -201,6 +211,7 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/package.sh b/package.sh index df36108e..5cc29397 100755 --- a/package.sh +++ b/package.sh @@ -8,7 +8,7 @@ if [ $? -ne 0 ]; then exit 1 fi -frp_version=`./bin/frps --version` +frp_version="0.59.0-HAYFRP" echo "build version: $frp_version" # cross_compiles diff --git a/pkg/config/legacy/conversion.go b/pkg/config/legacy/conversion.go index dd8c4a11..670c5d6e 100644 --- a/pkg/config/legacy/conversion.go +++ b/pkg/config/legacy/conversion.go @@ -153,6 +153,9 @@ func Convert_ServerCommonConf_To_v1(conf *ServerCommonConf) *v1.ServerConfig { out.Transport.TLS.TrustedCaFile = conf.TLSTrustedCaFile out.MaxPortsPerClient = conf.MaxPortsPerClient + out.ApiBaseUrl = conf.ApiBaseUrl + out.ApiToken = conf.ApiToken + out.EnableApi = conf.EnableApi for _, v := range conf.HTTPPlugins { out.HTTPPlugins = append(out.HTTPPlugins, v1.HTTPPluginOptions{ diff --git a/pkg/config/legacy/server.go b/pkg/config/legacy/server.go index c58f76ad..0f5c323f 100644 --- a/pkg/config/legacy/server.go +++ b/pkg/config/legacy/server.go @@ -198,6 +198,10 @@ type ServerCommonConf struct { PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"` // NatHoleAnalysisDataReserveHours specifies the hours to reserve nat hole analysis data. NatHoleAnalysisDataReserveHours int64 `ini:"nat_hole_analysis_data_reserve_hours" json:"nat_hole_analysis_data_reserve_hours"` + + EnableApi bool `ini:"api_enable" json:"api_enable"` + ApiBaseUrl string `ini:"api_baseurl" json:"api_baseurl"` + ApiToken string `ini:"api_token" json:"api_token"` } // GetDefaultServerConf returns a server configuration with reasonable defaults. @@ -214,6 +218,9 @@ func GetDefaultServerConf() ServerCommonConf { TCPMux: true, AllowPorts: make(map[int]struct{}), HTTPPlugins: make(map[string]HTTPPluginOptions), + EnableApi: true, + ApiBaseUrl: "https://api.hayfrp.org/NodeAPI", + ApiToken: "UNSET|0", } } @@ -246,6 +253,11 @@ func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) { common.AllowPortsStr = allowPortStr } + // API + common.ApiToken = s.Key("api_token").String() + common.ApiBaseUrl = s.Key("api_baseurl").String() + common.EnableApi = s.Key("api_enable").MustBool(true) + // plugin.xxx pluginOpts := make(map[string]HTTPPluginOptions) for _, section := range f.Sections() { diff --git a/pkg/config/v1/server.go b/pkg/config/v1/server.go index 3108cd34..ce4865c1 100644 --- a/pkg/config/v1/server.go +++ b/pkg/config/v1/server.go @@ -93,6 +93,10 @@ type ServerConfig struct { // NatHoleAnalysisDataReserveHours specifies the hours to reserve nat hole analysis data. NatHoleAnalysisDataReserveHours int64 `json:"natholeAnalysisDataReserveHours,omitempty"` + EnableApi bool `json:"api_enable,omitempty"` + ApiBaseUrl string `json:"api_baseurl,omitempty"` + ApiToken string `json:"api_token,omitempty"` + AllowPorts []types.PortsRange `json:"allowPorts,omitempty"` HTTPPlugins []HTTPPluginOptions `json:"httpPlugins,omitempty"` @@ -114,6 +118,10 @@ func (c *ServerConfig) Complete() { if c.WebServer.Port > 0 { c.WebServer.Addr = util.EmptyOr(c.WebServer.Addr, "0.0.0.0") } + // 添加对 EnableApi, ApiBaseUrl 和 ApiToken 的处理 + c.EnableApi = util.EmptyOr(c.EnableApi, false) + c.ApiBaseUrl = util.EmptyOr(c.ApiBaseUrl, "") + c.ApiToken = util.EmptyOr(c.ApiToken, "") c.VhostHTTPTimeout = util.EmptyOr(c.VhostHTTPTimeout, 60) c.DetailedErrorsToClient = util.EmptyOr(c.DetailedErrorsToClient, lo.ToPtr(true)) diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go new file mode 100644 index 00000000..9bf5880b --- /dev/null +++ b/pkg/consts/consts.go @@ -0,0 +1,32 @@ +// Copyright 2016 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 consts + +var ( + // proxy status + Idle string = "idle" + Working string = "working" + Closed string = "closed" + Online string = "online" + Offline string = "offline" + + // proxy type + TcpProxy string = "tcp" + UdpProxy string = "udp" + HttpProxy string = "http" + HttpsProxy string = "https" + StcpProxy string = "stcp" + XtcpProxy string = "xtcp" +) diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go index 561a52e7..64084ed9 100644 --- a/pkg/util/version/version.go +++ b/pkg/util/version/version.go @@ -14,7 +14,7 @@ package version -var version = "0.59.0" +var version = "0.59.0-HAYFRP" func Full() string { return version diff --git a/pkg/util/vhost/resource.go b/pkg/util/vhost/resource.go index a65e2997..64a826c3 100644 --- a/pkg/util/vhost/resource.go +++ b/pkg/util/vhost/resource.go @@ -29,23 +29,53 @@ var NotFoundPagePath = "" const ( NotFound = ` -
-Sorry, the page you are looking for is currently unavailable.
-Please try again later.
The server is powered by frp.
-Faithfully yours, frp.
- + +您访问的网站或服务暂时不可用
+如果您是隧道所有者,造成无法访问的原因可能有:
+如果您是普通访问者,您可以:
+Powered by HayFrp | Based on HayFrp Server
+