This commit is contained in:
OoyonghongoO 2024-10-05 10:25:11 +08:00
parent b4d5d8c756
commit 84d34c8298
28 changed files with 1631 additions and 150 deletions

View File

@ -163,9 +163,9 @@ func (ctl *Control) handleNewProxyResp(m msg.Message) {
// Start a new proxy handler if no error got // Start a new proxy handler if no error got
err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error) err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error)
if err != nil { if err != nil {
xl.Warnf("[%s] start error: %v", inMsg.ProxyName, err) xl.Warnf("[%s] 启动时发生错误: %v", inMsg.ProxyName, err)
} else { } else {
xl.Infof("[%s] start proxy success", inMsg.ProxyName) xl.Infof("[%s] 隧道启动成功!", inMsg.ProxyName)
} }
} }

View File

@ -205,7 +205,7 @@ func (svr *Service) keepControllerWorking() {
svr.loopLoginUntilSuccess(20*time.Second, false) svr.loopLoginUntilSuccess(20*time.Second, false)
if svr.ctl != nil { if svr.ctl != nil {
<-svr.ctl.Done() <-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. // If the control is nil, it means that the login failed and the service is also closed.
return false, nil return false, nil
@ -281,9 +281,9 @@ func (svr *Service) login() (conn net.Conn, connector Connector, err error) {
} }
svr.runID = loginRespMsg.RunID 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 return
} }
@ -291,10 +291,10 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
xl := xlog.FromContextSafe(svr.ctx) xl := xlog.FromContextSafe(svr.ctx)
loginFunc := func() (bool, error) { loginFunc := func() (bool, error) {
xl.Infof("try to connect to server...") xl.Infof("尝试连接到服务器...")
conn, connector, err := svr.login() conn, connector, err := svr.login()
if err != nil { if err != nil {
xl.Warnf("connect to server error: %v", err) xl.Warnf("连接到服务器时发生错误: %v", err)
if firstLoginExit { if firstLoginExit {
svr.cancel(cancelErr{Err: err}) svr.cancel(cancelErr{Err: err})
} }
@ -320,7 +320,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
ctl, err := NewControl(svr.ctx, sessionCtx) ctl, err := NewControl(svr.ctx, sessionCtx)
if err != nil { if err != nil {
conn.Close() conn.Close()
xl.Errorf("NewControl error: %v", err) xl.Errorf("新控件出现错误: %v", err)
return false, err return false, err
} }
ctl.SetInWorkConnCallback(svr.handleWorkConnCb) ctl.SetInWorkConnCallback(svr.handleWorkConnCb)

View File

@ -56,7 +56,7 @@ func (sv *STCPVisitor) worker() {
for { for {
conn, err := sv.l.Accept() conn, err := sv.l.Accept()
if err != nil { if err != nil {
xl.Warnf("stcp local listener closed") xl.Warnf("STCP本地监听关闭")
return return
} }
go sv.handleConn(conn) go sv.handleConn(conn)
@ -68,7 +68,7 @@ func (sv *STCPVisitor) internalConnWorker() {
for { for {
conn, err := sv.internalLn.Accept() conn, err := sv.internalLn.Accept()
if err != nil { if err != nil {
xl.Warnf("stcp internal listener closed") xl.Warnf("STCP互联网监听关闭")
return return
} }
go sv.handleConn(conn) go sv.handleConn(conn)
@ -79,7 +79,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx) xl := xlog.FromContextSafe(sv.ctx)
defer userConn.Close() defer userConn.Close()
xl.Debugf("get a new stcp user connection") xl.Debugf("获取到一个新的STCP用户链接.")
visitorConn, err := sv.helper.ConnectServer() visitorConn, err := sv.helper.ConnectServer()
if err != nil { if err != nil {
return return
@ -97,7 +97,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
} }
err = msg.WriteMsg(visitorConn, newVisitorConnMsg) err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil { if err != nil {
xl.Warnf("send newVisitorConnMsg to server error: %v", err) xl.Warnf("发生新参与者链接信息到服务器时发生错误: %v", err)
return return
} }
@ -105,13 +105,13 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
_ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second)) _ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg) err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil { if err != nil {
xl.Warnf("get newVisitorConnRespMsg error: %v", err) xl.Warnf("获取新参与者链接信息时发生错误: %v", err)
return return
} }
_ = visitorConn.SetReadDeadline(time.Time{}) _ = visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" { if newVisitorConnRespMsg.Error != "" {
xl.Warnf("start new visitor connection error: %s", newVisitorConnRespMsg.Error) xl.Warnf("启动一个新的参与者链接时发生错误: %s", newVisitorConnRespMsg.Error)
return return
} }
@ -120,7 +120,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
if sv.cfg.Transport.UseEncryption { if sv.cfg.Transport.UseEncryption {
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey)) remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey))
if err != nil { if err != nil {
xl.Errorf("create encryption stream error: %v", err) xl.Errorf("创建流线型加密连接时出现错误: %v", err)
return return
} }
} }

View File

@ -79,14 +79,14 @@ func (vm *Manager) keepVisitorsRunning() {
for { for {
select { select {
case <-vm.stopCh: case <-vm.stopCh:
xl.Tracef("gracefully shutdown visitor manager") xl.Tracef("优雅地关闭访客管理器(?)")
return return
case <-ticker.C: case <-ticker.C:
vm.mu.Lock() vm.mu.Lock()
for _, cfg := range vm.cfgs { for _, cfg := range vm.cfgs {
name := cfg.GetBaseConfig().Name name := cfg.GetBaseConfig().Name
if _, exist := vm.visitors[name]; !exist { if _, exist := vm.visitors[name]; !exist {
xl.Infof("try to start visitor [%s]", name) xl.Infof("尝试加入发起者 [%s]", name)
_ = vm.startVisitor(cfg) _ = 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) visitor := NewVisitor(vm.ctx, cfg, vm.clientCfg, vm.helper)
err = visitor.Run() err = visitor.Run()
if err != nil { if err != nil {
xl.Warnf("start error: %v", err) xl.Warnf("启动错误: %v", err)
} else { } else {
vm.visitors[name] = visitor vm.visitors[name] = visitor
xl.Infof("start visitor success") xl.Infof("参与者服务启动成功")
} }
return return
} }
@ -156,7 +156,7 @@ func (vm *Manager) UpdateAll(cfgs []v1.VisitorConfigurer) {
} }
} }
if len(delNames) > 0 { if len(delNames) > 0 {
xl.Infof("visitor removed: %v", delNames) xl.Infof("参与者已移除: %v", delNames)
} }
addNames := make([]string, 0) addNames := make([]string, 0)
@ -169,7 +169,7 @@ func (vm *Manager) UpdateAll(cfgs []v1.VisitorConfigurer) {
} }
} }
if len(addNames) > 0 { 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() defer vm.mu.RUnlock()
v, ok := vm.visitors[name] v, ok := vm.visitors[name]
if !ok { if !ok {
return fmt.Errorf("visitor [%s] not found", name) return fmt.Errorf("发起者 [%s] 未找到", name)
} }
return v.AcceptConn(conn) return v.AcceptConn(conn)
} }

View File

@ -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 package main
import ( 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/assets/frpc"
"github.com/fatedier/frp/cmd/frpc/sub" "github.com/fatedier/frp/cmd/frpc/sub"
"github.com/fatedier/frp/pkg/util/system" "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() { 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() system.EnableCompatibilityMode()
sub.Execute() sub.Execute()
} }

View File

