Compare commits

..

19 Commits

Author SHA1 Message Date
拾光,
fe4ca1b54e Update README_zh.md (#4421)
修复爱发电链接无法访问问题
2024-09-06 11:41:11 +08:00
crystalstall
edd7cf8967 chore: fix function name (#4416)
Signed-off-by: crystalstall <crystalruby@qq.com>
2024-09-06 11:39:22 +08:00
Wang Xiang
03c8d7bf96 bump kcp-go to add linux/loong64 support (#4384) 2024-08-16 22:24:27 +08:00
fatedier
2dcdb24cc4 replace github.com/templexxx/xorsimd to the new version (#4373) 2024-08-07 11:18:17 +08:00
Wang Xiang
d47e138bc9 bump templexxx/cpu version and add support for linux/loong64 (#4367)
* support linux/loong64

* bump cpu version
2024-08-06 17:33:14 +08:00
fatedier
f1fb2d721a update .github/FUNDING.yml (#4365) 2024-08-02 17:04:59 +08:00
fatedier
ae73ec2fed added a 30s timeout for frpc subcommands to avoid long delays (#4359) 2024-07-30 18:12:22 +08:00
Yurun
e8045194cd Fix loginFailExit = false bug (#4354)
* Fixed the issue that when loginFailExit = false, the frpc stop command cannot be stopped correctly if the server is not successfully connected after startup

* Update Release.md
2024-07-30 11:19:26 +08:00
fatedier
69cc422edf client plugin: added plugin tls2raw (#4341) 2024-07-25 14:28:17 +08:00
fatedier
b4d5d8c756 plugin https2http&https2https: return 421 if host not match sni (#4323) 2024-07-09 10:50:16 +08:00
fatedier
c6f9d8d403 update sponsors (#4303) 2024-06-26 14:51:34 +08:00
fatedier
939c490768 Add http2http client plugin with hostHeaderRewrite and requestHeaders support (#4275) 2024-06-12 17:30:10 +08:00
fatedier
f390e4a401 add sponsor (#4265) 2024-06-05 16:53:29 +08:00
fatedier
77990c31ef fix ini configuration default values (#4250) 2024-05-30 10:36:30 +08:00
fatedier
e680acf42d android: only use google dns server when the default dns server cannot be obtained (#4236) 2024-05-23 16:09:58 +08:00
fatedier
522e2c94c1 config: return error if plugin type is empty (#4235) 2024-05-23 14:52:12 +08:00
fatedier
301515d2e8 update the default value of transport.tcpMuxKeepaliveInterval (#4231) 2024-05-21 12:00:35 +08:00
fatedier
f0442d0cd5 plugin: fix http2 not enabled for https2http and https2https plugin (#4230) 2024-05-21 11:26:52 +08:00
fatedier
9ced717d69 update build-and-push-image.yml (#4206) 2024-05-07 19:14:09 +08:00
49 changed files with 594 additions and 204 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,4 +1,4 @@
# These are supported funding model platforms # These are supported funding model platforms
github: [fatedier] github: [fatedier]
custom: ["https://afdian.net/a/fatedier"] custom: ["https://afdian.com/a/fatedier"]

View File

@@ -2,7 +2,7 @@ name: Build Image and Publish to Dockerhub & GPR
on: on:
release: release:
types: [ created ] types: [ published ]
workflow_dispatch: workflow_dispatch:
inputs: inputs:
tag: tag:
@@ -61,7 +61,7 @@ jobs:
echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
- name: Build and push frpc - name: Build and push frpc
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
file: ./dockerfiles/Dockerfile-for-frpc file: ./dockerfiles/Dockerfile-for-frpc

View File

@@ -88,6 +88,7 @@ linters-settings:
excludes: excludes:
- G401 - G401
- G402 - G402
- G404
- G501 - G501
issues: issues:

View File

@@ -2,7 +2,7 @@ export PATH := $(PATH):`go env GOPATH`/bin
export GO111MODULE=on export GO111MODULE=on
LDFLAGS := -s -w LDFLAGS := -s -w
os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm:7 linux:arm:5 linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 android:arm64 os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm:7 linux:arm:5 linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 linux:loong64 android:arm64
all: build all: build

View File

@@ -11,11 +11,17 @@
<!--gold sponsors start--> <!--gold sponsors start-->
<p align="center"> <p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank"> <a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="350px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png"> <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
</a> </a>
<a>&nbsp</a> </p>
<p align="center">
<a href="https://github.com/daytonaio/daytona" target="_blank"> <a href="https://github.com/daytonaio/daytona" target="_blank">
<img width="360px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png"> <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
</a>
</p>
<p align="center">
<a href="https://github.com/beclab/terminus" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_terminusos.jpeg">
</a> </a>
</p> </p>
<!--gold sponsors end--> <!--gold sponsors end-->

View File

@@ -13,11 +13,17 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP
<!--gold sponsors start--> <!--gold sponsors start-->
<p align="center"> <p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank"> <a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="350px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png"> <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
</a> </a>
<a>&nbsp</a> </p>
<p align="center">
<a href="https://github.com/daytonaio/daytona" target="_blank"> <a href="https://github.com/daytonaio/daytona" target="_blank">
<img width="360px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png"> <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
</a>
</p>
<p align="center">
<a href="https://github.com/beclab/terminus" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_terminusos.jpeg">
</a> </a>
</p> </p>
<!--gold sponsors end--> <!--gold sponsors end-->
@@ -89,7 +95,7 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。 您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。
国内用户可以通过 [爱发电](https://afdian.net/a/fatedier) 赞助我们。 国内用户可以通过 [爱发电](https://afdian.com/a/fatedier) 赞助我们。
企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。 企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。

View File

@@ -1,14 +1,8 @@
### Notable Changes
We have optimized the heartbeat mechanism when tcpmux is enabled (enabled by default). The default value of `heartbeatInterval` has been adjusted to -1. This update ensures that when tcpmux is active, the client does not send additional heartbeats to the server. Since tcpmux incorporates its own heartbeat system, this change effectively reduces unnecessary data consumption, streamlining communication efficiency between client and server.
When connecting to frps versions older than v0.39.0 might encounter compatibility issues due to changes in the heartbeat mechanism. As a temporary workaround, setting the `heartbeatInterval` to 30 can help maintain stable connectivity with these older versions. We recommend updating to the latest frps version to leverage full functionality and improvements.
### Features ### Features
* Show tcpmux proxies on the frps dashboard. * Added a new plugin `tls2raw`: Enables TLS termination and forwarding of decrypted raw traffic to local service.
* `http` proxy can modify the response header. For example, `responseHeaders.set.foo = "bar"` will add a new header `foo: bar` to the response. * Added a default timeout of 30 seconds for the frpc subcommands to prevent commands from being stuck for a long time due to network issues.
### Fixes ### Fixes
* When an HTTP proxy request times out, it returns 504 instead of 404 now. * Fixed the issue that when `loginFailExit = false`, the frpc stop command cannot be stopped correctly if the server is not successfully connected after startup.

View File

@@ -230,7 +230,7 @@ func (ctl *Control) registerMsgHandlers() {
ctl.msgDispatcher.RegisterHandler(&msg.Pong{}, ctl.handlePong) ctl.msgDispatcher.RegisterHandler(&msg.Pong{}, ctl.handlePong)
} }
// headerWorker sends heartbeat to server and check heartbeat timeout. // heartbeatWorker sends heartbeat to server and check heartbeat timeout.
func (ctl *Control) heartbeatWorker() { func (ctl *Control) heartbeatWorker() {
xl := ctl.xl xl := ctl.xl

View File

@@ -192,7 +192,7 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
if pxy.proxyPlugin != nil { if pxy.proxyPlugin != nil {
// if plugin is set, let plugin handle connection first // if plugin is set, let plugin handle connection first
xl.Debugf("handle by plugin: %s", pxy.proxyPlugin.Name()) xl.Debugf("handle by plugin: %s", pxy.proxyPlugin.Name())
pxy.proxyPlugin.Handle(remote, workConn, &extraInfo) pxy.proxyPlugin.Handle(pxy.ctx, remote, workConn, &extraInfo)
xl.Debugf("handle by plugin finished") xl.Debugf("handle by plugin finished")
return return
} }

View File

@@ -169,6 +169,15 @@ func (svr *Service) Run(ctx context.Context) error {
netpkg.SetDefaultDNSAddress(svr.common.DNSServer) netpkg.SetDefaultDNSAddress(svr.common.DNSServer)
} }
if svr.webServer != nil {
go func() {
log.Infof("admin server listen on %s", svr.webServer.Address())
if err := svr.webServer.Run(); err != nil {
log.Warnf("admin server exit with error: %v", err)
}
}()
}
// first login to frps // first login to frps
svr.loopLoginUntilSuccess(10*time.Second, lo.FromPtr(svr.common.LoginFailExit)) svr.loopLoginUntilSuccess(10*time.Second, lo.FromPtr(svr.common.LoginFailExit))
if svr.ctl == nil { if svr.ctl == nil {
@@ -179,14 +188,6 @@ func (svr *Service) Run(ctx context.Context) error {
go svr.keepControllerWorking() go svr.keepControllerWorking()
if svr.webServer != nil {
go func() {
log.Infof("admin server listen on %s", svr.webServer.Address())
if err := svr.webServer.Run(); err != nil {
log.Warnf("admin server exit with error: %v", err)
}
}()
}
<-svr.ctx.Done() <-svr.ctx.Done()
svr.stop() svr.stop()
return nil return nil

View File

@@ -15,9 +15,11 @@
package sub package sub
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"strings" "strings"
"time"
"github.com/rodaine/table" "github.com/rodaine/table"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -27,24 +29,24 @@ import (
clientsdk "github.com/fatedier/frp/pkg/sdk/client" clientsdk "github.com/fatedier/frp/pkg/sdk/client"
) )
var adminAPITimeout = 30 * time.Second
func init() { func init() {
rootCmd.AddCommand(NewAdminCommand( commands := []struct {
"reload", name string
"Hot-Reload frpc configuration", description string
ReloadHandler, handler func(*v1.ClientCommonConfig) error
)) }{
{"reload", "Hot-Reload frpc configuration", ReloadHandler},
{"status", "Overview of all proxies status", StatusHandler},
{"stop", "Stop the running frpc", StopHandler},
}
rootCmd.AddCommand(NewAdminCommand( for _, cmdConfig := range commands {
"status", cmd := NewAdminCommand(cmdConfig.name, cmdConfig.description, cmdConfig.handler)
"Overview of all proxies status", cmd.Flags().DurationVar(&adminAPITimeout, "api-timeout", adminAPITimeout, "Timeout for admin API calls")
StatusHandler, rootCmd.AddCommand(cmd)
)) }
rootCmd.AddCommand(NewAdminCommand(
"stop",
"Stop the running frpc",
StopHandler,
))
} }
func NewAdminCommand(name, short string, handler func(*v1.ClientCommonConfig) error) *cobra.Command { func NewAdminCommand(name, short string, handler func(*v1.ClientCommonConfig) error) *cobra.Command {
@@ -73,7 +75,9 @@ func NewAdminCommand(name, short string, handler func(*v1.ClientCommonConfig) er
func ReloadHandler(clientCfg *v1.ClientCommonConfig) error { func ReloadHandler(clientCfg *v1.ClientCommonConfig) error {
client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port) client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port)
client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password) client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password)
if err := client.Reload(strictConfigMode); err != nil { ctx, cancel := context.WithTimeout(context.Background(), adminAPITimeout)
defer cancel()
if err := client.Reload(ctx, strictConfigMode); err != nil {
return err return err
} }
fmt.Println("reload success") fmt.Println("reload success")
@@ -83,7 +87,9 @@ func ReloadHandler(clientCfg *v1.ClientCommonConfig) error {
func StatusHandler(clientCfg *v1.ClientCommonConfig) error { func StatusHandler(clientCfg *v1.ClientCommonConfig) error {
client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port) client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port)
client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password) client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password)
res, err := client.GetAllProxyStatus() ctx, cancel := context.WithTimeout(context.Background(), adminAPITimeout)
defer cancel()
res, err := client.GetAllProxyStatus(ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -109,7 +115,9 @@ func StatusHandler(clientCfg *v1.ClientCommonConfig) error {
func StopHandler(clientCfg *v1.ClientCommonConfig) error { func StopHandler(clientCfg *v1.ClientCommonConfig) error {
client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port) client := clientsdk.New(clientCfg.WebServer.Addr, clientCfg.WebServer.Port)
client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password) client.SetAuth(clientCfg.WebServer.User, clientCfg.WebServer.Password)
if err := client.Stop(); err != nil { ctx, cancel := context.WithTimeout(context.Background(), adminAPITimeout)
defer cancel()
if err := client.Stop(ctx); err != nil {
return err return err
} }
fmt.Println("stop success") fmt.Println("stop success")

View File

@@ -76,7 +76,7 @@ transport.poolCount = 5
# Specify keep alive interval for tcp mux. # Specify keep alive interval for tcp mux.
# only valid if tcpMux is enabled. # only valid if tcpMux is enabled.
# transport.tcpMuxKeepaliveInterval = 60 # transport.tcpMuxKeepaliveInterval = 30
# Communication protocol used to connect to server # Communication protocol used to connect to server
# supports tcp, kcp, quic, websocket and wss now, default is tcp # supports tcp, kcp, quic, websocket and wss now, default is tcp
@@ -315,6 +315,26 @@ localAddr = "127.0.0.1:443"
hostHeaderRewrite = "127.0.0.1" hostHeaderRewrite = "127.0.0.1"
requestHeaders.set.x-from-where = "frp" requestHeaders.set.x-from-where = "frp"
[[proxies]]
name = "plugin_http2http"
type = "tcp"
remotePort = 6007
[proxies.plugin]
type = "http2http"
localAddr = "127.0.0.1:80"
hostHeaderRewrite = "127.0.0.1"
requestHeaders.set.x-from-where = "frp"
[[proxies]]
name = "plugin_tls2raw"
type = "https"
remotePort = 6008
[proxies.plugin]
type = "tls2raw"
localAddr = "127.0.0.1:80"
crtPath = "./server.crt"
keyPath = "./server.key"
[[proxies]] [[proxies]]
name = "secret_tcp" name = "secret_tcp"
# If the type is secret tcp, remotePort is useless # If the type is secret tcp, remotePort is useless

View File

@@ -34,7 +34,7 @@ transport.maxPoolCount = 5
# Specify keep alive interval for tcp mux. # Specify keep alive interval for tcp mux.
# only valid if tcpMux is true. # only valid if tcpMux is true.
# transport.tcpMuxKeepaliveInterval = 60 # transport.tcpMuxKeepaliveInterval = 30
# tcpKeepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps. # tcpKeepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
# If negative, keep-alive probes are disabled. # If negative, keep-alive probes are disabled.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

BIN
doc/pic/sponsor_lokal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

7
go.mod
View File

@@ -23,7 +23,7 @@ require (
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.17.1 github.com/tidwall/gjson v1.17.1
github.com/xtaci/kcp-go/v5 v5.6.8 github.com/xtaci/kcp-go/v5 v5.6.13
golang.org/x/crypto v0.22.0 golang.org/x/crypto v0.22.0
golang.org/x/net v0.24.0 golang.org/x/net v0.24.0
golang.org/x/oauth2 v0.16.0 golang.org/x/oauth2 v0.16.0
@@ -59,9 +59,8 @@ require (
github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect
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/templexxx/cpu v0.1.1 // indirect
github.com/templexxx/cpu v0.1.0 // indirect github.com/templexxx/xorsimd v0.4.3 // 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

16
go.sum
View File

@@ -116,8 +116,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rodaine/table v1.2.0 h1:38HEnwK4mKSHQJIkavVj+bst1TEY7j9zhLMWu4QJrMA= github.com/rodaine/table v1.2.0 h1:38HEnwK4mKSHQJIkavVj+bst1TEY7j9zhLMWu4QJrMA=
github.com/rodaine/table v1.2.0/go.mod h1:wejb/q/Yd4T/SVmBSRMr7GCq3KlcZp3gyNYdLSBhkaE= github.com/rodaine/table v1.2.0/go.mod h1:wejb/q/Yd4T/SVmBSRMr7GCq3KlcZp3gyNYdLSBhkaE=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
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=
@@ -136,10 +136,10 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40= github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI= github.com/templexxx/xorsimd v0.4.3 h1:9AQTFHd7Bhk3dIT7Al2XeBX5DWOvsUPZCuhyAtNbHjU=
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI= github.com/templexxx/xorsimd v0.4.3/go.mod h1:oZQcD6RFDisW2Am58dSAGwwL6rHjbzrlu25VDqfWkQg=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -148,8 +148,8 @@ 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/xtaci/kcp-go/v5 v5.6.8 h1:jlI/0jAyjoOjT/SaGB58s4bQMJiNS41A2RKzR6TMWeI= github.com/xtaci/kcp-go/v5 v5.6.13 h1:FEjtz9+D4p8t2x4WjciGt/jsIuhlWjjgPCCWjrVR4Hk=
github.com/xtaci/kcp-go/v5 v5.6.8/go.mod h1:oE9j2NVqAkuKO5o8ByKGch3vgVX3BNf8zqP8JiGq0bM= github.com/xtaci/kcp-go/v5 v5.6.13/go.mod h1:75S1AKYYzNUSXIv30h+jPKJYZUwqpfvLshu63nCNSOM=
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=

View File

@@ -18,7 +18,7 @@ rm -rf ./release/packages
mkdir -p ./release/packages mkdir -p ./release/packages
os_all='linux windows darwin freebsd android' os_all='linux windows darwin freebsd android'
arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle riscv64' arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle riscv64 loong64'
extra_all='_ hf' extra_all='_ hf'
cd ./release cd ./release

View File

@@ -345,35 +345,19 @@ func copySection(source, target *ini.Section) {
} }
// GetDefaultClientConf returns a client configuration with default values. // GetDefaultClientConf returns a client configuration with default values.
// Note: Some default values here will be set to empty and will be converted to them
// new configuration through the 'Complete' function to set them as the default
// values of the new configuration.
func GetDefaultClientConf() ClientCommonConf { func GetDefaultClientConf() ClientCommonConf {
return ClientCommonConf{ return ClientCommonConf{
ClientConfig: legacyauth.GetDefaultClientConf(), ClientConfig: legacyauth.GetDefaultClientConf(),
ServerAddr: "0.0.0.0",
ServerPort: 7000,
NatHoleSTUNServer: "stun.easyvoip.com:3478",
DialServerTimeout: 10,
DialServerKeepAlive: 7200,
HTTPProxy: os.Getenv("http_proxy"),
LogFile: "console",
LogWay: "console",
LogLevel: "info",
LogMaxDays: 3,
AdminAddr: "127.0.0.1",
PoolCount: 1,
TCPMux: true, TCPMux: true,
TCPMuxKeepaliveInterval: 60,
LoginFailExit: true, LoginFailExit: true,
Start: make([]string, 0),
Protocol: "tcp", Protocol: "tcp",
QUICKeepalivePeriod: 10, Start: make([]string, 0),
QUICMaxIdleTimeout: 30,
QUICMaxIncomingStreams: 100000,
TLSEnable: true, TLSEnable: true,
DisableCustomTLSFirstByte: true, DisableCustomTLSFirstByte: true,
HeartbeatInterval: 30,
HeartbeatTimeout: 90,
Metas: make(map[string]string), Metas: make(map[string]string),
UDPPacketSize: 1500,
IncludeConfigFiles: make([]string, 0), IncludeConfigFiles: make([]string, 0),
} }
} }

View File

@@ -200,34 +200,20 @@ type ServerCommonConf struct {
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"`
} }
// GetDefaultServerConf returns a server configuration with reasonable // GetDefaultServerConf returns a server configuration with reasonable defaults.
// defaults. // Note: Some default values here will be set to empty and will be converted to them
// new configuration through the 'Complete' function to set them as the default
// values of the new configuration.
func GetDefaultServerConf() ServerCommonConf { func GetDefaultServerConf() ServerCommonConf {
return ServerCommonConf{ return ServerCommonConf{
ServerConfig: legacyauth.GetDefaultServerConf(), ServerConfig: legacyauth.GetDefaultServerConf(),
BindAddr: "0.0.0.0", DashboardAddr: "0.0.0.0",
BindPort: 7000, LogFile: "console",
QUICKeepalivePeriod: 10, LogWay: "console",
QUICMaxIdleTimeout: 30, DetailedErrorsToClient: true,
QUICMaxIncomingStreams: 100000, TCPMux: true,
VhostHTTPTimeout: 60, AllowPorts: make(map[int]struct{}),
DashboardAddr: "0.0.0.0", HTTPPlugins: make(map[string]HTTPPluginOptions),
LogFile: "console",
LogWay: "console",
LogLevel: "info",
LogMaxDays: 3,
DetailedErrorsToClient: true,
TCPMux: true,
TCPMuxKeepaliveInterval: 60,
TCPKeepAlive: 7200,
AllowPorts: make(map[int]struct{}),
MaxPoolCount: 5,
MaxPortsPerClient: 0,
HeartbeatTimeout: 90,
UserConnTimeout: 10,
HTTPPlugins: make(map[string]HTTPPluginOptions),
UDPPacketSize: 1500,
NatHoleAnalysisDataReserveHours: 7 * 24,
} }
} }

View File

@@ -135,7 +135,7 @@ func (c *ClientTransportConfig) Complete() {
c.ProxyURL = util.EmptyOr(c.ProxyURL, os.Getenv("http_proxy")) c.ProxyURL = util.EmptyOr(c.ProxyURL, os.Getenv("http_proxy"))
c.PoolCount = util.EmptyOr(c.PoolCount, 1) c.PoolCount = util.EmptyOr(c.PoolCount, 1)
c.TCPMux = util.EmptyOr(c.TCPMux, lo.ToPtr(true)) c.TCPMux = util.EmptyOr(c.TCPMux, lo.ToPtr(true))
c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 60) c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 30)
if lo.FromPtr(c.TCPMux) { if lo.FromPtr(c.TCPMux) {
// If TCPMux is enabled, heartbeat of application layer is unnecessary because we can rely on heartbeat in tcpmux. // If TCPMux is enabled, heartbeat of application layer is unnecessary because we can rely on heartbeat in tcpmux.
c.HeartbeatInterval = util.EmptyOr(c.HeartbeatInterval, -1) c.HeartbeatInterval = util.EmptyOr(c.HeartbeatInterval, -1)

View File

@@ -17,11 +17,18 @@ package v1
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"reflect" "reflect"
"github.com/samber/lo"
"github.com/fatedier/frp/pkg/util/util"
) )
type ClientPluginOptions interface{} type ClientPluginOptions interface {
Complete()
}
type TypedClientPluginOptions struct { type TypedClientPluginOptions struct {
Type string `json:"type"` Type string `json:"type"`
@@ -42,7 +49,7 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
c.Type = typeStruct.Type c.Type = typeStruct.Type
if c.Type == "" { if c.Type == "" {
return nil return errors.New("plugin type is empty")
} }
v, ok := clientPluginOptionsTypeMap[typeStruct.Type] v, ok := clientPluginOptionsTypeMap[typeStruct.Type]
@@ -63,14 +70,20 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
return nil return nil
} }
func (c *TypedClientPluginOptions) MarshalJSON() ([]byte, error) {
return json.Marshal(c.ClientPluginOptions)
}
const ( const (
PluginHTTP2HTTPS = "http2https" PluginHTTP2HTTPS = "http2https"
PluginHTTPProxy = "http_proxy" PluginHTTPProxy = "http_proxy"
PluginHTTPS2HTTP = "https2http" PluginHTTPS2HTTP = "https2http"
PluginHTTPS2HTTPS = "https2https" PluginHTTPS2HTTPS = "https2https"
PluginHTTP2HTTP = "http2http"
PluginSocks5 = "socks5" PluginSocks5 = "socks5"
PluginStaticFile = "static_file" PluginStaticFile = "static_file"
PluginUnixDomainSocket = "unix_domain_socket" PluginUnixDomainSocket = "unix_domain_socket"
PluginTLS2Raw = "tls2raw"
) )
var clientPluginOptionsTypeMap = map[string]reflect.Type{ var clientPluginOptionsTypeMap = map[string]reflect.Type{
@@ -78,9 +91,11 @@ var clientPluginOptionsTypeMap = map[string]reflect.Type{
PluginHTTPProxy: reflect.TypeOf(HTTPProxyPluginOptions{}), PluginHTTPProxy: reflect.TypeOf(HTTPProxyPluginOptions{}),
PluginHTTPS2HTTP: reflect.TypeOf(HTTPS2HTTPPluginOptions{}), PluginHTTPS2HTTP: reflect.TypeOf(HTTPS2HTTPPluginOptions{}),
PluginHTTPS2HTTPS: reflect.TypeOf(HTTPS2HTTPSPluginOptions{}), PluginHTTPS2HTTPS: reflect.TypeOf(HTTPS2HTTPSPluginOptions{}),
PluginHTTP2HTTP: reflect.TypeOf(HTTP2HTTPPluginOptions{}),
PluginSocks5: reflect.TypeOf(Socks5PluginOptions{}), PluginSocks5: reflect.TypeOf(Socks5PluginOptions{}),
PluginStaticFile: reflect.TypeOf(StaticFilePluginOptions{}), PluginStaticFile: reflect.TypeOf(StaticFilePluginOptions{}),
PluginUnixDomainSocket: reflect.TypeOf(UnixDomainSocketPluginOptions{}), PluginUnixDomainSocket: reflect.TypeOf(UnixDomainSocketPluginOptions{}),
PluginTLS2Raw: reflect.TypeOf(TLS2RawPluginOptions{}),
} }
type HTTP2HTTPSPluginOptions struct { type HTTP2HTTPSPluginOptions struct {
@@ -90,36 +105,61 @@ type HTTP2HTTPSPluginOptions struct {
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"` RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
} }
func (o *HTTP2HTTPSPluginOptions) Complete() {}
type HTTPProxyPluginOptions struct { type HTTPProxyPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
HTTPUser string `json:"httpUser,omitempty"` HTTPUser string `json:"httpUser,omitempty"`
HTTPPassword string `json:"httpPassword,omitempty"` HTTPPassword string `json:"httpPassword,omitempty"`
} }
func (o *HTTPProxyPluginOptions) Complete() {}
type HTTPS2HTTPPluginOptions struct { type HTTPS2HTTPPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"` LocalAddr string `json:"localAddr,omitempty"`
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"` HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"` RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
EnableHTTP2 *bool `json:"enableHTTP2,omitempty"`
CrtPath string `json:"crtPath,omitempty"` CrtPath string `json:"crtPath,omitempty"`
KeyPath string `json:"keyPath,omitempty"` KeyPath string `json:"keyPath,omitempty"`
} }
func (o *HTTPS2HTTPPluginOptions) Complete() {
o.EnableHTTP2 = util.EmptyOr(o.EnableHTTP2, lo.ToPtr(true))
}
type HTTPS2HTTPSPluginOptions struct { type HTTPS2HTTPSPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"` LocalAddr string `json:"localAddr,omitempty"`
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"` HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"` RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
EnableHTTP2 *bool `json:"enableHTTP2,omitempty"`
CrtPath string `json:"crtPath,omitempty"` CrtPath string `json:"crtPath,omitempty"`
KeyPath string `json:"keyPath,omitempty"` KeyPath string `json:"keyPath,omitempty"`
} }
func (o *HTTPS2HTTPSPluginOptions) Complete() {
o.EnableHTTP2 = util.EmptyOr(o.EnableHTTP2, lo.ToPtr(true))
}
type HTTP2HTTPPluginOptions struct {
Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"`
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
}
func (o *HTTP2HTTPPluginOptions) Complete() {}
type Socks5PluginOptions struct { type Socks5PluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
} }
func (o *Socks5PluginOptions) Complete() {}
type StaticFilePluginOptions struct { type StaticFilePluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
LocalPath string `json:"localPath,omitempty"` LocalPath string `json:"localPath,omitempty"`
@@ -128,7 +168,20 @@ type StaticFilePluginOptions struct {
HTTPPassword string `json:"httpPassword,omitempty"` HTTPPassword string `json:"httpPassword,omitempty"`
} }
func (o *StaticFilePluginOptions) Complete() {}
type UnixDomainSocketPluginOptions struct { type UnixDomainSocketPluginOptions struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
UnixPath string `json:"unixPath,omitempty"` UnixPath string `json:"unixPath,omitempty"`
} }
func (o *UnixDomainSocketPluginOptions) Complete() {}
type TLS2RawPluginOptions struct {
Type string `json:"type,omitempty"`
LocalAddr string `json:"localAddr,omitempty"`
CrtPath string `json:"crtPath,omitempty"`
KeyPath string `json:"keyPath,omitempty"`
}
func (o *TLS2RawPluginOptions) Complete() {}

View File

@@ -127,6 +127,10 @@ func (c *ProxyBaseConfig) Complete(namePrefix string) {
c.Name = lo.Ternary(namePrefix == "", "", namePrefix+".") + c.Name c.Name = lo.Ternary(namePrefix == "", "", namePrefix+".") + c.Name
c.LocalIP = util.EmptyOr(c.LocalIP, "127.0.0.1") c.LocalIP = util.EmptyOr(c.LocalIP, "127.0.0.1")
c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient) c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient)
if c.Plugin.ClientPluginOptions != nil {
c.Plugin.ClientPluginOptions.Complete()
}
} }
func (c *ProxyBaseConfig) MarshalToMsg(m *msg.NewProxy) { func (c *ProxyBaseConfig) MarshalToMsg(m *msg.NewProxy) {
@@ -195,6 +199,10 @@ func (c *TypedProxyConfig) UnmarshalJSON(b []byte) error {
return nil return nil
} }
func (c *TypedProxyConfig) MarshalJSON() ([]byte, error) {
return json.Marshal(c.ProxyConfigurer)
}
type ProxyConfigurer interface { type ProxyConfigurer interface {
Complete(namePrefix string) Complete(namePrefix string)
GetBaseConfig() *ProxyBaseConfig GetBaseConfig() *ProxyBaseConfig

View File

@@ -176,7 +176,7 @@ type ServerTransportConfig struct {
func (c *ServerTransportConfig) Complete() { func (c *ServerTransportConfig) Complete() {
c.TCPMux = util.EmptyOr(c.TCPMux, lo.ToPtr(true)) c.TCPMux = util.EmptyOr(c.TCPMux, lo.ToPtr(true))
c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 60) c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 30)
c.TCPKeepAlive = util.EmptyOr(c.TCPKeepAlive, 7200) c.TCPKeepAlive = util.EmptyOr(c.TCPKeepAlive, 7200)
c.MaxPoolCount = util.EmptyOr(c.MaxPoolCount, 5) c.MaxPoolCount = util.EmptyOr(c.MaxPoolCount, 5)
if lo.FromPtr(c.TCPMux) { if lo.FromPtr(c.TCPMux) {

View File

@@ -32,6 +32,8 @@ func ValidateClientPluginOptions(c v1.ClientPluginOptions) error {
return validateStaticFilePluginOptions(v) return validateStaticFilePluginOptions(v)
case *v1.UnixDomainSocketPluginOptions: case *v1.UnixDomainSocketPluginOptions:
return validateUnixDomainSocketPluginOptions(v) return validateUnixDomainSocketPluginOptions(v)
case *v1.TLS2RawPluginOptions:
return validateTLS2RawPluginOptions(v)
} }
return nil return nil
} }
@@ -70,3 +72,10 @@ func validateUnixDomainSocketPluginOptions(c *v1.UnixDomainSocketPluginOptions)
} }
return nil return nil
} }
func validateTLS2RawPluginOptions(c *v1.TLS2RawPluginOptions) error {
if c.LocalAddr == "" {
return errors.New("localAddr is required")
}
return nil
}

View File

@@ -120,6 +120,10 @@ func (c *TypedVisitorConfig) UnmarshalJSON(b []byte) error {
return nil return nil
} }
func (c *TypedVisitorConfig) MarshalJSON() ([]byte, error) {
return json.Marshal(c.VisitorConfigurer)
}
func NewVisitorConfigurerByType(t VisitorType) VisitorConfigurer { func NewVisitorConfigurerByType(t VisitorType) VisitorConfigurer {
v, ok := visitorConfigTypeMap[t] v, ok := visitorConfigTypeMap[t]
if !ok { if !ok {

View File

@@ -0,0 +1,94 @@
// Copyright 2024 The frp Authors
//
// 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.
//go:build !frps
package plugin
import (
"context"
"io"
stdlog "log"
"net"
"net/http"
"net/http/httputil"
"github.com/fatedier/golib/pool"
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/util/log"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
func init() {
Register(v1.PluginHTTP2HTTP, NewHTTP2HTTPPlugin)
}
type HTTP2HTTPPlugin struct {
opts *v1.HTTP2HTTPPluginOptions
l *Listener
s *http.Server
}
func NewHTTP2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.HTTP2HTTPPluginOptions)
listener := NewProxyListener()
p := &HTTP2HTTPPlugin{
opts: opts,
l: listener,
}
rp := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
req := r.Out
req.URL.Scheme = "http"
req.URL.Host = p.opts.LocalAddr
if p.opts.HostHeaderRewrite != "" {
req.Host = p.opts.HostHeaderRewrite
}
for k, v := range p.opts.RequestHeaders.Set {
req.Header.Set(k, v)
}
},
BufferPool: pool.NewBuffer(32 * 1024),
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
}
p.s = &http.Server{
Handler: rp,
ReadHeaderTimeout: 0,
}
go func() {
_ = p.s.Serve(listener)
}()
return p, nil
}
func (p *HTTP2HTTPPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = p.l.PutConn(wrapConn)
}
func (p *HTTP2HTTPPlugin) Name() string {
return v1.PluginHTTP2HTTP
}
func (p *HTTP2HTTPPlugin) Close() error {
return p.s.Close()
}

View File

@@ -17,6 +17,7 @@
package plugin package plugin
import ( import (
"context"
"crypto/tls" "crypto/tls"
"io" "io"
stdlog "log" stdlog "log"
@@ -88,7 +89,7 @@ func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
return p, nil return p, nil
} }
func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) { func (p *HTTP2HTTPSPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn) wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = p.l.PutConn(wrapConn) _ = p.l.PutConn(wrapConn)
} }

View File

@@ -18,6 +18,7 @@ package plugin
import ( import (
"bufio" "bufio"
"context"
"encoding/base64" "encoding/base64"
"io" "io"
"net" "net"
@@ -68,7 +69,7 @@ func (hp *HTTPProxy) Name() string {
return v1.PluginHTTPProxy return v1.PluginHTTPProxy
} }
func (hp *HTTPProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) { func (hp *HTTPProxy) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn) wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
sc, rd := libnet.NewSharedConn(wrapConn) sc, rd := libnet.NewSharedConn(wrapConn)

View File

@@ -17,6 +17,7 @@
package plugin package plugin
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
@@ -27,9 +28,11 @@ import (
"time" "time"
"github.com/fatedier/golib/pool" "github.com/fatedier/golib/pool"
"github.com/samber/lo"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/transport"
httppkg "github.com/fatedier/frp/pkg/util/http"
"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"
) )
@@ -71,44 +74,39 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
BufferPool: pool.NewBuffer(32 * 1024), BufferPool: pool.NewBuffer(32 * 1024),
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0), ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
} }
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.TLS != nil {
tlsServerName, _ := httppkg.CanonicalHost(r.TLS.ServerName)
host, _ := httppkg.CanonicalHost(r.Host)
if tlsServerName != "" && tlsServerName != host {
w.WriteHeader(http.StatusMisdirectedRequest)
return
}
}
rp.ServeHTTP(w, r)
})
p.s = &http.Server{ tlsConfig, err := transport.NewServerTLSConfig(p.opts.CrtPath, p.opts.KeyPath, "")
Handler: rp,
ReadHeaderTimeout: 60 * time.Second,
}
var (
tlsConfig *tls.Config
err error
)
if opts.CrtPath != "" || opts.KeyPath != "" {
tlsConfig, err = p.genTLSConfig()
} else {
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
tlsConfig.InsecureSkipVerify = true
}
if err != nil { if err != nil {
return nil, fmt.Errorf("gen TLS config error: %v", err) return nil, fmt.Errorf("gen TLS config error: %v", err)
} }
ln := tls.NewListener(listener, tlsConfig)
p.s = &http.Server{
Handler: handler,
ReadHeaderTimeout: 60 * time.Second,
TLSConfig: tlsConfig,
}
if !lo.FromPtr(opts.EnableHTTP2) {
p.s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
}
go func() { go func() {
_ = p.s.Serve(ln) _ = p.s.ServeTLS(listener, "", "")
}() }()
return p, nil return p, nil
} }
func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) { func (p *HTTPS2HTTPPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
cert, err := tls.LoadX509KeyPair(p.opts.CrtPath, p.opts.KeyPath)
if err != nil {
return nil, err
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
return config, nil
}
func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn) wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
if extra.SrcAddr != nil { if extra.SrcAddr != nil {
wrapConn.SetRemoteAddr(extra.SrcAddr) wrapConn.SetRemoteAddr(extra.SrcAddr)

View File

@@ -17,6 +17,7 @@
package plugin package plugin
import ( import (
"context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
@@ -27,9 +28,11 @@ import (
"time" "time"
"github.com/fatedier/golib/pool" "github.com/fatedier/golib/pool"
"github.com/samber/lo"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/transport"
httppkg "github.com/fatedier/frp/pkg/util/http"
"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"
) )
@@ -77,44 +80,39 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
BufferPool: pool.NewBuffer(32 * 1024), BufferPool: pool.NewBuffer(32 * 1024),
ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0), ErrorLog: stdlog.New(log.NewWriteLogger(log.WarnLevel, 2), "", 0),
} }
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.TLS != nil {
tlsServerName, _ := httppkg.CanonicalHost(r.TLS.ServerName)
host, _ := httppkg.CanonicalHost(r.Host)
if tlsServerName != "" && tlsServerName != host {
w.WriteHeader(http.StatusMisdirectedRequest)
return
}
}
rp.ServeHTTP(w, r)
})
p.s = &http.Server{ tlsConfig, err := transport.NewServerTLSConfig(p.opts.CrtPath, p.opts.KeyPath, "")
Handler: rp,
ReadHeaderTimeout: 60 * time.Second,
}
var (
tlsConfig *tls.Config
err error
)
if opts.CrtPath != "" || opts.KeyPath != "" {
tlsConfig, err = p.genTLSConfig()
} else {
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
tlsConfig.InsecureSkipVerify = true
}
if err != nil { if err != nil {
return nil, fmt.Errorf("gen TLS config error: %v", err) return nil, fmt.Errorf("gen TLS config error: %v", err)
} }
ln := tls.NewListener(listener, tlsConfig)
p.s = &http.Server{
Handler: handler,
ReadHeaderTimeout: 60 * time.Second,
TLSConfig: tlsConfig,
}
if !lo.FromPtr(opts.EnableHTTP2) {
p.s.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
}
go func() { go func() {
_ = p.s.Serve(ln) _ = p.s.ServeTLS(listener, "", "")
}() }()
return p, nil return p, nil
} }
func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) { func (p *HTTPS2HTTPSPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
cert, err := tls.LoadX509KeyPair(p.opts.CrtPath, p.opts.KeyPath)
if err != nil {
return nil, err
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
return config, nil
}
func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn) wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
if extra.SrcAddr != nil { if extra.SrcAddr != nil {
wrapConn.SetRemoteAddr(extra.SrcAddr) wrapConn.SetRemoteAddr(extra.SrcAddr)

View File

@@ -15,6 +15,7 @@
package plugin package plugin
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -57,7 +58,7 @@ type ExtraInfo struct {
type Plugin interface { type Plugin interface {
Name() string Name() string
Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) Handle(ctx context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo)
Close() error Close() error
} }

View File

@@ -17,6 +17,7 @@
package plugin package plugin
import ( import (
"context"
"io" "io"
"log" "log"
"net" "net"
@@ -50,7 +51,7 @@ func NewSocks5Plugin(options v1.ClientPluginOptions) (p Plugin, err error) {
return return
} }
func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) { func (sp *Socks5Plugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
defer conn.Close() defer conn.Close()
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn) wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = sp.Server.ServeConn(wrapConn) _ = sp.Server.ServeConn(wrapConn)

View File

@@ -17,6 +17,7 @@
package plugin package plugin
import ( import (
"context"
"io" "io"
"net" "net"
"net/http" "net/http"
@@ -69,7 +70,7 @@ func NewStaticFilePlugin(options v1.ClientPluginOptions) (Plugin, error) {
return sp, nil return sp, nil
} }
func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) { func (sp *StaticFilePlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn) wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = sp.l.PutConn(wrapConn) _ = sp.l.PutConn(wrapConn)
} }

View File

@@ -0,0 +1,83 @@
// Copyright 2024 The frp Authors
//
// 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.
//go:build !frps
package plugin
import (
"context"
"crypto/tls"
"io"
"net"
libio "github.com/fatedier/golib/io"
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport"
netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/xlog"
)
func init() {
Register(v1.PluginTLS2Raw, NewTLS2RawPlugin)
}
type TLS2RawPlugin struct {
opts *v1.TLS2RawPluginOptions
tlsConfig *tls.Config
}
func NewTLS2RawPlugin(options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.TLS2RawPluginOptions)
p := &TLS2RawPlugin{
opts: opts,
}
tlsConfig, err := transport.NewServerTLSConfig(p.opts.CrtPath, p.opts.KeyPath, "")
if err != nil {
return nil, err
}
p.tlsConfig = tlsConfig
return p, nil
}
func (p *TLS2RawPlugin) Handle(ctx context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
xl := xlog.FromContextSafe(ctx)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
tlsConn := tls.Server(wrapConn, p.tlsConfig)
if err := tlsConn.Handshake(); err != nil {
xl.Warnf("tls handshake error: %v", err)
return
}
rawConn, err := net.Dial("tcp", p.opts.LocalAddr)
if err != nil {
xl.Warnf("dial to local addr error: %v", err)
return
}
libio.Join(tlsConn, rawConn)
}
func (p *TLS2RawPlugin) Name() string {
return v1.PluginTLS2Raw
}
func (p *TLS2RawPlugin) Close() error {
return nil
}

View File

@@ -17,12 +17,14 @@
package plugin package plugin
import ( import (
"context"
"io" "io"
"net" "net"
libio "github.com/fatedier/golib/io" libio "github.com/fatedier/golib/io"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/util/xlog"
) )
func init() { func init() {
@@ -48,9 +50,11 @@ func NewUnixDomainSocketPlugin(options v1.ClientPluginOptions) (p Plugin, err er
return return
} }
func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, _ net.Conn, extra *ExtraInfo) { func (uds *UnixDomainSocketPlugin) Handle(ctx context.Context, conn io.ReadWriteCloser, _ net.Conn, extra *ExtraInfo) {
xl := xlog.FromContextSafe(ctx)
localConn, err := net.DialUnix("unix", nil, uds.UnixAddr) localConn, err := net.DialUnix("unix", nil, uds.UnixAddr)
if err != nil { if err != nil {
xl.Warnf("dial to uds %s error: %v", uds.UnixAddr, err)
return return
} }
if extra.ProxyProtocolHeader != nil { if extra.ProxyProtocolHeader != nil {

View File

@@ -1,6 +1,7 @@
package client package client
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@@ -31,8 +32,8 @@ func (c *Client) SetAuth(user, pwd string) {
c.authPwd = pwd c.authPwd = pwd
} }
func (c *Client) GetProxyStatus(name string) (*client.ProxyStatusResp, error) { func (c *Client) GetProxyStatus(ctx context.Context, name string) (*client.ProxyStatusResp, error) {
req, err := http.NewRequest("GET", "http://"+c.address+"/api/status", nil) req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/status", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -54,8 +55,8 @@ func (c *Client) GetProxyStatus(name string) (*client.ProxyStatusResp, error) {
return nil, fmt.Errorf("no proxy status found") return nil, fmt.Errorf("no proxy status found")
} }
func (c *Client) GetAllProxyStatus() (client.StatusResp, error) { func (c *Client) GetAllProxyStatus(ctx context.Context) (client.StatusResp, error) {
req, err := http.NewRequest("GET", "http://"+c.address+"/api/status", nil) req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/status", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -70,7 +71,7 @@ func (c *Client) GetAllProxyStatus() (client.StatusResp, error) {
return allStatus, nil return allStatus, nil
} }
func (c *Client) Reload(strictMode bool) error { func (c *Client) Reload(ctx context.Context, strictMode bool) error {
v := url.Values{} v := url.Values{}
if strictMode { if strictMode {
v.Set("strictConfig", "true") v.Set("strictConfig", "true")
@@ -79,7 +80,7 @@ func (c *Client) Reload(strictMode bool) error {
if len(v) > 0 { if len(v) > 0 {
queryStr = "?" + v.Encode() queryStr = "?" + v.Encode()
} }
req, err := http.NewRequest("GET", "http://"+c.address+"/api/reload"+queryStr, nil) req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/reload"+queryStr, nil)
if err != nil { if err != nil {
return err return err
} }
@@ -87,8 +88,8 @@ func (c *Client) Reload(strictMode bool) error {
return err return err
} }
func (c *Client) Stop() error { func (c *Client) Stop(ctx context.Context) error {
req, err := http.NewRequest("POST", "http://"+c.address+"/api/stop", nil) req, err := http.NewRequestWithContext(ctx, "POST", "http://"+c.address+"/api/stop", nil)
if err != nil { if err != nil {
return err return err
} }
@@ -96,16 +97,16 @@ func (c *Client) Stop() error {
return err return err
} }
func (c *Client) GetConfig() (string, error) { func (c *Client) GetConfig(ctx context.Context) (string, error) {
req, err := http.NewRequest("GET", "http://"+c.address+"/api/config", nil) req, err := http.NewRequestWithContext(ctx, "GET", "http://"+c.address+"/api/config", nil)
if err != nil { if err != nil {
return "", err return "", err
} }
return c.do(req) return c.do(req)
} }
func (c *Client) UpdateConfig(content string) error { func (c *Client) UpdateConfig(ctx context.Context, content string) error {
req, err := http.NewRequest("PUT", "http://"+c.address+"/api/config", strings.NewReader(content)) req, err := http.NewRequestWithContext(ctx, "PUT", "http://"+c.address+"/api/config", strings.NewReader(content))
if err != nil { if err != nil {
return err return err
} }

View File

@@ -59,8 +59,12 @@ func fixDNSResolver() {
// Note: If there are other methods to obtain the default DNS servers, the default DNS servers should be used preferentially. // Note: If there are other methods to obtain the default DNS servers, the default DNS servers should be used preferentially.
net.DefaultResolver = &net.Resolver{ net.DefaultResolver = &net.Resolver{
PreferGo: true, PreferGo: true,
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) { Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial(network, "8.8.8.8:53") if addr == "127.0.0.1:53" || addr == "[::1]:53" {
addr = "8.8.8.8:53"
}
var d net.Dialer
return d.DialContext(ctx, network, addr)
}, },
} }
} }

View File

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

View File

@@ -1,6 +1,7 @@
package basic package basic
import ( import (
"context"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
@@ -54,7 +55,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(p3Port).Ensure() framework.NewRequestExpect(f).Port(p3Port).Ensure()
client := f.APIClientForFrpc(adminPort) client := f.APIClientForFrpc(adminPort)
conf, err := client.GetConfig() conf, err := client.GetConfig(context.Background())
framework.ExpectNoError(err) framework.ExpectNoError(err)
newP2Port := f.AllocPort() newP2Port := f.AllocPort()
@@ -65,10 +66,10 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
newClientConf = newClientConf[:p3Index] newClientConf = newClientConf[:p3Index]
} }
err = client.UpdateConfig(newClientConf) err = client.UpdateConfig(context.Background(), newClientConf)
framework.ExpectNoError(err) framework.ExpectNoError(err)
err = client.Reload(true) err = client.Reload(context.Background(), true)
framework.ExpectNoError(err) framework.ExpectNoError(err)
time.Sleep(time.Second) time.Sleep(time.Second)
@@ -120,7 +121,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(testPort).Ensure() framework.NewRequestExpect(f).Port(testPort).Ensure()
client := f.APIClientForFrpc(adminPort) client := f.APIClientForFrpc(adminPort)
err := client.Stop() err := client.Stop(context.Background())
framework.ExpectNoError(err) framework.ExpectNoError(err)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)

View File

@@ -1,6 +1,7 @@
package basic package basic
import ( import (
"context"
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
@@ -101,7 +102,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
client := f.APIClientForFrpc(adminPort) client := f.APIClientForFrpc(adminPort)
// tcp random port // tcp random port
status, err := client.GetProxyStatus("tcp") status, err := client.GetProxyStatus(context.Background(), "tcp")
framework.ExpectNoError(err) framework.ExpectNoError(err)
_, portStr, err := net.SplitHostPort(status.RemoteAddr) _, portStr, err := net.SplitHostPort(status.RemoteAddr)
@@ -112,7 +113,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
framework.NewRequestExpect(f).Port(port).Ensure() framework.NewRequestExpect(f).Port(port).Ensure()
// udp random port // udp random port
status, err = client.GetProxyStatus("udp") status, err = client.GetProxyStatus(context.Background(), "udp")
framework.ExpectNoError(err) framework.ExpectNoError(err)
_, portStr, err = net.SplitHostPort(status.RemoteAddr) _, portStr, err = net.SplitHostPort(status.RemoteAddr)

View File

@@ -1,6 +1,7 @@
package basic package basic
import ( import (
"context"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
@@ -57,7 +58,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(p3Port).Ensure() framework.NewRequestExpect(f).Port(p3Port).Ensure()
client := f.APIClientForFrpc(adminPort) client := f.APIClientForFrpc(adminPort)
conf, err := client.GetConfig() conf, err := client.GetConfig(context.Background())
framework.ExpectNoError(err) framework.ExpectNoError(err)
newP2Port := f.AllocPort() newP2Port := f.AllocPort()
@@ -68,10 +69,10 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
newClientConf = newClientConf[:p3Index] newClientConf = newClientConf[:p3Index]
} }
err = client.UpdateConfig(newClientConf) err = client.UpdateConfig(context.Background(), newClientConf)
framework.ExpectNoError(err) framework.ExpectNoError(err)
err = client.Reload(true) err = client.Reload(context.Background(), true)
framework.ExpectNoError(err) framework.ExpectNoError(err)
time.Sleep(time.Second) time.Sleep(time.Second)
@@ -124,7 +125,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(testPort).Ensure() framework.NewRequestExpect(f).Port(testPort).Ensure()
client := f.APIClientForFrpc(adminPort) client := f.APIClientForFrpc(adminPort)
err := client.Stop() err := client.Stop(context.Background())
framework.ExpectNoError(err) framework.ExpectNoError(err)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)

View File

@@ -1,6 +1,7 @@
package basic package basic
import ( import (
"context"
"fmt" "fmt"
"github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2"
@@ -72,7 +73,7 @@ var _ = ginkgo.Describe("[Feature: Config]", func() {
client := f.APIClientForFrpc(adminPort) client := f.APIClientForFrpc(adminPort)
checkProxyFn := func(name string, localPort, remotePort int) { checkProxyFn := func(name string, localPort, remotePort int) {
status, err := client.GetProxyStatus(name) status, err := client.GetProxyStatus(context.Background(), name)
framework.ExpectNoError(err) framework.ExpectNoError(err)
framework.ExpectContainSubstring(status.LocalAddr, fmt.Sprintf(":%d", localPort)) framework.ExpectContainSubstring(status.LocalAddr, fmt.Sprintf(":%d", localPort))

View File

@@ -1,6 +1,7 @@
package basic package basic
import ( import (
"context"
"fmt" "fmt"
"net" "net"
"strconv" "strconv"
@@ -112,7 +113,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
client := f.APIClientForFrpc(adminPort) client := f.APIClientForFrpc(adminPort)
// tcp random port // tcp random port
status, err := client.GetProxyStatus("tcp") status, err := client.GetProxyStatus(context.Background(), "tcp")
framework.ExpectNoError(err) framework.ExpectNoError(err)
_, portStr, err := net.SplitHostPort(status.RemoteAddr) _, portStr, err := net.SplitHostPort(status.RemoteAddr)
@@ -123,7 +124,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
framework.NewRequestExpect(f).Port(port).Ensure() framework.NewRequestExpect(f).Port(port).Ensure()
// udp random port // udp random port
status, err = client.GetProxyStatus("udp") status, err = client.GetProxyStatus(context.Background(), "udp")
framework.ExpectNoError(err) framework.ExpectNoError(err)
_, portStr, err = net.SplitHostPort(status.RemoteAddr) _, portStr, err = net.SplitHostPort(status.RemoteAddr)

View File

@@ -3,6 +3,7 @@ package plugin
import ( import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net/http"
"strconv" "strconv"
"github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2"
@@ -329,4 +330,122 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
ExpectResp([]byte("test")). ExpectResp([]byte("test")).
Ensure() Ensure()
}) })
ginkgo.Describe("http2http", func() {
ginkgo.It("host header rewrite", func() {
serverConf := consts.DefaultServerConfig
localPort := f.AllocPort()
remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "http2http"
type = "tcp"
remotePort = %d
[proxies.plugin]
type = "http2http"
localAddr = "127.0.0.1:%d"
hostHeaderRewrite = "rewrite.test.com"
`, remotePort, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte(req.Host))
})),
)
f.RunServer("", localServer)
framework.NewRequestExpect(f).
Port(remotePort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("example.com")
}).
ExpectResp([]byte("rewrite.test.com")).
Ensure()
})
ginkgo.It("set request header", func() {
serverConf := consts.DefaultServerConfig
localPort := f.AllocPort()
remotePort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "http2http"
type = "tcp"
remotePort = %d
[proxies.plugin]
type = "http2http"
localAddr = "127.0.0.1:%d"
requestHeaders.set.x-from-where = "frp"
`, remotePort, localPort)
f.RunProcesses([]string{serverConf}, []string{clientConf})
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte(req.Header.Get("x-from-where")))
})),
)
f.RunServer("", localServer)
framework.NewRequestExpect(f).
Port(remotePort).
RequestModify(func(r *request.Request) {
r.HTTP().HTTPHost("example.com")
}).
ExpectResp([]byte("frp")).
Ensure()
})
})
ginkgo.It("tls2raw", func() {
generator := &cert.SelfSignedCertGenerator{}
artifacts, err := generator.Generate("example.com")
framework.ExpectNoError(err)
crtPath := f.WriteTempFile("tls2raw_server.crt", string(artifacts.Cert))
keyPath := f.WriteTempFile("tls2raw_server.key", string(artifacts.Key))
serverConf := consts.DefaultServerConfig
vhostHTTPSPort := f.AllocPort()
serverConf += fmt.Sprintf(`
vhostHTTPSPort = %d
`, vhostHTTPSPort)
localPort := f.AllocPort()
clientConf := consts.DefaultClientConfig + fmt.Sprintf(`
[[proxies]]
name = "tls2raw-test"
type = "https"
customDomains = ["example.com"]
[proxies.plugin]
type = "tls2raw"
localAddr = "127.0.0.1:%d"
crtPath = "%s"
keyPath = "%s"
`, localPort, crtPath, keyPath)
f.RunProcesses([]string{serverConf}, []string{clientConf})
localServer := httpserver.New(
httpserver.WithBindPort(localPort),
httpserver.WithResponse([]byte("test")),
)
f.RunServer("", localServer)
framework.NewRequestExpect(f).
Port(vhostHTTPSPort).
RequestModify(func(r *request.Request) {
r.HTTPS().HTTPHost("example.com").TLSConfig(&tls.Config{
ServerName: "example.com",
InsecureSkipVerify: true,
})
}).
ExpectResp([]byte("test")).
Ensure()
})
}) })