Skip to content

Commit

Permalink
Merge branch 'fatedier:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
pb8DvwQkfRR authored Feb 10, 2022
2 parents 1ed8596 + 88fcc07 commit ea71e1b
Show file tree
Hide file tree
Showing 50 changed files with 601 additions and 596 deletions.
47 changes: 36 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,32 @@

[README](README.md) | [中文文档](README_zh.md)

<h3 align="center">Platinum Sponsors</h3>
<!--platinum sponsors start-->

<p align="center">
<a href="https://www.doppler.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="400px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_doppler.png">
</a>
</p>

<!--platinum sponsors end-->

<h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start-->

<p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="300px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
</a>
</p>

<!--gold sponsors end-->

<h3 align="center">Silver Sponsors</h3>

* Sakura Frp - 欢迎点击 "加入我们"

## What is frp?

frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name.
Expand Down Expand Up @@ -67,8 +93,7 @@ frp also has a P2P connect mode.
* [Development Plan](#development-plan)
* [Contributing](#contributing)
* [Donation](#donation)
* [AliPay](#alipay)
* [Wechat Pay](#wechat-pay)
* [GitHub Sponsors](#github-sponsors)
* [PayPal](#paypal)

<!-- vim-markdown-toc -->
Expand All @@ -77,7 +102,9 @@ frp also has a P2P connect mode.

frp is under development. Try the latest release version in the `master` branch, or use the `dev` branch for the version in development.

**The protocol might change at a release and we don't promise backwards compatibility. Please check the release log when upgrading the client and the server.**
We are working on v2 version and trying to do some code refactor and improvements. It won't be compatible with v1.

We will switch v0 to v1 at the right time and only accept bug fixes and improvements instead of big feature requirements.

## Architecture

Expand Down Expand Up @@ -867,7 +894,7 @@ In this example, it will set header `X-From-Where: frp` in the HTTP request.

This feature is for http proxy only.

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

#### Proxy Protocol

Expand Down Expand Up @@ -983,11 +1010,13 @@ server_port = 7000
type = tcpmux
multiplexer = httpconnect
custom_domains = test1
local_port = 80

[proxy2]
type = tcpmux
multiplexer = httpconnect
custom_domains = test2
local_port = 8080
```

In the above configuration - frps can be contacted on port 1337 with a HTTP CONNECT header such as:
Expand Down Expand Up @@ -1073,15 +1102,11 @@ Interested in getting involved? We would like to help you!

If frp helps you a lot, you can support us by:

frp QQ group: 606194980

### AliPay

![donation-alipay](/doc/pic/donate-alipay.png)
### GitHub Sponsors

### Wechat Pay
Support us by [Github Sponsors](https://github.com/sponsors/fatedier).

![donation-wechatpay](/doc/pic/donate-wechatpay.png)
You can have your company's logo placed on README file of this project.

### PayPal

Expand Down
40 changes: 36 additions & 4 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@

frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。

<h3 align="center">Platinum Sponsors</h3>
<!--platinum sponsors start-->

<p align="center">
<a href="https://www.doppler.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="400px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_doppler.png">
</a>
</p>

<!--platinum sponsors end-->

<h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start-->

<p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="300px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
</a>
</p>

<!--gold sponsors end-->

<h3 align="center">Silver Sponsors</h3>

* Sakura Frp - 欢迎点击 "加入我们"

## 为什么使用 frp ?

通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括:
Expand All @@ -25,6 +51,10 @@ frp 目前已被很多公司广泛用于测试、生产环境。

master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。

我们正在进行 v2 大版本的开发,将会尝试在各个方面进行重构和升级,且不会与 v1 版本进行兼容,预计会持续一段时间。

现在的 v0 版本将会在合适的时间切换为 v1 版本并且保证兼容性,后续只做 bug 修复和优化,不再进行大的功能性更新。

## 文档

完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)
Expand All @@ -46,6 +76,12 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进

如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。

### GitHub Sponsors

您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。

企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。

### 知识星球

如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:
Expand All @@ -59,7 +95,3 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
### 微信支付捐赠

![donate-wechatpay](/doc/pic/donate-wechatpay.png)

### Paypal 捐赠

海外用户推荐通过 [Paypal](https://www.paypal.me/fatedier) 向我的账户 **fatedier@gmail.com** 进行捐赠。
9 changes: 2 additions & 7 deletions Release.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
### New
### Fix

* Add `/healthz` API.
* frpc support `disable_custom_tls_first_byte` .If set true, frpc will not send custom header byte.

### Improve

* Use go standard embed package instead of statik.
* Fixed IPv6 address parse issue.
20 changes: 11 additions & 9 deletions client/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,22 @@ func (svr *Service) RunAdminServer(address string) (err error) {
// url router
router := mux.NewRouter()

router.HandleFunc("/healthz", svr.healthz)

subRouter := router.NewRoute().Subrouter()
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)

// api, see admin_api.go
router.HandleFunc("/healthz", svr.healthz)
router.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
router.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
router.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
router.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT")
subRouter.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
subRouter.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
subRouter.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
subRouter.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT")

// view
router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
router.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET")
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
subRouter.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
subRouter.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET")
subRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
})

Expand Down
58 changes: 47 additions & 11 deletions client/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (

"github.com/fatedier/golib/control/shutdown"
"github.com/fatedier/golib/crypto"
libdial "github.com/fatedier/golib/net/dial"
fmux "github.com/hashicorp/yamux"
)

Expand Down Expand Up @@ -234,12 +235,36 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
}
}

address := net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort))
conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol, address, tlsConfig, ctl.clientCfg.DisableCustomTLSFirstByte)

proxyType, addr, auth, err := libdial.ParseProxyURL(ctl.clientCfg.HTTPProxy)
if err != nil {
xl.Error("fail to parse proxy url")
return nil, err
}
dialOptions := []libdial.DialOption{}
protocol := ctl.clientCfg.Protocol
if protocol == "websocket" {
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
}
if ctl.clientCfg.ConnectServerLocalIP != "" {
dialOptions = append(dialOptions, libdial.WithLocalAddr(ctl.clientCfg.ConnectServerLocalIP))
}
dialOptions = append(dialOptions,
libdial.WithProtocol(protocol),
libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig),
libdial.WithAfterHook(libdial.AfterHook{
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, ctl.clientCfg.DisableCustomTLSFirstByte),
}),
)
conn, err = libdial.Dial(
net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)),
dialOptions...,
)
if err != nil {
xl.Warn("start new connection to server error: %v", err)
return
return nil, err
}
}
return
Expand Down Expand Up @@ -308,16 +333,27 @@ func (ctl *Control) msgHandler() {
}()
defer ctl.msgHandlerShutdown.Done()

hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second)
defer hbSend.Stop()
hbCheck := time.NewTicker(time.Second)
defer hbCheck.Stop()
var hbSendCh <-chan time.Time
// TODO(fatedier): disable heartbeat if TCPMux is enabled.
// Just keep it here to keep compatible with old version frps.
if ctl.clientCfg.HeartbeatInterval > 0 {
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second)
defer hbSend.Stop()
hbSendCh = hbSend.C
}

ctl.lastPong = time.Now()
var hbCheckCh <-chan time.Time
// Check heartbeat timeout only if TCPMux is not enabled and users don't disable heartbeat feature.
if ctl.clientCfg.HeartbeatInterval > 0 && ctl.clientCfg.HeartbeatTimeout > 0 && !ctl.clientCfg.TCPMux {
hbCheck := time.NewTicker(time.Second)
defer hbCheck.Stop()
hbCheckCh = hbCheck.C
}

ctl.lastPong = time.Now()
for {
select {
case <-hbSend.C:
case <-hbSendCh:
// send heartbeat to server
xl.Debug("send heartbeat to server")
pingMsg := &msg.Ping{}
Expand All @@ -326,7 +362,7 @@ func (ctl *Control) msgHandler() {
return
}
ctl.sendCh <- pingMsg
case <-hbCheck.C:
case <-hbCheckCh:
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartbeatTimeout)*time.Second {
xl.Warn("heartbeat timeout")
// let reader() stop
Expand Down
19 changes: 8 additions & 11 deletions client/proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (

"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
libdial "github.com/fatedier/golib/net/dial"
"github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux"
pp "github.com/pires/go-proxyproto"
Expand Down Expand Up @@ -346,22 +347,18 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)

// Send detect message
array := strings.Split(natHoleRespMsg.VisitorAddr, ":")
if len(array) <= 1 {
xl.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
host, portStr, err := net.SplitHostPort(natHoleRespMsg.VisitorAddr)
if err != nil {
xl.Error("get NatHoleResp visitor address [%s] error: %v", natHoleRespMsg.VisitorAddr, err)
}
laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
/*
for i := 1000; i < 65000; i++ {
pxy.sendDetectMsg(array[0], int64(i), laddr, "a")
}
*/
port, err := strconv.ParseInt(array[1], 10, 64)

port, err := strconv.ParseInt(portStr, 10, 64)
if err != nil {
xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
return
}
pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
pxy.sendDetectMsg(host, int(port), laddr, []byte(natHoleRespMsg.Sid))
xl.Trace("send all detect msg done")

msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
Expand Down Expand Up @@ -790,7 +787,7 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
return
}

localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIP, localInfo.LocalPort))
localConn, err := libdial.Dial(net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort)))
if err != nil {
workConn.Close()
xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err)
Expand Down
32 changes: 29 additions & 3 deletions client/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/version"
"github.com/fatedier/frp/pkg/util/xlog"
libdial "github.com/fatedier/golib/net/dial"

fmux "github.com/hashicorp/yamux"
)
Expand Down Expand Up @@ -228,8 +229,33 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
}
}

address := net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort))
conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol, address, tlsConfig, svr.cfg.DisableCustomTLSFirstByte)
proxyType, addr, auth, err := libdial.ParseProxyURL(svr.cfg.HTTPProxy)
if err != nil {
xl.Error("fail to parse proxy url")
return
}
dialOptions := []libdial.DialOption{}
protocol := svr.cfg.Protocol
if protocol == "websocket" {
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
}
if svr.cfg.ConnectServerLocalIP != "" {
dialOptions = append(dialOptions, libdial.WithLocalAddr(svr.cfg.ConnectServerLocalIP))
}
dialOptions = append(dialOptions,
libdial.WithProtocol(protocol),
libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig),
libdial.WithAfterHook(libdial.AfterHook{
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, svr.cfg.DisableCustomTLSFirstByte),
}),
)
conn, err = libdial.Dial(
net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)),
dialOptions...,
)
if err != nil {
return
}
Expand All @@ -245,7 +271,7 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {

if svr.cfg.TCPMux {
fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 20 * time.Second
fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.TCPMuxKeepaliveInterval) * time.Second
fmuxCfg.LogOutput = io.Discard
session, err = fmux.Client(conn, fmuxCfg)
if err != nil {
Expand Down
Loading

0 comments on commit ea71e1b

Please sign in to comment.