Skip to content

Commit

Permalink
Merge branch 'fatedier:dev' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
aiastia authored May 29, 2024
2 parents c893325 + e680acf commit 491dc72
Show file tree
Hide file tree
Showing 54 changed files with 628 additions and 371 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-and-push-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Build Image and Publish to Dockerhub & GPR

on:
release:
types: [ created ]
types: [ published ]
workflow_dispatch:
inputs:
tag:
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
- name: Build and push frpc
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
file: ./dockerfiles/Dockerfile-for-frpc
Expand Down
6 changes: 1 addition & 5 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
service:
golangci-lint-version: 1.57.x # use the fixed version to not introduce new linters unexpectedly

run:
concurrency: 4
# timeout for analysis, e.g. 30s, 5m, default is 1m
Expand Down Expand Up @@ -86,12 +86,8 @@ linters-settings:
severity: "low"
confidence: "low"
excludes:
- G102
- G112
- G306
- G401
- G402
- G404
- G501

issues:
Expand Down
32 changes: 22 additions & 10 deletions Makefile.cross-compiles
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,34 @@ export PATH := $(PATH):`go env GOPATH`/bin
export GO111MODULE=on
LDFLAGS := -s -w

os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm 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 android:arm64

all: build

build: app

app:
@$(foreach n, $(os-archs),\
os=$(shell echo "$(n)" | cut -d : -f 1);\
arch=$(shell echo "$(n)" | cut -d : -f 2);\
gomips=$(shell echo "$(n)" | cut -d : -f 3);\
target_suffix=$${os}_$${arch};\
echo "Build $${os}-$${arch}...";\
env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -tags frpc -o ./release/frpc_$${target_suffix} ./cmd/frpc;\
env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -tags frps -o ./release/frps_$${target_suffix} ./cmd/frps;\
echo "Build $${os}-$${arch} done";\
@$(foreach n, $(os-archs), \
os=$(shell echo "$(n)" | cut -d : -f 1); \
arch=$(shell echo "$(n)" | cut -d : -f 2); \
extra=$(shell echo "$(n)" | cut -d : -f 3); \
flags=''; \
target_suffix=$${os}_$${arch}; \
if [ "$${os}" = "linux" ] && [ "$${arch}" = "arm" ] && [ "$${extra}" != "" ] ; then \
if [ "$${extra}" = "7" ]; then \
flags=GOARM=7; \
target_suffix=$${os}_arm_hf; \
elif [ "$${extra}" = "5" ]; then \
flags=GOARM=5; \
target_suffix=$${os}_arm; \
fi; \
elif [ "$${os}" = "linux" ] && ([ "$${arch}" = "mips" ] || [ "$${arch}" = "mipsle" ]) && [ "$${extra}" != "" ] ; then \
flags=GOMIPS=$${extra}; \
fi; \
echo "Build $${os}-$${arch}$${extra:+ ($${extra})}..."; \
env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} $${flags} go build -trimpath -ldflags "$(LDFLAGS)" -tags frpc -o ./release/frpc_$${target_suffix} ./cmd/frpc; \
env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} $${flags} go build -trimpath -ldflags "$(LDFLAGS)" -tags frps -o ./release/frps_$${target_suffix} ./cmd/frps; \
echo "Build $${os}-$${arch}$${extra:+ ($${extra})} done"; \
)
@mv ./release/frpc_windows_amd64 ./release/frpc_windows_amd64.exe
@mv ./release/frps_windows_amd64 ./release/frps_windows_amd64.exe
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ frp also offers a P2P connect mode.
* [Client Plugins](#client-plugins)
* [Server Manage Plugins](#server-manage-plugins)
* [SSH Tunnel Gateway](#ssh-tunnel-gateway)
* [Releated Projects](#releated-projects)
* [Contributing](#contributing)
* [Donation](#donation)
* [GitHub Sponsors](#github-sponsors)
Expand Down Expand Up @@ -351,7 +352,6 @@ You may substitute `https2https` for the plugin, and point the `localAddr` to a
# frpc.toml
serverAddr = "x.x.x.x"
serverPort = 7000
vhostHTTPSPort = 443

[[proxies]]
name = "test_https2http"
Expand Down Expand Up @@ -804,7 +804,7 @@ You can disable this feature by modify `frps.toml` and `frpc.toml`:

```toml
# frps.toml and frpc.toml, must be same
tcpMux = false
transport.tcpMux = false
```

### Support KCP Protocol
Expand Down Expand Up @@ -983,7 +983,7 @@ The HTTP request will have the `Host` header rewritten to `Host: dev.example.com

### Setting other HTTP Headers

Similar to `Host`, You can override other HTTP request headers with proxy type `http`.
Similar to `Host`, You can override other HTTP request and response headers with proxy type `http`.

```toml
# frpc.toml
Expand All @@ -995,15 +995,16 @@ localPort = 80
customDomains = ["test.example.com"]
hostHeaderRewrite = "dev.example.com"
requestHeaders.set.x-from-where = "frp"
responseHeaders.set.foo = "bar"
```

In this example, it will set header `x-from-where: frp` in the HTTP request.
In this example, it will set header `x-from-where: frp` in the HTTP request and `foo: bar` in the HTTP response.

### Get Real IP

#### HTTP X-Forwarded-For

This feature is for http proxy only.
This feature is for `http` proxies or proxies with the `https2http` and `https2https` plugins enabled.

You can get user's real IP from HTTP request headers `X-Forwarded-For`.

Expand Down Expand Up @@ -1244,6 +1245,11 @@ frpc tcp --proxy_name "test-tcp" --local_ip 127.0.0.1 --local_port 8080 --remote

Please refer to this [document](/doc/ssh_tunnel_gateway.md) for more information.

## Releated Projects

* [gofrp/plugin](https://github.com/gofrp/plugin) - A repository for frp plugins that contains a variety of plugins implemented based on the frp extension mechanism, meeting the customization needs of different scenarios.
* [gofrp/tiny-frpc](https://github.com/gofrp/tiny-frpc) - A lightweight version of the frp client (around 3.5MB at minimum) implemented using the ssh protocol, supporting some of the most commonly used features, suitable for devices with limited resources.

## Contributing

Interested in getting involved? We would like to help you!
Expand Down
11 changes: 6 additions & 5 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
* 贡献代码请提交 PR 至 dev 分支,master 分支仅用于发布稳定可用版本。
* 如果你有任何其他方面的问题或合作,欢迎发送邮件至 [email protected]

**提醒:和项目相关的问题最好在 [issues](https://github.com/fatedier/frp/issues) 中反馈,这样方便其他有类似问题的人可以快速查找解决方法,并且也避免了我们重复回答一些问题。**
**提醒:和项目相关的问题请在 [issues](https://github.com/fatedier/frp/issues) 中反馈,这样方便其他有类似问题的人可以快速查找解决方法,并且也避免了我们重复回答一些问题。**

## 关联项目

* [gofrp/plugin](https://github.com/gofrp/plugin) - frp 插件仓库,收录了基于 frp 扩展机制实现的各种插件,满足各种场景下的定制化需求。
* [gofrp/tiny-frpc](https://github.com/gofrp/tiny-frpc) - 基于 ssh 协议实现的 frp 客户端的精简版本(最低约 3.5MB 左右),支持常用的部分功能,适用于资源有限的设备。

## 赞助

Expand All @@ -93,7 +98,3 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
如果您想了解更多 frp 相关技术以及更新详解,或者寻求任何 frp 使用方面的帮助,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:

![zsxq](/doc/pic/zsxq.jpg)

### 微信支付捐赠

![donate-wechatpay](/doc/pic/donate-wechatpay.png)
9 changes: 5 additions & 4 deletions Release.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
### Features
### Fixes

* `https2http` and `https2https` plugin now supports `X-Forwared-For` header.
* Fixed an issue where HTTP/2 was not enabled for https2http and https2https plugins.

### Fixes
### Changes

* `X-Forwared-For` header is now correctly set in the request to the backend server for proxy type http.
* Updated the default value of `transport.tcpMuxKeepaliveInterval` from 60 to 30.
* On the Android platform, the Google DNS server is used only when the default DNS server cannot be obtained.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/frps/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<head>
<meta charset="utf-8">
<title>frps dashboard</title>
<script type="module" crossorigin src="./index-Q42Pu2_S.js"></script>
<script type="module" crossorigin src="./index-82-40HIG.js"></script>
<link rel="stylesheet" crossorigin href="./index-rzPDshRD.css">
</head>

Expand Down
2 changes: 1 addition & 1 deletion client/admin_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
return
}

if err := os.WriteFile(svr.configFilePath, body, 0o644); err != nil {
if err := os.WriteFile(svr.configFilePath, body, 0o600); err != nil {
res.Code = 500
res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
log.Warnf("%s", res.Msg)
Expand Down
34 changes: 17 additions & 17 deletions client/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
"sync"
"time"

libdial "github.com/fatedier/golib/net/dial"
libnet "github.com/fatedier/golib/net"
fmux "github.com/hashicorp/yamux"
quic "github.com/quic-go/quic-go"
"github.com/samber/lo"
Expand Down Expand Up @@ -169,44 +169,44 @@ func (c *defaultConnectorImpl) realConnect() (net.Conn, error) {
}
}

proxyType, addr, auth, err := libdial.ParseProxyURL(c.cfg.Transport.ProxyURL)
proxyType, addr, auth, err := libnet.ParseProxyURL(c.cfg.Transport.ProxyURL)
if err != nil {
xl.Errorf("fail to parse proxy url")
return nil, err
}
dialOptions := []libdial.DialOption{}
dialOptions := []libnet.DialOption{}
protocol := c.cfg.Transport.Protocol
switch protocol {
case "websocket":
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: netpkg.DialHookWebsocket(protocol, "")}))
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{
dialOptions = append(dialOptions, libnet.WithAfterHook(libnet.AfterHook{Hook: netpkg.DialHookWebsocket(protocol, "")}))
dialOptions = append(dialOptions, libnet.WithAfterHook(libnet.AfterHook{
Hook: netpkg.DialHookCustomTLSHeadByte(tlsConfig != nil, lo.FromPtr(c.cfg.Transport.TLS.DisableCustomTLSFirstByte)),
}))
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
dialOptions = append(dialOptions, libnet.WithTLSConfig(tlsConfig))
case "wss":
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithTLSConfigAndPriority(100, tlsConfig))
dialOptions = append(dialOptions, libnet.WithTLSConfigAndPriority(100, tlsConfig))
// Make sure that if it is wss, the websocket hook is executed after the tls hook.
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: netpkg.DialHookWebsocket(protocol, tlsConfig.ServerName), Priority: 110}))
dialOptions = append(dialOptions, libnet.WithAfterHook(libnet.AfterHook{Hook: netpkg.DialHookWebsocket(protocol, tlsConfig.ServerName), Priority: 110}))
default:
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{
dialOptions = append(dialOptions, libnet.WithAfterHook(libnet.AfterHook{
Hook: netpkg.DialHookCustomTLSHeadByte(tlsConfig != nil, lo.FromPtr(c.cfg.Transport.TLS.DisableCustomTLSFirstByte)),
}))
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
dialOptions = append(dialOptions, libnet.WithTLSConfig(tlsConfig))
}

if c.cfg.Transport.ConnectServerLocalIP != "" {
dialOptions = append(dialOptions, libdial.WithLocalAddr(c.cfg.Transport.ConnectServerLocalIP))
dialOptions = append(dialOptions, libnet.WithLocalAddr(c.cfg.Transport.ConnectServerLocalIP))
}
dialOptions = append(dialOptions,
libdial.WithProtocol(protocol),
libdial.WithTimeout(time.Duration(c.cfg.Transport.DialServerTimeout)*time.Second),
libdial.WithKeepAlive(time.Duration(c.cfg.Transport.DialServerKeepAlive)*time.Second),
libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth),
libnet.WithProtocol(protocol),
libnet.WithTimeout(time.Duration(c.cfg.Transport.DialServerTimeout)*time.Second),
libnet.WithKeepAlive(time.Duration(c.cfg.Transport.DialServerKeepAlive)*time.Second),
libnet.WithProxy(proxyType, addr),
libnet.WithProxyAuth(auth),
)
conn, err := libdial.DialContext(
conn, err := libnet.DialContext(
c.ctx,
net.JoinHostPort(c.cfg.ServerAddr, strconv.Itoa(c.cfg.ServerPort)),
dialOptions...,
Expand Down
12 changes: 3 additions & 9 deletions client/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"sync/atomic"
"time"

"github.com/samber/lo"

"github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/client/visitor"
"github.com/fatedier/frp/pkg/auth"
Expand Down Expand Up @@ -236,10 +234,8 @@ func (ctl *Control) registerMsgHandlers() {
func (ctl *Control) heartbeatWorker() {
xl := ctl.xl

// TODO(fatedier): Change default value of HeartbeatInterval to -1 if tcpmux is enabled.
// Users can still enable heartbeat feature by setting HeartbeatInterval to a positive value.
if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 {
// send heartbeat to server
// Send heartbeat to server.
sendHeartBeat := func() (bool, error) {
xl.Debugf("send heartbeat to server")
pingMsg := &msg.Ping{}
Expand All @@ -263,10 +259,8 @@ func (ctl *Control) heartbeatWorker() {
)
}

// Check heartbeat timeout only if TCPMux is not enabled and users don't disable heartbeat feature.
if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 && ctl.sessionCtx.Common.Transport.HeartbeatTimeout > 0 &&
!lo.FromPtr(ctl.sessionCtx.Common.Transport.TCPMux) {

// Check heartbeat timeout.
if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 && ctl.sessionCtx.Common.Transport.HeartbeatTimeout > 0 {
go wait.Until(func() {
if time.Since(ctl.lastPong.Load().(time.Time)) > time.Duration(ctl.sessionCtx.Common.Transport.HeartbeatTimeout)*time.Second {
xl.Warnf("heartbeat timeout")
Expand Down
6 changes: 3 additions & 3 deletions client/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
"time"

libio "github.com/fatedier/golib/io"
libdial "github.com/fatedier/golib/net/dial"
libnet "github.com/fatedier/golib/net"
pp "github.com/pires/go-proxyproto"
"golang.org/x/time/rate"

Expand Down Expand Up @@ -197,9 +197,9 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
return
}

localConn, err := libdial.Dial(
localConn, err := libnet.Dial(
net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort)),
libdial.WithTimeout(10*time.Second),
libnet.WithTimeout(10*time.Second),
)
if err != nil {
workConn.Close()
Expand Down
27 changes: 20 additions & 7 deletions client/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,18 +380,31 @@ func (svr *Service) stop() {
}
}

// TODO(fatedier): Use StatusExporter to provide query interfaces instead of directly using methods from the Service.
func (svr *Service) GetProxyStatus(name string) (*proxy.WorkingStatus, error) {
func (svr *Service) getProxyStatus(name string) (*proxy.WorkingStatus, bool) {
svr.ctlMu.RLock()
ctl := svr.ctl
svr.ctlMu.RUnlock()

if ctl == nil {
return nil, fmt.Errorf("control is not running")
return nil, false
}
ws, ok := ctl.pm.GetProxyStatus(name)
if !ok {
return nil, fmt.Errorf("proxy [%s] is not found", name)
return ctl.pm.GetProxyStatus(name)
}

func (svr *Service) StatusExporter() StatusExporter {
return &statusExporterImpl{
getProxyStatusFunc: svr.getProxyStatus,
}
return ws, nil
}

type StatusExporter interface {
GetProxyStatus(name string) (*proxy.WorkingStatus, bool)
}

type statusExporterImpl struct {
getProxyStatusFunc func(name string) (*proxy.WorkingStatus, bool)
}

func (s *statusExporterImpl) GetProxyStatus(name string) (*proxy.WorkingStatus, bool) {
return s.getProxyStatusFunc(name)
}
3 changes: 2 additions & 1 deletion conf/frpc_full_example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ transport.poolCount = 5

# Specify keep alive interval for tcp mux.
# only valid if tcpMux is enabled.
# transport.tcpMuxKeepaliveInterval = 60
# transport.tcpMuxKeepaliveInterval = 30

# Communication protocol used to connect to server
# supports tcp, kcp, quic, websocket and wss now, default is tcp
Expand Down Expand Up @@ -209,6 +209,7 @@ locations = ["/", "/pic"]
# routeByHTTPUser = abc
hostHeaderRewrite = "example.com"
requestHeaders.set.x-from-where = "frp"
responseHeaders.set.foo = "bar"
healthCheck.type = "http"
# frpc will send a GET http request '/status' to local http service
# http service is alive when it return 2xx http response code
Expand Down
2 changes: 1 addition & 1 deletion conf/frps_full_example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ transport.maxPoolCount = 5

# Specify keep alive interval for tcp mux.
# 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.
# If negative, keep-alive probes are disabled.
Expand Down
Loading

0 comments on commit 491dc72

Please sign in to comment.