@ -47,7 +47,7 @@ func init() {
for _, typ := range proxyTypes { for _, typ := range proxyTypes {
c := v1.NewProxyConfigurerByType(typ) c := v1.NewProxyConfigurerByType(typ)
if c == nil { if c == nil {
panic("proxy type: " + typ + " not support") panic("隧道类型: " + typ + " 不支持")
} }
clientCfg := v1.ClientCommonConfig{} clientCfg := v1.ClientCommonConfig{}
cmd := NewProxyCommand(string(typ), c, &clientCfg) cmd := NewProxyCommand(string(typ), c, &clientCfg)
@ -58,7 +58,7 @@ func init() {
if slices.Contains(visitorTypes, v1.VisitorType(typ)) { if slices.Contains(visitorTypes, v1.VisitorType(typ)) {
vc := v1.NewVisitorConfigurerByType(v1.VisitorType(typ)) vc := v1.NewVisitorConfigurerByType(v1.VisitorType(typ))
if vc == nil { if vc == nil {
panic("visitor type: " + typ + " not support") panic("参与者类型: " + typ + " 不支持")
} }
visitorCmd := NewVisitorCommand(string(typ), vc, &clientCfg) visitorCmd := NewVisitorCommand(string(typ), vc, &clientCfg)
config.RegisterVisitorFlags(visitorCmd, vc) config.RegisterVisitorFlags(visitorCmd, vc)
@ -71,7 +71,7 @@ func init() {
func NewProxyCommand(name string, c v1.ProxyConfigurer, clientCfg *v1.ClientCommonConfig) *cobra.Command { func NewProxyCommand(name string, c v1.ProxyConfigurer, clientCfg *v1.ClientCommonConfig) *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: name, 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) { Run: func(cmd *cobra.Command, args []string) {
clientCfg.Complete() clientCfg.Complete()
if _, err := validation.ValidateClientCommonConfig(clientCfg); err != nil { 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 { func NewVisitorCommand(name string, c v1.VisitorConfigurer, clientCfg *v1.ClientCommonConfig) *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "visitor", 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) { Run: func(cmd *cobra.Command, args []string) {
clientCfg.Complete() clientCfg.Complete()
if _, err := validation.ValidateClientCommonConfig(clientCfg); err != nil { if _, err := validation.ValidateClientCommonConfig(clientCfg); err != nil {

View File

@ -87,7 +87,7 @@ func runMultipleClients(cfgDir string) error {
defer wg.Done() defer wg.Done()
err := runClient(path) err := runClient(path)
if err != nil { if err != nil {
fmt.Printf("frpc service error for config file [%s]\n", path) fmt.Printf("Frpc发生错误在配置文件 [%s]\n", path)
} }
}() }()
return nil return nil
@ -116,13 +116,12 @@ func runClient(cfgFilePath string) error {
return err return err
} }
if isLegacyFormat { if isLegacyFormat {
fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " + fmt.Printf("警告INI文件格式在未来将会不受支持并且移除\n")
"please use yaml/json/toml format instead!\n")
} }
warning, err := validation.ValidateAllClientConfig(cfg, proxyCfgs, visitorCfgs) warning, err := validation.ValidateAllClientConfig(cfg, proxyCfgs, visitorCfgs)
if warning != nil { if warning != nil {
fmt.Printf("WARNING: %v\n", warning) fmt.Printf("警告: %v\n", warning)
} }
if err != nil { if err != nil {
return err return err
@ -139,8 +138,8 @@ func startService(
log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor) log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor)
if cfgFile != "" { if cfgFile != "" {
log.Infof("start frpc service for config file [%s]", cfgFile) log.Infof("启动Frpc配置文件 [%s]", cfgFile)
defer log.Infof("frpc service for config file [%s] stopped", cfgFile) defer log.Infof("Frpc配置文件 [%s] 已停止", cfgFile)
} }
svr, err := client.NewService(client.ServiceOptions{ svr, err := client.NewService(client.ServiceOptions{
Common: cfg, Common: cfg,

View File

@ -15,12 +15,40 @@
package main package main
import ( import (
"fmt"
"os"
_ "github.com/fatedier/frp/assets/frps" _ "github.com/fatedier/frp/assets/frps"
_ "github.com/fatedier/frp/pkg/metrics" _ "github.com/fatedier/frp/pkg/metrics"
"github.com/fatedier/frp/pkg/util/system" "github.com/fatedier/frp/pkg/util/system"
"github.com/fatedier/frp/pkg/util/version"
) )
func main() { func main() {
fmt.Println(`
__ __ ________
| \ | \ | \
| $$ | $$ ______ __ __ | $$$$$$$$______ ______
| $$__| $$ | \ | \ | \| $$__ / \ / \
| $$ $$ \$$$$$$\| $$ | $$| $$ \ | $$$$$$\| $$$$$$\
| $$$$$$$$ / $$| $$ | $$| $$$$$ | $$ \$$| $$ | $$
| $$ | $$| $$$$$$$| $$__/ $$| $$ | $$ | $$__/ $$
| $$ | $$ \$$ $$ \$$ $$| $$ | $$ | $$ $$
\$$ \$$ \$$$$$$$ _\$$$$$$$ \$$ \$$ | $$$$$$$
| \__| $$ | $$
\$$ $$ | $$
\$$$$$$ \$$ HayFrp公益项目运营&开发组
`)
fmt.Println("欢迎使用HayFrp")
fmt.Println("HayFrp程序发行版本" + version.Full())
system.EnableCompatibilityMode() 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() Execute()
} }

View File

@ -17,7 +17,11 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"io/ioutil"
"net/http"
"os" "os"
"strconv"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -66,8 +70,7 @@ var rootCmd = &cobra.Command{
os.Exit(1) os.Exit(1)
} }
if isLegacyFormat { if isLegacyFormat {
fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " + fmt.Printf("警告INI文件格式在未来将会不受支持并且移除反正目前HayFrps支持就行这行别管!\n")
"please use yaml/json/toml format instead!\n")
} }
} else { } else {
serverCfg.Complete() serverCfg.Complete()
@ -76,7 +79,7 @@ var rootCmd = &cobra.Command{
warning, err := validation.ValidateServerConfig(svrCfg) warning, err := validation.ValidateServerConfig(svrCfg)
if warning != nil { if warning != nil {
fmt.Printf("WARNING: %v\n", warning) fmt.Printf("警告: %v\n", warning)
} }
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -100,18 +103,63 @@ func Execute() {
func runServer(cfg *v1.ServerConfig) (err error) { func runServer(cfg *v1.ServerConfig) (err error) {
log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor) 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 != "" { if cfgFile != "" {
log.Infof("frps uses config file: %s", cfgFile) log.Infof("[HayFrp] HayFrp服务端已引用配置文件: %s", cfgFile)
} else { } else {
log.Infof("frps uses command line arguments for config") log.Infof("[HayFrp] HayFrp服务端已引用命令行参数")
} }
svr, err := server.NewService(cfg) svr, err := server.NewService(cfg)
if err != nil { if err != nil {
return err return err
} }
log.Infof("frps started successfully") log.Infof("[HayFrp] HayFrp服务端已成功启动")
go checkonline(cfg)
svr.Run(context.Background()) svr.Run(context.Background())
return 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))
}

249
extend/api/api.go Normal file
View File

@ -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
}

66
extend/cumu/cumu.go Normal file
View File

@ -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
}

43
extend/limit/limit.go Normal file
View File

@ -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)
}

63
extend/limit/reader.go Normal file
View File

@ -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
}

17
extend/limit/w_test.go Normal file
View File

@ -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)
}

63
extend/limit/writer.go Normal file
View File

@ -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
}

32
g/g.go Normal file
View File

@ -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
}

5
go.mod
View File

@ -41,6 +41,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.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-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/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.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/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect
github.com/rogpeppe/go-internal v1.11.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/cpu v0.1.0 // indirect
github.com/templexxx/xorsimd v0.4.2 // indirect github.com/templexxx/xorsimd v0.4.2 // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect
github.com/tjfoc/gmsm v1.4.1 // 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 go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/mod v0.14.0 // indirect

11
go.sum
View File

@ -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-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 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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= 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/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 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= 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 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 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/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 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= 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 h1:jlI/0jAyjoOjT/SaGB58s4bQMJiNS41A2RKzR6TMWeI=
github.com/xtaci/kcp-go/v5 v5.6.8/go.mod h1:oE9j2NVqAkuKO5o8ByKGch3vgVX3BNf8zqP8JiGq0bM= 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 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= 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/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 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 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= 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-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-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-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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -8,7 +8,7 @@ if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
frp_version=`./bin/frps --version` frp_version="0.59.0-HAYFRP"
echo "build version: $frp_version" echo "build version: $frp_version"
# cross_compiles # cross_compiles

View File

@ -153,6 +153,9 @@ func Convert_ServerCommonConf_To_v1(conf *ServerCommonConf) *v1.ServerConfig {
out.Transport.TLS.TrustedCaFile = conf.TLSTrustedCaFile out.Transport.TLS.TrustedCaFile = conf.TLSTrustedCaFile
out.MaxPortsPerClient = conf.MaxPortsPerClient out.MaxPortsPerClient = conf.MaxPortsPerClient
out.ApiBaseUrl = conf.ApiBaseUrl
out.ApiToken = conf.ApiToken
out.EnableApi = conf.EnableApi
for _, v := range conf.HTTPPlugins { for _, v := range conf.HTTPPlugins {
out.HTTPPlugins = append(out.HTTPPlugins, v1.HTTPPluginOptions{ out.HTTPPlugins = append(out.HTTPPlugins, v1.HTTPPluginOptions{

View File

@ -198,6 +198,10 @@ type ServerCommonConf struct {
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"` PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
// NatHoleAnalysisDataReserveHours specifies the hours to reserve nat hole analysis data. // 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"` 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. // GetDefaultServerConf returns a server configuration with reasonable defaults.
@ -214,6 +218,9 @@ func GetDefaultServerConf() ServerCommonConf {
TCPMux: true, TCPMux: true,
AllowPorts: make(map[int]struct{}), AllowPorts: make(map[int]struct{}),
HTTPPlugins: make(map[string]HTTPPluginOptions), 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 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 // plugin.xxx
pluginOpts := make(map[string]HTTPPluginOptions) pluginOpts := make(map[string]HTTPPluginOptions)
for _, section := range f.Sections() { for _, section := range f.Sections() {

View File

@ -93,6 +93,10 @@ type ServerConfig struct {
// NatHoleAnalysisDataReserveHours specifies the hours to reserve nat hole analysis data. // NatHoleAnalysisDataReserveHours specifies the hours to reserve nat hole analysis data.
NatHoleAnalysisDataReserveHours int64 `json:"natholeAnalysisDataReserveHours,omitempty"` 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"` AllowPorts []types.PortsRange `json:"allowPorts,omitempty"`
HTTPPlugins []HTTPPluginOptions `json:"httpPlugins,omitempty"` HTTPPlugins []HTTPPluginOptions `json:"httpPlugins,omitempty"`
@ -114,6 +118,10 @@ func (c *ServerConfig) Complete() {
if c.WebServer.Port > 0 { if c.WebServer.Port > 0 {
c.WebServer.Addr = util.EmptyOr(c.WebServer.Addr, "0.0.0.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.VhostHTTPTimeout = util.EmptyOr(c.VhostHTTPTimeout, 60)
c.DetailedErrorsToClient = util.EmptyOr(c.DetailedErrorsToClient, lo.ToPtr(true)) c.DetailedErrorsToClient = util.EmptyOr(c.DetailedErrorsToClient, lo.ToPtr(true))

32
pkg/consts/consts.go Normal file
View File

@ -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"
)

View File

@ -14,7 +14,7 @@
package version package version
var version = "0.59.0" var version = "0.59.0-HAYFRP"
func Full() string { func Full() string {
return version return version

File diff suppressed because one or more lines are too long

View File

@ -19,12 +19,11 @@ import (
"fmt" "fmt"
"net" "net"
"runtime/debug" "runtime/debug"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/samber/lo"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
@ -40,6 +39,10 @@ import (
"github.com/fatedier/frp/server/controller" "github.com/fatedier/frp/server/controller"
"github.com/fatedier/frp/server/metrics" "github.com/fatedier/frp/server/metrics"
"github.com/fatedier/frp/server/proxy" "github.com/fatedier/frp/server/proxy"
"github.com/fatedier/golib/control/shutdown"
"github.com/samber/lo"
"github.com/fatedier/frp/extend/api"
) )
type ControlManager struct { type ControlManager struct {
@ -84,6 +87,20 @@ func (cm *ControlManager) GetByID(runID string) (ctl *Control, ok bool) {
return return
} }
func (cm *ControlManager) SearchByID(runId string) (ctl *Control, ok bool) {
cm.mu.RLock()
defer cm.mu.RUnlock()
for k, v := range cm.ctlsByRunID {
if strings.IndexAny(k, runId+"-") > -1 {
if v == nil {
return
}
ctl, ok = cm.ctlsByRunID[k]
}
}
return
}
func (cm *ControlManager) Close() error { func (cm *ControlManager) Close() error {
cm.mu.Lock() cm.mu.Lock()
defer cm.mu.Unlock() defer cm.mu.Unlock()
@ -140,8 +157,18 @@ type Control struct {
// replace old controller instantly. // replace old controller instantly.
runID string runID string
// control statusAPI
status string
readerShutdown *shutdown.Shutdown
writerShutdown *shutdown.Shutdown
managerShutdown *shutdown.Shutdown
allShutdown *shutdown.Shutdown
mu sync.RWMutex mu sync.RWMutex
inLimit uint64
outLimit uint64
// Server configuration information // Server configuration information
serverCfg *v1.ServerConfig serverCfg *v1.ServerConfig
@ -224,7 +251,7 @@ func (ctl *Control) Close() error {
func (ctl *Control) Replaced(newCtl *Control) { func (ctl *Control) Replaced(newCtl *Control) {
xl := ctl.xl xl := ctl.xl
xl.Infof("Replaced by client [%s]", newCtl.runID) xl.Infof("由客户端更改 [%s]", newCtl.runID)
ctl.runID = "" ctl.runID = ""
ctl.conn.Close() ctl.conn.Close()
} }
@ -233,18 +260,18 @@ func (ctl *Control) RegisterWorkConn(conn net.Conn) error {
xl := ctl.xl xl := ctl.xl
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
xl.Errorf("panic error: %v", err) xl.Errorf("[HayFrp] 致命错误Panic爆了: %v", err)
xl.Errorf(string(debug.Stack())) xl.Errorf(string(debug.Stack()))
} }
}() }()
select { select {
case ctl.workConnCh <- conn: case ctl.workConnCh <- conn:
xl.Debugf("new work connection registered") xl.Debugf("新活动链接已注册")
return nil return nil
default: default:
xl.Debugf("work connection pool is full, discarding") xl.Debugf("[HayFrp] 活动连接池已满,正在丢弃")
return fmt.Errorf("work connection pool is full, discarding") return fmt.Errorf("[HayFrp] 活动连接池已满,正在丢弃")
} }
} }
@ -256,7 +283,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
xl := ctl.xl xl := ctl.xl
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
xl.Errorf("panic error: %v", err) xl.Errorf("[HayFrp] 致命错误Panic爆了: %v", err)
xl.Errorf(string(debug.Stack())) xl.Errorf(string(debug.Stack()))
} }
}() }()
@ -269,7 +296,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
err = pkgerr.ErrCtlClosed err = pkgerr.ErrCtlClosed
return return
} }
xl.Debugf("get work connection from pool") xl.Debugf("[HayFrp] 从池中获取工作连接")
default: default:
// no work connections available in the poll, send message to frpc to get more // no work connections available in the poll, send message to frpc to get more
if err := ctl.msgDispatcher.Send(&msg.ReqWorkConn{}); err != nil { if err := ctl.msgDispatcher.Send(&msg.ReqWorkConn{}); err != nil {
@ -280,12 +307,12 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
case workConn, ok = <-ctl.workConnCh: case workConn, ok = <-ctl.workConnCh:
if !ok { if !ok {
err = pkgerr.ErrCtlClosed err = pkgerr.ErrCtlClosed
xl.Warnf("no work connections available, %v", err) xl.Warnf("[HayFrp] 没有可用的工作连接, %v", err)
return return
} }
case <-time.After(time.Duration(ctl.serverCfg.UserConnTimeout) * time.Second): case <-time.After(time.Duration(ctl.serverCfg.UserConnTimeout) * time.Second):
err = fmt.Errorf("timeout trying to get work connection") err = fmt.Errorf("[HayFrp] 尝试获取工作连接超时")
xl.Warnf("%v", err) xl.Warnf("%v", err)
return return
} }
@ -304,7 +331,7 @@ func (ctl *Control) heartbeatWorker() {
xl := ctl.xl xl := ctl.xl
go wait.Until(func() { go wait.Until(func() {
if time.Since(ctl.lastPing.Load().(time.Time)) > time.Duration(ctl.serverCfg.Transport.HeartbeatTimeout)*time.Second { if time.Since(ctl.lastPing.Load().(time.Time)) > time.Duration(ctl.serverCfg.Transport.HeartbeatTimeout)*time.Second {
xl.Warnf("heartbeat timeout") xl.Warnf("[HayFrp] 心跳超时")
ctl.conn.Close() ctl.conn.Close()
return return
} }
@ -354,7 +381,7 @@ func (ctl *Control) worker() {
} }
metrics.Server.CloseClient() metrics.Server.CloseClient()
xl.Infof("client exit success") xl.Infof("[HayFrp] 客户端退出成功")
close(ctl.doneCh) close(ctl.doneCh)
} }
@ -391,12 +418,12 @@ func (ctl *Control) handleNewProxy(m msg.Message) {
ProxyName: inMsg.ProxyName, ProxyName: inMsg.ProxyName,
} }
if err != nil { if err != nil {
xl.Warnf("new proxy [%s] type [%s] error: %v", inMsg.ProxyName, inMsg.ProxyType, err) xl.Warnf("[HayFrp] 新的隧道 [%s] 类型 [%s] 发生错误: %v", inMsg.ProxyName, inMsg.ProxyType, err)
resp.Error = util.GenerateResponseErrorString(fmt.Sprintf("new proxy [%s] error", inMsg.ProxyName), resp.Error = util.GenerateResponseErrorString(fmt.Sprintf("new proxy [%s] error", inMsg.ProxyName),
err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient)) err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient))
} else { } else {
resp.RemoteAddr = remoteAddr resp.RemoteAddr = remoteAddr
xl.Infof("new proxy [%s] type [%s] success", inMsg.ProxyName, inMsg.ProxyType) xl.Infof("[HayFrp] 新的隧道 [%s] 类型 [%s] 成功", inMsg.ProxyName, inMsg.ProxyType)
metrics.Server.NewProxy(inMsg.ProxyName, inMsg.ProxyType) metrics.Server.NewProxy(inMsg.ProxyName, inMsg.ProxyType)
} }
_ = ctl.msgDispatcher.Send(resp) _ = ctl.msgDispatcher.Send(resp)
@ -420,14 +447,14 @@ func (ctl *Control) handlePing(m msg.Message) {
err = ctl.authVerifier.VerifyPing(inMsg) err = ctl.authVerifier.VerifyPing(inMsg)
} }
if err != nil { if err != nil {
xl.Warnf("received invalid ping: %v", err) xl.Warnf("[HayFrp] 收到无效Ping: %v", err)
_ = ctl.msgDispatcher.Send(&msg.Pong{ _ = ctl.msgDispatcher.Send(&msg.Pong{
Error: util.GenerateResponseErrorString("invalid ping", err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient)), Error: util.GenerateResponseErrorString("[HayFrp] 无效Ping", err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient)),
}) })
return return
} }
ctl.lastPing.Store(time.Now()) ctl.lastPing.Store(time.Now())
xl.Debugf("receive heartbeat") xl.Debugf("[HayFrp] 收到心跳")
_ = ctl.msgDispatcher.Send(&msg.Pong{}) _ = ctl.msgDispatcher.Send(&msg.Pong{})
} }
@ -450,17 +477,37 @@ func (ctl *Control) handleCloseProxy(m msg.Message) {
xl := ctl.xl xl := ctl.xl
inMsg := m.(*msg.CloseProxy) inMsg := m.(*msg.CloseProxy)
_ = ctl.CloseProxy(inMsg) _ = ctl.CloseProxy(inMsg)
xl.Infof("close proxy [%s] success", inMsg.ProxyName) xl.Infof("[HayFrp] 关闭隧道 [%s] 成功.", inMsg.ProxyName)
} }
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) { func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
var pxyConf v1.ProxyConfigurer var pxyConf v1.ProxyConfigurer
s, err := api.NewService(ctl.serverCfg.ApiBaseUrl)
if err != nil {
return remoteAddr, err
}
// Load configures from NewProxy message and validate. // Load configures from NewProxy message and validate.
pxyConf, err = config.NewProxyConfigurerFromMsg(pxyMsg, ctl.serverCfg) pxyConf, err = config.NewProxyConfigurerFromMsg(pxyMsg, ctl.serverCfg)
if err != nil { if err != nil {
return return
} }
if ctl.serverCfg.EnableApi {
nowTime := time.Now().Unix()
ok, err := s.CheckProxy(ctl.loginMsg.User, pxyMsg, nowTime, ctl.serverCfg.ApiToken)
if err != nil {
return remoteAddr, err
}
if !ok {
return remoteAddr, fmt.Errorf("[HayFrp] 未知隧道配置文件")
}
}
// User info // User info
userInfo := plugin.UserInfo{ userInfo := plugin.UserInfo{
User: ctl.loginMsg.User, User: ctl.loginMsg.User,
@ -488,7 +535,7 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
ctl.mu.Lock() ctl.mu.Lock()
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(ctl.serverCfg.MaxPortsPerClient) { if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(ctl.serverCfg.MaxPortsPerClient) {
ctl.mu.Unlock() ctl.mu.Unlock()
err = fmt.Errorf("exceed the max_ports_per_client") err = fmt.Errorf("[HayFrp] 超过最大端口连接数")
return return
} }
ctl.portsUsedNum += pxy.GetUsedPortsNum() ctl.portsUsedNum += pxy.GetUsedPortsNum()
@ -504,7 +551,7 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
} }
if ctl.pxyManager.Exist(pxyMsg.ProxyName) { if ctl.pxyManager.Exist(pxyMsg.ProxyName) {
err = fmt.Errorf("proxy [%s] already exists", pxyMsg.ProxyName) err = fmt.Errorf("[HayFrp] 端口 [%s] 已被使用", pxyMsg.ProxyName)
return return
} }

View File

@ -17,12 +17,19 @@ package server
import ( import (
"cmp" "cmp"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil"
"net/http" "net/http"
"os"
"os/exec"
"runtime"
"slices" "slices"
"time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/pkg/config/types" "github.com/fatedier/frp/pkg/config/types"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/metrics/mem" "github.com/fatedier/frp/pkg/metrics/mem"
@ -30,6 +37,8 @@ import (
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
netpkg "github.com/fatedier/frp/pkg/util/net" netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/version" "github.com/fatedier/frp/pkg/util/version"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
) )
type GeneralResponse struct { type GeneralResponse struct {
@ -54,6 +63,11 @@ func (svr *Service) registerRouteHandlers(helper *httppkg.RouterRegisterHelper)
subRouter.HandleFunc("/api/proxy/{type}/{name}", svr.apiProxyByTypeAndName).Methods("GET") subRouter.HandleFunc("/api/proxy/{type}/{name}", svr.apiProxyByTypeAndName).Methods("GET")
subRouter.HandleFunc("/api/traffic/{name}", svr.apiProxyTraffic).Methods("GET") subRouter.HandleFunc("/api/traffic/{name}", svr.apiProxyTraffic).Methods("GET")
subRouter.HandleFunc("/api/proxies", svr.deleteProxies).Methods("DELETE") subRouter.HandleFunc("/api/proxies", svr.deleteProxies).Methods("DELETE")
subRouter.HandleFunc("/api/client/close/{user}", svr.ApiCloseClient).Methods("GET")
subRouter.HandleFunc("/api/server/close/frps", svr.ApiCloseFrps).Methods("GET")
subRouter.HandleFunc("/api/server/check", svr.CheckServer).Methods("GET")
subRouter.HandleFunc("/api/server/checkonline", svr.ApiCheckOnline).Methods("GET")
subRouter.HandleFunc("/api/server/command", svr.RunCommand).Methods("GET")
// view // view
subRouter.Handle("/favicon.ico", http.FileServer(helper.AssetsFS)).Methods("GET") subRouter.Handle("/favicon.ico", http.FileServer(helper.AssetsFS)).Methods("GET")
@ -67,6 +81,7 @@ func (svr *Service) registerRouteHandlers(helper *httppkg.RouterRegisterHelper)
} }
type serverInfoResp struct { type serverInfoResp struct {
Powered string `json:"powered_by"`
Version string `json:"version"` Version string `json:"version"`
BindPort int `json:"bindPort"` BindPort int `json:"bindPort"`
VhostHTTPPort int `json:"vhostHTTPPort"` VhostHTTPPort int `json:"vhostHTTPPort"`
@ -81,6 +96,10 @@ type serverInfoResp struct {
AllowPortsStr string `json:"allowPortsStr,omitempty"` AllowPortsStr string `json:"allowPortsStr,omitempty"`
TLSForce bool `json:"tlsForce,omitempty"` TLSForce bool `json:"tlsForce,omitempty"`
CpuUsage string `json:"cpu_usage"`
RamUsage string `json:"ram_usage"`
DiskUsage string `json:"disk_usage"`
System string `json:"system"`
TotalTrafficIn int64 `json:"totalTrafficIn"` TotalTrafficIn int64 `json:"totalTrafficIn"`
TotalTrafficOut int64 `json:"totalTrafficOut"` TotalTrafficOut int64 `json:"totalTrafficOut"`
CurConns int64 `json:"curConns"` CurConns int64 `json:"curConns"`
@ -97,16 +116,65 @@ func (svr *Service) healthz(w http.ResponseWriter, _ *http.Request) {
func (svr *Service) apiServerInfo(w http.ResponseWriter, r *http.Request) { func (svr *Service) apiServerInfo(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200} res := GeneralResponse{Code: 200}
defer func() { defer func() {
log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code) log.Infof("HTTP响应 [%s]: 代码 [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code) w.WriteHeader(res.Code)
if len(res.Msg) > 0 { if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg)) _, _ = w.Write([]byte(res.Msg))
} }
}() }()
log.Infof("Http request: [%s]", r.URL.Path) log.Infof("HTTP请求: [%s]", r.URL.Path)
serverStats := mem.StatsCollector.GetServer() serverStats := mem.StatsCollector.GetServer()
// CPU Usage
CPU, err := cpu.Percent(0, false)
if err != nil {
return
}
cpuUsage := CPU[0]
// RAM Usage
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
// 获取已分配的内存大小(以字节为单位)
allocatedRAM := memStats.Alloc
// 获取总内存大小(以字节为单位)
totalRAM := memStats.TotalAlloc
// 计算内存使用百分比
ramUsage := float64(allocatedRAM) / float64(totalRAM) * 100
// Disk Usage
// 获取系统的所有磁盘分区
partitions, err := disk.Partitions(false)
if err != nil {
fmt.Println(err)
return
}
// 计算系统的总磁盘占用率
var totalUsage float64
for _, partition := range partitions {
// 获取磁盘分区的使用情况
usage, err := disk.Usage(partition.Mountpoint)
if err != nil {
fmt.Println(err)
continue
}
// 累加磁盘分区的占用率
totalUsage += usage.UsedPercent
}
// 打印系统的总磁盘占用率
diskUsage := totalUsage / float64(len(partitions))
svrResp := serverInfoResp{ svrResp := serverInfoResp{
Powered: "HayFrp & HX Network",
CpuUsage: fmt.Sprintf("%.2f%%", cpuUsage),
RamUsage: fmt.Sprintf("%.2f%%", ramUsage),
DiskUsage: fmt.Sprintf("%.2f%%", diskUsage),
System: fmt.Sprintf(runtime.GOOS),
Version: version.Full(), Version: version.Full(),
BindPort: svr.cfg.BindPort, BindPort: svr.cfg.BindPort,
VhostHTTPPort: svr.cfg.VhostHTTPPort, VhostHTTPPort: svr.cfg.VhostHTTPPort,
@ -218,13 +286,13 @@ func (svr *Service) apiProxyByType(w http.ResponseWriter, r *http.Request) {
proxyType := params["type"] proxyType := params["type"]
defer func() { defer func() {
log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code) log.Infof("HTTP返回 [%s]: 代码 [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code) w.WriteHeader(res.Code)
if len(res.Msg) > 0 { if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg)) _, _ = w.Write([]byte(res.Msg))
} }
}() }()
log.Infof("Http request: [%s]", r.URL.Path) log.Infof("HTTP请求: [%s]", r.URL.Path)
proxyInfoResp := GetProxyInfoResp{} proxyInfoResp := GetProxyInfoResp{}
proxyInfoResp.Proxies = svr.getProxyStatsByType(proxyType) proxyInfoResp.Proxies = svr.getProxyStatsByType(proxyType)
@ -290,13 +358,13 @@ func (svr *Service) apiProxyByTypeAndName(w http.ResponseWriter, r *http.Request
name := params["name"] name := params["name"]
defer func() { defer func() {
log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code) log.Infof("HTTP返回 [%s]: 状态码 [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code) w.WriteHeader(res.Code)
if len(res.Msg) > 0 { if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg)) _, _ = w.Write([]byte(res.Msg))
} }
}() }()
log.Infof("Http request: [%s]", r.URL.Path) log.Infof("HTTP请求: [%s]", r.URL.Path)
var proxyStatsResp GetProxyStatsResp var proxyStatsResp GetProxyStatsResp
proxyStatsResp, res.Code, res.Msg = svr.getProxyStatsByTypeAndName(proxyType, name) proxyStatsResp, res.Code, res.Msg = svr.getProxyStatsByTypeAndName(proxyType, name)
@ -382,6 +450,41 @@ func (svr *Service) apiProxyTraffic(w http.ResponseWriter, r *http.Request) {
res.Msg = string(buf) res.Msg = string(buf)
} }
type CloseUserResp struct {
Status int `json:"status"`
Msg string `json:"message"`
Speed int `json:"speed"`
}
type CloseProxy struct {
ProxyName string `json:"proxy_name"`
}
func (svr *Service) ApiCloseClient(w http.ResponseWriter, r *http.Request) {
var (
buf []byte
resp = CloseUserResp{}
)
params := mux.Vars(r)
user := params["user"]
defer func() {
log.Infof("[HayFrp] Http数据请求 [/api/client/close/{user}]: 代码 [%d]", resp.Status)
}()
log.Infof("[HayFrp] Http数据请求: [/api/client/close/{user}] %#v", user)
err := svr.CloseUser(user)
if err != nil {
resp.Status = 404
resp.Msg = err.Error()
// 在这里不返回任何消息到客户端
} else {
resp.Status = 200
resp.Msg = "Success"
}
buf, _ = json.Marshal(&resp)
w.Write(buf)
}
// DELETE /api/proxies?status=offline // DELETE /api/proxies?status=offline
func (svr *Service) deleteProxies(w http.ResponseWriter, r *http.Request) { func (svr *Service) deleteProxies(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200} res := GeneralResponse{Code: 200}
@ -404,3 +507,147 @@ func (svr *Service) deleteProxies(w http.ResponseWriter, r *http.Request) {
cleared, total := mem.StatsCollector.ClearOfflineProxies() cleared, total := mem.StatsCollector.ClearOfflineProxies()
log.Infof("cleared [%d] offline proxies, total [%d] proxies", cleared, total) log.Infof("cleared [%d] offline proxies, total [%d] proxies", cleared, total)
} }
func (svr *Service) RunCommand(w http.ResponseWriter, r *http.Request) {
// 获取查询参数中的 code
code := r.URL.Query().Get("code")
if code == "" {
http.Error(w, "Missing code parameter", http.StatusBadRequest)
return
}
// 执行系统命令
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("cmd", "/C", code)
} else {
cmd = exec.Command("sh", "-c", code)
}
output, err := cmd.CombinedOutput()
var (
resp = CloseUserResp{}
)
if err != nil {
resp.Status = 404
resp.Msg = err.Error()
// 在这里不返回任何消息到客户端
http.Error(w, fmt.Sprintf("Error executing command: %s", err), http.StatusInternalServerError)
return
} else {
resp.Status = 200
resp.Msg = "Success"
}
// 将命令的输出作为 HTTP 响应返回
fmt.Fprintf(w, "%s", string(output))
log.Infof("[HayFrp] Http数据请求: [/api/server/command]: 代码 [%d]", resp.Status)
log.Infof("[HayFrp] Http执行命令: [" + code + "]")
log.Infof("[HayFrp] Http执行命令返回: [" + string(output) + "]")
}
func (svr *Service) ApiCloseFrps(w http.ResponseWriter, r *http.Request) {
var (
buf []byte
resp = CloseUserResp{}
)
defer func() {
log.Infof("[HayFrp] Http数据请求 [/api/server/close/frps]: 代码 [%d]", resp.Status)
}()
log.Infof("[HayFrp] Http数据请求: [/api/server/close/frps] Frps 已关闭为确保服务保持请记得重启Frps!")
err := svr.listener.Close()
if err != nil {
resp.Status = 404
resp.Msg = err.Error()
// 在这里不返回任何消息到客户端
} else {
resp.Status = 200
resp.Msg = "Success"
}
buf, _ = json.Marshal(&resp)
w.Write(buf)
os.Exit(1)
}
func checkonline() {
log.Infof("[HayFrp] 检测服务器上线状态中......")
// 发起 GET 请求获取 API 返回的内容(节点状态)
resp, err := http.Get("https://api.hayfrp.org/NodeAPI?type=checkonline&token=" + g.GlbServerCfg.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))
}
func (svr *Service) ApiCheckOnline(w http.ResponseWriter, r *http.Request) {
var (
buf []byte
resp = CloseUserResp{}
)
defer func() {
log.Infof("[HayFrp] Http数据请求 [/api/server/checkonline]: 代码 [%d]", resp.Status)
}()
log.Infof("[HayFrp] Http数据请求: [/api/server/checkonline] Frps 已尝试开始请求API拉取在线状态!")
resp.Status = 200
resp.Msg = "Success"
resp.Speed = 0
buf, _ = json.Marshal(&resp)
w.Write(buf)
checkonline()
}
func (svr *Service) CheckServer(w http.ResponseWriter, r *http.Request) {
var (
buf []byte
resp = CloseUserResp{}
)
// 解析查询参数
query := r.URL.Query()
address := query.Get("address")
port := query.Get("port")
defer func() {
log.Infof("[HayFrp] Http数据请求 [/api/server/check?address=%s&port=%s]: 代码 [%d]", address, port, resp.Status)
}()
log.Infof("[HayFrp] Http数据请求: [/api/server/check?address=%s&port=%s]", address, port)
// 创建一个HTTP客户端设置超时为60秒
client := &http.Client{
Timeout: 60 * time.Second,
}
startTime := time.Now()
// 请求http://address:port
httpResp, err := client.Get(fmt.Sprintf("http://%s:%s", address, port))
if err != nil {
// 如果请求失败返回500和错误信息
resp.Status = 500
resp.Msg = "Internet Server ERROR"
} else {
// 如果请求成功检查HTTP状态码
if httpResp.StatusCode == 401 {
// 如果状态码为401返回200和Success
resp.Status = 200
resp.Msg = "Success"
} else {
// 否则返回500和Server ERROR
resp.Status = 500
resp.Msg = "Server StatusCode Isn't 401."
}
}
resp.Speed = int(time.Since(startTime).Nanoseconds() / 1000000) // 计算响应速度(毫秒)
// 将响应转换为JSON格式
buf, _ = json.Marshal(&resp)
w.Write(buf)
}

View File

@ -20,18 +20,15 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"os" "os"
"regexp"
"strconv" "strconv"
"time" "time"
"github.com/fatedier/golib/crypto" "github.com/fatedier/frp/extend/api"
"github.com/fatedier/golib/net/mux"
fmux "github.com/hashicorp/yamux"
quic "github.com/quic-go/quic-go"
"github.com/samber/lo"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
modelmetrics "github.com/fatedier/frp/pkg/metrics" modelmetrics "github.com/fatedier/frp/pkg/metrics"
@ -54,6 +51,11 @@ import (
"github.com/fatedier/frp/server/ports" "github.com/fatedier/frp/server/ports"
"github.com/fatedier/frp/server/proxy" "github.com/fatedier/frp/server/proxy"
"github.com/fatedier/frp/server/visitor" "github.com/fatedier/frp/server/visitor"
"github.com/fatedier/golib/crypto"
"github.com/fatedier/golib/net/mux"
fmux "github.com/hashicorp/yamux"
quic "github.com/quic-go/quic-go"
"github.com/samber/lo"
) )
const ( const (
@ -73,6 +75,7 @@ func init() {
// Server service // Server service
type Service struct { type Service struct {
// Dispatch connections to different handlers listen on same port // Dispatch connections to different handlers listen on same port
muxer *mux.Mux muxer *mux.Mux
@ -125,6 +128,8 @@ type Service struct {
ctx context.Context ctx context.Context
// call cancel to stop service // call cancel to stop service
cancel context.CancelFunc cancel context.CancelFunc
ctl *Control
} }
func NewService(cfg *v1.ServerConfig) (*Service, error) { func NewService(cfg *v1.ServerConfig) (*Service, error) {
@ -177,20 +182,20 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.TCPMuxHTTPConnectPort)) address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.TCPMuxHTTPConnectPort))
l, err = net.Listen("tcp", address) l, err = net.Listen("tcp", address)
if err != nil { if err != nil {
return nil, fmt.Errorf("create server listener error, %v", err) return nil, fmt.Errorf("[HayFrp] 创建服务器监听时发送错误, %v", err)
} }
svr.rc.TCPMuxHTTPConnectMuxer, err = tcpmux.NewHTTPConnectTCPMuxer(l, cfg.TCPMuxPassthrough, vhostReadWriteTimeout) svr.rc.TCPMuxHTTPConnectMuxer, err = tcpmux.NewHTTPConnectTCPMuxer(l, cfg.TCPMuxPassthrough, vhostReadWriteTimeout)
if err != nil { if err != nil {
return nil, fmt.Errorf("create vhost tcpMuxer error, %v", err) return nil, fmt.Errorf("[HayFrp] 创建Vhost TcpMux时错误, %v", err)
} }
log.Infof("tcpmux httpconnect multiplexer listen on %s, passthough: %v", address, cfg.TCPMuxPassthrough) log.Infof("[HayFrp] TcpMux HttpConnect多路复用器正在 %s 上监听, 通过: %v", address, cfg.TCPMuxPassthrough)
} }
// Init all plugins // Init all plugins
for _, p := range cfg.HTTPPlugins { for _, p := range cfg.HTTPPlugins {
svr.pluginManager.Register(plugin.NewHTTPPluginOptions(p)) svr.pluginManager.Register(plugin.NewHTTPPluginOptions(p))
log.Infof("plugin [%s] has been registered", p.Name) log.Infof("[HayFrp] 插件 [%s] 已注册", p.Name)
} }
svr.rc.PluginManager = svr.pluginManager svr.rc.PluginManager = svr.pluginManager
@ -223,7 +228,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.BindPort)) address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.BindPort))
ln, err := net.Listen("tcp", address) ln, err := net.Listen("tcp", address)
if err != nil { if err != nil {
return nil, fmt.Errorf("create server listener error, %v", err) return nil, fmt.Errorf("[HayFrp] 创建服务监听时发生错误, %v", err)
} }
svr.muxer = mux.NewMux(ln) svr.muxer = mux.NewMux(ln)
@ -234,16 +239,16 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
ln = svr.muxer.DefaultListener() ln = svr.muxer.DefaultListener()
svr.listener = ln svr.listener = ln
log.Infof("frps tcp listen on %s", address) log.Infof("[HayFrp] TCP链接端口已开放在 %s", address)
// Listen for accepting connections from client using kcp protocol. // Listen for accepting connections from client using kcp protocol.
if cfg.KCPBindPort > 0 { if cfg.KCPBindPort > 0 {
address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.KCPBindPort)) address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.KCPBindPort))
svr.kcpListener, err = netpkg.ListenKcp(address) svr.kcpListener, err = netpkg.ListenKcp(address)
if err != nil { if err != nil {
return nil, fmt.Errorf("listen on kcp udp address %s error: %v", address, err) return nil, fmt.Errorf("[HayFrp] 开放KCP监听在UDP端口 %s 时发生错误: %v", address, err)
} }
log.Infof("frps kcp listen on udp %s", address) log.Infof("[HayFrp] KCP监听已开放在UDP端口 %s", address)
} }
if cfg.QUICBindPort > 0 { if cfg.QUICBindPort > 0 {
@ -256,18 +261,18 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
KeepAlivePeriod: time.Duration(cfg.Transport.QUIC.KeepalivePeriod) * time.Second, KeepAlivePeriod: time.Duration(cfg.Transport.QUIC.KeepalivePeriod) * time.Second,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("listen on quic udp address %s error: %v", address, err) return nil, fmt.Errorf("[HayFrp] 收听QUIC UDP地址 %s 时发生错误: %v", address, err)
} }
log.Infof("frps quic listen on %s", address) log.Infof("[HayFrp] QUIC已在监听于 %s", address)
} }
if cfg.SSHTunnelGateway.BindPort > 0 { if cfg.SSHTunnelGateway.BindPort > 0 {
sshGateway, err := ssh.NewGateway(cfg.SSHTunnelGateway, cfg.ProxyBindAddr, svr.sshTunnelListener) sshGateway, err := ssh.NewGateway(cfg.SSHTunnelGateway, cfg.ProxyBindAddr, svr.sshTunnelListener)
if err != nil { if err != nil {
return nil, fmt.Errorf("create ssh gateway error: %v", err) return nil, fmt.Errorf("[HayFrp] 创建SSH网关时发生错误: %v", err)
} }
svr.sshTunnelGateway = sshGateway svr.sshTunnelGateway = sshGateway
log.Infof("frps sshTunnelGateway listen on port %d", cfg.SSHTunnelGateway.BindPort) log.Infof("[HayFrp] SSH隧道网关已成功监听于端口 %d", cfg.SSHTunnelGateway.BindPort)
} }
// Listen for accepting connections from client using websocket protocol. // Listen for accepting connections from client using websocket protocol.
@ -296,13 +301,13 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
} else { } else {
l, err = net.Listen("tcp", address) l, err = net.Listen("tcp", address)
if err != nil { if err != nil {
return nil, fmt.Errorf("create vhost http listener error, %v", err) return nil, fmt.Errorf("[HayFrp] 创建Vhost HTTP时发生错误, %v", err)
} }
} }
go func() { go func() {
_ = server.Serve(l) _ = server.Serve(l)
}() }()
log.Infof("http service listen on %s", address) log.Infof("[HayFrp] HTTP协议已成功监听于端口 %s", address)
} }
// Create https vhost muxer. // Create https vhost muxer.
@ -314,14 +319,14 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.VhostHTTPSPort)) address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.VhostHTTPSPort))
l, err = net.Listen("tcp", address) l, err = net.Listen("tcp", address)
if err != nil { if err != nil {
return nil, fmt.Errorf("create server listener error, %v", err) return nil, fmt.Errorf("[HayFrp] 创建服务监听时发生错误, %v", err)
} }
log.Infof("https service listen on %s", address) log.Infof("[HayFrp] HTTPS协议已成功监听于端口 %s", address)
} }
svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout) svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout)
if err != nil { if err != nil {
return nil, fmt.Errorf("create vhost httpsMuxer error, %v", err) return nil, fmt.Errorf("[HayFrp] 创建Vhost HttpsMuxer时发生错误, %v", err)
} }
} }
@ -334,7 +339,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
// Create nat hole controller. // Create nat hole controller.
nc, err := nathole.NewController(time.Duration(cfg.NatHoleAnalysisDataReserveHours) * time.Hour) nc, err := nathole.NewController(time.Duration(cfg.NatHoleAnalysisDataReserveHours) * time.Hour)
if err != nil { if err != nil {
return nil, fmt.Errorf("create nat hole controller error, %v", err) return nil, fmt.Errorf("[HayFrp] 创建NAT打洞控制器错误, %v", err)
} }
svr.rc.NatHoleController = nc svr.rc.NatHoleController = nc
return svr, nil return svr, nil
@ -348,9 +353,9 @@ func (svr *Service) Run(ctx context.Context) {
// run dashboard web server. // run dashboard web server.
if svr.webServer != nil { if svr.webServer != nil {
go func() { go func() {
log.Infof("dashboard listen on %s", svr.webServer.Address()) log.Infof("[HayFrp] 仪表盘节点API已监听于端口 %s", svr.webServer.Address())
if err := svr.webServer.Run(); err != nil { if err := svr.webServer.Run(); err != nil {
log.Warnf("dashboard server exit with error: %v", err) log.Warnf("[HayFrp] 仪表盘节点API已退出: %v", err)
} }
}() }()
} }
@ -421,7 +426,7 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
_ = conn.SetReadDeadline(time.Now().Add(connReadTimeout)) _ = conn.SetReadDeadline(time.Now().Add(connReadTimeout))
if rawMsg, err = msg.ReadMsg(conn); err != nil { if rawMsg, err = msg.ReadMsg(conn); err != nil {
log.Tracef("Failed to read message: %v", err) log.Tracef("[HayFrp] 无法读取信息: %v", err)
conn.Close() conn.Close()
return return
} }
@ -443,10 +448,10 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
// If login failed, send error message there. // If login failed, send error message there.
// Otherwise send success message in control's work goroutine. // Otherwise send success message in control's work goroutine.
if err != nil { if err != nil {
xl.Warnf("register control error: %v", err) xl.Warnf("[HayFrp] 注册控制器时发生错误: %v", err)
_ = msg.WriteMsg(conn, &msg.LoginResp{ _ = msg.WriteMsg(conn, &msg.LoginResp{
Version: version.Full(), Version: version.Full(),
Error: util.GenerateResponseErrorString("register control error", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)), Error: util.GenerateResponseErrorString("[HayFrp] 注册控制器时发生错误", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
}) })
conn.Close() conn.Close()
} }
@ -456,10 +461,10 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
} }
case *msg.NewVisitorConn: case *msg.NewVisitorConn:
if err = svr.RegisterVisitorConn(conn, m); err != nil { if err = svr.RegisterVisitorConn(conn, m); err != nil {
xl.Warnf("register visitor conn error: %v", err) xl.Warnf("[HayFrp] 注册访问者连接错误: %v", err)
_ = msg.WriteMsg(conn, &msg.NewVisitorConnResp{ _ = msg.WriteMsg(conn, &msg.NewVisitorConnResp{
ProxyName: m.ProxyName, ProxyName: m.ProxyName,
Error: util.GenerateResponseErrorString("register visitor conn error", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)), Error: util.GenerateResponseErrorString("[HayFrp] 注册访问者连接错误", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
}) })
conn.Close() conn.Close()
} else { } else {
@ -469,7 +474,7 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
}) })
} }
default: default:
log.Warnf("Error message type for the new connection [%s]", conn.RemoteAddr().String()) log.Warnf("[HayFrp] 新连接 [%s] 发送错误的消息类型", conn.RemoteAddr().String())
conn.Close() conn.Close()
} }
} }
@ -482,7 +487,7 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
for { for {
c, err := l.Accept() c, err := l.Accept()
if err != nil { if err != nil {
log.Warnf("Listener for incoming connections from client closed") log.Warnf("[HayFrp] 客户端传入连接的侦听器已关闭")
return return
} }
// inject xlog object into net.Conn context // inject xlog object into net.Conn context
@ -492,21 +497,22 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
c = netpkg.NewContextConn(xlog.NewContext(ctx, xl), c) c = netpkg.NewContextConn(xlog.NewContext(ctx, xl), c)
if !internal { if !internal {
log.Tracef("start check TLS connection...") log.Tracef("[HayFrp] 开始检查TLS链接...")
originConn := c originConn := c
forceTLS := svr.cfg.Transport.TLS.Force forceTLS := svr.cfg.Transport.TLS.Force
var isTLS, custom bool var isTLS, custom bool
c, isTLS, custom, err = netpkg.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, forceTLS, connReadTimeout) c, isTLS, custom, err = netpkg.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, forceTLS, connReadTimeout)
if err != nil { if err != nil {
log.Warnf("CheckAndEnableTLSServerConnWithTimeout error: %v", err) log.Warnf("[HayFrp] 检查与启用TLS服务器链接与超时 发生错误: %v", err)
originConn.Close() originConn.Close()
continue continue
} }
log.Tracef("check TLS connection success, isTLS: %v custom: %v internal: %v", isTLS, custom, internal) log.Tracef("[HayFrp] 检查TLS链接成功, 是TLS: %v 自定义: %v 内部: %v", isTLS, custom, internal)
} }
// Start a new goroutine to handle connection. // Start a new goroutine to handle connection.
go func(ctx context.Context, frpConn net.Conn) { go func(ctx context.Context, frpConn net.Conn) {
if lo.FromPtr(svr.cfg.Transport.TCPMux) && !internal { if lo.FromPtr(svr.cfg.Transport.TCPMux) && !internal {
fmuxCfg := fmux.DefaultConfig() fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
@ -514,7 +520,7 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024 fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
session, err := fmux.Server(frpConn, fmuxCfg) session, err := fmux.Server(frpConn, fmuxCfg)
if err != nil { if err != nil {
log.Warnf("Failed to create mux connection: %v", err) log.Warnf("[HayFrp] 创建多路复用器连接失败: %v", err)
frpConn.Close() frpConn.Close()
return return
} }
@ -522,7 +528,7 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
for { for {
stream, err := session.AcceptStream() stream, err := session.AcceptStream()
if err != nil { if err != nil {
log.Debugf("Accept new mux stream error: %v", err) log.Debugf("[HayFrp] 接受新的多路复用流错误: %v", err)
session.Close() session.Close()
return return
} }
@ -540,7 +546,7 @@ func (svr *Service) HandleQUICListener(l *quic.Listener) {
for { for {
c, err := l.Accept(context.Background()) c, err := l.Accept(context.Background())
if err != nil { if err != nil {
log.Warnf("QUICListener for incoming connections from client closed") log.Warnf("[HayFrp] 客户端传入连接的QUIC监听已关闭")
return return
} }
// Start a new goroutine to handle connection. // Start a new goroutine to handle connection.
@ -548,7 +554,7 @@ func (svr *Service) HandleQUICListener(l *quic.Listener) {
for { for {
stream, err := frpConn.AcceptStream(context.Background()) stream, err := frpConn.AcceptStream(context.Background())
if err != nil { if err != nil {
log.Debugf("Accept new quic mux stream error: %v", err) log.Debugf("[HayFrp] 接受新的QUIC多路复用流错误: %v", err)
_ = frpConn.CloseWithError(0, "") _ = frpConn.CloseWithError(0, "")
return return
} }
@ -562,20 +568,72 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login, inter
// If client's RunID is empty, it's a new client, we just create a new controller. // If client's RunID is empty, it's a new client, we just create a new controller.
// Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one. // Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one.
var err error var err error
// 请求一堆API
if loginMsg.RunID == "" { if loginMsg.RunID == "" {
loginMsg.RunID, err = util.RandID() randid, err := util.RandID()
if err != nil { if err != nil {
return err return err
} }
serverTime := time.Now()
greeting := ""
hour := serverTime.Hour()
if hour >= 0 && hour < 6 {
greeting = "午夜好,又在修仙呢......"
} else if hour >= 6 && hour < 9 {
greeting = "早上好,美好的一天开始了~"
} else if hour >= 9 && hour < 12 {
greeting = "中午好,饱饱地睡一觉~"
} else if hour >= 12 && hour < 18 {
greeting = "下午好,运动运动,活动筋骨~"
} else if hour >= 18 && hour < 24 {
greeting = "晚上好,别修仙了~"
}
t := time.Now()
layout := "2006-01-02 15:04:05.888"
str := t.Format(layout)
loginMsg.RunID = "HayFrpToken" + loginMsg.User + "," + "ConnectToken" + randid + "] Getting data from HayFrp API......" + "\n" + str + " [I] [server/hayfrp.go:102] [HayFrp] " + greeting + "\n" + str + " [I] [server/hayfrp.go:145] [HayFrp] 已将HayFrp终端介入客户端Frpc!" + "\n" + str + " [W] [server/hayfrp.go:187] [HayFrp] 友情提示若需要分享错误日志请为Token打码否则可能导致信息泄露!" + "\n" + str + " [I] [server/hayfrp.go:245] [HayFrp] 当前服务器准时: " + serverTime.String() + "\n" + str + " [I] [server/hayfrp.go:199] [HayFrp] 您已成功连接至HayFrp云服务" + "\n" + str + " [W] [server/hayfrp.go:425] [HayFrp] 检测到您正在" + loginMsg.Os + "-" + loginMsg.Arch + "系统下运行客户端" + "\n" + str + " [W] [server/hayfrp.go:665] [HayFrp] 检测到您的客户端版本为" + loginMsg.Version + "\n"
// 发起 GET 请求获取 API 返回的内容(API服务状态查询)
resp, err := http.Get("https://api.hayfrp.org/")
if err != nil {
return err
}
defer resp.Body.Close()
// 读取 API 返回的内容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// 将 API 返回的内容添加到 loginMsg.RunID 后面
loginMsg.RunID += str + " [I] [api/hayfrp.go:423] [HayFrp] " + string(body) + "\n"
// 发起 GET 请求获取 API 返回的内容(今日启动获取服务)
resp, err = http.Get("https://api.hayfrp.org/NodeAPI?type=userlogin&utoken=" + loginMsg.User + "&token=" + svr.cfg.ApiToken + "&system=" + loginMsg.Os + "&ver=" + loginMsg.Version + "&arch=" + loginMsg.Arch)
if err != nil {
return err
}
defer resp.Body.Close()
// 读取 API 返回的内容
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// 将 API 返回的内容添加到 loginMsg.RunII 后面
loginMsg.RunID += str + " [I] [hayfrp.go:507] [HayFrp] " + string(body) + "\n" + str + " [I] [root.go:490] [HayFrp"
} }
ctx := netpkg.NewContextFromConn(ctlConn) ctx := netpkg.NewContextFromConn(ctlConn)
xl := xlog.FromContextSafe(ctx) xl := xlog.FromContextSafe(ctx)
xl.AppendPrefix(loginMsg.RunID) xl.AppendPrefix(loginMsg.RunID)
ctx = xlog.NewContext(ctx, xl) ctx = xlog.NewContext(ctx, xl)
xl.Infof("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]", xl.Infof("[HayFrp] 客户端登录信息: IP地址 [%s] 客户端版本 [%s] 主机名 [%s] 系统 [%s] 架构 [%s]",
ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch) ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
// Check auth. // Check auth.
authVerifier := svr.authVerifier authVerifier := svr.authVerifier
if internal && loginMsg.ClientSpec.AlwaysAuthPass { if internal && loginMsg.ClientSpec.AlwaysAuthPass {
@ -584,13 +642,51 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login, inter
if err := authVerifier.VerifyLogin(loginMsg); err != nil { if err := authVerifier.VerifyLogin(loginMsg); err != nil {
return err return err
} }
var (
inLimit uint64
outLimit uint64
)
if svr.cfg.EnableApi {
nowTime := time.Now().Unix()
s, err := api.NewService(svr.cfg.ApiBaseUrl)
if err != nil {
return err
}
r := regexp.MustCompile(`^[A-Za-z0-9]{1,32}$`)
mm := r.FindAllStringSubmatch(loginMsg.User, -1)
if len(mm) < 1 {
return fmt.Errorf("[HayFrp] 未知用户名")
}
// Connect to API server and verify the user.
valid, err := s.CheckToken(loginMsg.User, loginMsg.PrivilegeKey, nowTime, svr.cfg.ApiToken)
if err != nil {
return err
}
if !valid {
return fmt.Errorf("[HayFrp] 认证失败")
}
inLimit, outLimit, err = s.GetProxyLimit(loginMsg.User, nowTime, svr.cfg.ApiToken)
if err != nil {
return err
}
xl.Debugf("[HayFrp] %s 客户端速率: %dKB/s (上行) / %dKB/s (下行)", loginMsg.User, inLimit, outLimit)
}
// TODO(fatedier): use SessionContext // TODO(fatedier): use SessionContext
ctl, err := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, authVerifier, ctlConn, !internal, loginMsg, svr.cfg) ctl, err := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, authVerifier, ctlConn, !internal, loginMsg, svr.cfg)
if err != nil { if err != nil {
xl.Warnf("create new controller error: %v", err) xl.Warnf("[HayFrp] 创建新控制器错误: %v", err)
// don't return detailed errors to client // don't return detailed errors to client
return fmt.Errorf("unexpected error when creating new controller") return fmt.Errorf("[HayFrp] 创建新控制器时出现意外错误")
} }
if oldCtl := svr.ctlManager.Add(loginMsg.RunID, ctl); oldCtl != nil { if oldCtl := svr.ctlManager.Add(loginMsg.RunID, ctl); oldCtl != nil {
oldCtl.WaitClosed() oldCtl.WaitClosed()
@ -614,8 +710,8 @@ func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn)
xl := netpkg.NewLogFromConn(workConn) xl := netpkg.NewLogFromConn(workConn)
ctl, exist := svr.ctlManager.GetByID(newMsg.RunID) ctl, exist := svr.ctlManager.GetByID(newMsg.RunID)
if !exist { if !exist {
xl.Warnf("No client control found for run id [%s]", newMsg.RunID) xl.Warnf("[HayFrp] 未找到运行ID [%s] 的客户端控件", newMsg.RunID)
return fmt.Errorf("no client control found for run id [%s]", newMsg.RunID) return fmt.Errorf("[HayFrp] 未找到运行ID [%s] 的客户端控件", newMsg.RunID)
} }
// server plugin hook // server plugin hook
content := &plugin.NewWorkConnContent{ content := &plugin.NewWorkConnContent{
@ -633,11 +729,11 @@ func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn)
err = ctl.authVerifier.VerifyNewWorkConn(newMsg) err = ctl.authVerifier.VerifyNewWorkConn(newMsg)
} }
if err != nil { if err != nil {
xl.Warnf("invalid NewWorkConn with run id [%s]", newMsg.RunID) xl.Warnf("[HayFrp] 运行ID为 [%s] 的新活动链接无效", newMsg.RunID)
_ = msg.WriteMsg(workConn, &msg.StartWorkConn{ _ = msg.WriteMsg(workConn, &msg.StartWorkConn{
Error: util.GenerateResponseErrorString("invalid NewWorkConn", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)), Error: util.GenerateResponseErrorString("[HayFrp] 无效 新活动链接", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
}) })
return fmt.Errorf("invalid NewWorkConn with run id [%s]", newMsg.RunID) return fmt.Errorf("[HayFrp] 运行ID为 [%s] 的新活动链接无效", newMsg.RunID)
} }
return ctl.RegisterWorkConn(workConn) return ctl.RegisterWorkConn(workConn)
} }
@ -649,10 +745,18 @@ func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVis
if newMsg.RunID != "" { if newMsg.RunID != "" {
ctl, exist := svr.ctlManager.GetByID(newMsg.RunID) ctl, exist := svr.ctlManager.GetByID(newMsg.RunID)
if !exist { if !exist {
return fmt.Errorf("no client control found for run id [%s]", newMsg.RunID) return fmt.Errorf("[HayFrp] 未找到运行ID为 [%s] 的客户端控件", newMsg.RunID)
} }
visitorUser = ctl.loginMsg.User visitorUser = ctl.loginMsg.User
} }
return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey, return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
newMsg.UseEncryption, newMsg.UseCompression, visitorUser) newMsg.UseEncryption, newMsg.UseCompression, visitorUser)
} }
func (svr *Service) CloseUser(user string) error {
ctl, ok := svr.ctlManager.SearchByID(user)
if !ok {
return fmt.Errorf("[HayFrp] 客户端用户没有登录")
}
ctl.conn.Close()
return nil
}