Compare commits
62 Commits
c064142501
...
v0.58.0
Author | SHA1 | Date | |
---|---|---|---|
|
4e8e9e1dec | ||
|
8f23733f47 | ||
|
5a6d9f60c2 | ||
|
a5b7abfc8b | ||
|
1e650ea9a7 | ||
|
d689f0fc53 | ||
|
d505ecb473 | ||
|
2b83436a97 | ||
|
051299ec25 | ||
|
44985f574d | ||
|
c9ca9353cf | ||
|
31fa3f021a | ||
|
2d3af8a108 | ||
|
466d69eae0 | ||
|
7c8cbeb250 | ||
|
4fd6301577 | ||
|
53626b370c | ||
|
4fd800bc48 | ||
|
0d6d968fe8 | ||
|
8fb99ef7a9 | ||
|
88e74ff24d | ||
|
534dc99d55 | ||
|
595aba5a9b | ||
|
a4189ba474 | ||
|
9ec84f8143 | ||
|
8ab474cc97 | ||
|
a301046f3d | ||
|
8888610d83 | ||
|
fe5fb0326b | ||
|
eb1e19a821 | ||
|
10f2620131 | ||
|
ce677820c6 | ||
|
88fcc079e8 | ||
|
2dab5d0bca | ||
|
143750901e | ||
|
997d406ec2 | ||
|
cfd1a3128a | ||
|
57577ea044 | ||
|
c5c79e4148 | ||
|
55da58eca4 | ||
|
76a1efccd9 | ||
|
980f084ad1 | ||
|
3bf1eb8565 | ||
|
b2ae433e18 | ||
|
aa0a41ee4e | ||
|
1ea1530b36 | ||
|
e0c45a1aca | ||
|
813c45f5c2 | ||
|
aa74dc4646 | ||
|
2406ecdfea | ||
|
8668fef136 | ||
|
ea62bc5a34 | ||
|
23bb76397a | ||
|
487c8d7c29 | ||
|
f480160e2d | ||
|
30c246c488 | ||
|
75f3bce04d | ||
|
adc3adc13b | ||
|
e62d9a5242 | ||
|
134a46c00b | ||
|
ae08811636 | ||
|
6451583e60 |
4
.github/workflows/build-and-push-image.yml
vendored
@@ -2,7 +2,7 @@ name: Build Image and Publish to Dockerhub & GPR
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [ published ]
|
types: [ created ]
|
||||||
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@v5
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./dockerfiles/Dockerfile-for-frpc
|
file: ./dockerfiles/Dockerfile-for-frpc
|
||||||
|
@@ -88,7 +88,6 @@ linters-settings:
|
|||||||
excludes:
|
excludes:
|
||||||
- G401
|
- G401
|
||||||
- G402
|
- G402
|
||||||
- G404
|
|
||||||
- G501
|
- G501
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
17
README.md
@@ -9,24 +9,13 @@
|
|||||||
|
|
||||||
<h3 align="center">Gold Sponsors</h3>
|
<h3 align="center">Gold Sponsors</h3>
|
||||||
<!--gold sponsors start-->
|
<!--gold sponsors start-->
|
||||||
<p align="center">
|
|
||||||
<a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
|
|
||||||
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<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="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
|
<img width="350px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
<a> </a>
|
||||||
<p align="center">
|
|
||||||
<a href="https://github.com/daytonaio/daytona" target="_blank">
|
<a href="https://github.com/daytonaio/daytona" target="_blank">
|
||||||
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
|
<img width="360px" 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-->
|
||||||
|
17
README_zh.md
@@ -11,24 +11,13 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP
|
|||||||
|
|
||||||
<h3 align="center">Gold Sponsors</h3>
|
<h3 align="center">Gold Sponsors</h3>
|
||||||
<!--gold sponsors start-->
|
<!--gold sponsors start-->
|
||||||
<p align="center">
|
|
||||||
<a href="https://lokal.so/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
|
|
||||||
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_lokal.png">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<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="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
|
<img width="350px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
<a> </a>
|
||||||
<p align="center">
|
|
||||||
<a href="https://github.com/daytonaio/daytona" target="_blank">
|
<a href="https://github.com/daytonaio/daytona" target="_blank">
|
||||||
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
|
<img width="360px" 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-->
|
||||||
|
13
Release.md
@@ -1,3 +1,14 @@
|
|||||||
|
### 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
|
||||||
|
|
||||||
* Added a new plugin `tls2raw`: Enables TLS termination and forwarding of decrypted raw traffic to local service.
|
* Show tcpmux proxies on the frps dashboard.
|
||||||
|
* `http` proxy can modify the response header. For example, `responseHeaders.set.foo = "bar"` will add a new header `foo: bar` to the response.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* When an HTTP proxy request times out, it returns 504 instead of 404 now.
|
||||||
|
@@ -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(pxy.ctx, remote, workConn, &extraInfo)
|
pxy.proxyPlugin.Handle(remote, workConn, &extraInfo)
|
||||||
xl.Debugf("handle by plugin finished")
|
xl.Debugf("handle by plugin finished")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -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 = 30
|
# transport.tcpMuxKeepaliveInterval = 60
|
||||||
|
|
||||||
# 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,26 +315,6 @@ 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
|
||||||
|
@@ -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 = 30
|
# transport.tcpMuxKeepaliveInterval = 60
|
||||||
|
|
||||||
# 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.
|
||||||
|
BIN
doc/pic/donate-wechatpay.png
Normal file
After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 56 KiB |
BIN
doc/pic/sponsor_doppler.png
Normal file
After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 55 KiB |
BIN
doc/pic/sponsor_nango.png
Normal file
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 31 KiB |
@@ -345,19 +345,35 @@ 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,
|
||||||
Protocol: "tcp",
|
|
||||||
Start: make([]string, 0),
|
Start: make([]string, 0),
|
||||||
|
Protocol: "tcp",
|
||||||
|
QUICKeepalivePeriod: 10,
|
||||||
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -200,20 +200,34 @@ 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 defaults.
|
// GetDefaultServerConf returns a server configuration with reasonable
|
||||||
// Note: Some default values here will be set to empty and will be converted to them
|
// defaults.
|
||||||
// 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(),
|
||||||
DashboardAddr: "0.0.0.0",
|
BindAddr: "0.0.0.0",
|
||||||
LogFile: "console",
|
BindPort: 7000,
|
||||||
LogWay: "console",
|
QUICKeepalivePeriod: 10,
|
||||||
DetailedErrorsToClient: true,
|
QUICMaxIdleTimeout: 30,
|
||||||
TCPMux: true,
|
QUICMaxIncomingStreams: 100000,
|
||||||
AllowPorts: make(map[int]struct{}),
|
VhostHTTPTimeout: 60,
|
||||||
HTTPPlugins: make(map[string]HTTPPluginOptions),
|
DashboardAddr: "0.0.0.0",
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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, 30)
|
c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 60)
|
||||||
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)
|
||||||
|
@@ -17,18 +17,11 @@ 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"`
|
||||||
@@ -49,7 +42,7 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
|
|||||||
|
|
||||||
c.Type = typeStruct.Type
|
c.Type = typeStruct.Type
|
||||||
if c.Type == "" {
|
if c.Type == "" {
|
||||||
return errors.New("plugin type is empty")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
v, ok := clientPluginOptionsTypeMap[typeStruct.Type]
|
v, ok := clientPluginOptionsTypeMap[typeStruct.Type]
|
||||||
@@ -70,20 +63,14 @@ 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{
|
||||||
@@ -91,11 +78,9 @@ 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 {
|
||||||
@@ -105,61 +90,36 @@ 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"`
|
||||||
@@ -168,20 +128,7 @@ 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() {}
|
|
||||||
|
@@ -127,10 +127,6 @@ 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) {
|
||||||
@@ -199,10 +195,6 @@ 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
|
||||||
|
@@ -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, 30)
|
c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 60)
|
||||||
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) {
|
||||||
|
@@ -32,8 +32,6 @@ 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
|
||||||
}
|
}
|
||||||
@@ -72,10 +70,3 @@ 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
|
|
||||||
}
|
|
||||||
|
@@ -120,10 +120,6 @@ 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 {
|
||||||
|
@@ -1,94 +0,0 @@
|
|||||||
// 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()
|
|
||||||
}
|
|
@@ -17,7 +17,6 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
@@ -89,7 +88,7 @@ func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
|||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTP2HTTPSPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
|
func (p *HTTP2HTTPSPlugin) Handle(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)
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@ package plugin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -69,7 +68,7 @@ func (hp *HTTPProxy) Name() string {
|
|||||||
return v1.PluginHTTPProxy
|
return v1.PluginHTTPProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hp *HTTPProxy) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
|
func (hp *HTTPProxy) Handle(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)
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -28,11 +27,9 @@ 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"
|
||||||
)
|
)
|
||||||
@@ -74,39 +71,44 @@ 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)
|
|
||||||
})
|
|
||||||
|
|
||||||
tlsConfig, err := transport.NewServerTLSConfig(p.opts.CrtPath, p.opts.KeyPath, "")
|
p.s = &http.Server{
|
||||||
|
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.ServeTLS(listener, "", "")
|
_ = p.s.Serve(ln)
|
||||||
}()
|
}()
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
|
func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
|
||||||
|
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)
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -28,11 +27,9 @@ 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"
|
||||||
)
|
)
|
||||||
@@ -80,39 +77,44 @@ 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)
|
|
||||||
})
|
|
||||||
|
|
||||||
tlsConfig, err := transport.NewServerTLSConfig(p.opts.CrtPath, p.opts.KeyPath, "")
|
p.s = &http.Server{
|
||||||
|
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.ServeTLS(listener, "", "")
|
_ = p.s.Serve(ln)
|
||||||
}()
|
}()
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPSPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
|
func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) {
|
||||||
|
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)
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -58,7 +57,7 @@ type ExtraInfo struct {
|
|||||||
type Plugin interface {
|
type Plugin interface {
|
||||||
Name() string
|
Name() string
|
||||||
|
|
||||||
Handle(ctx context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo)
|
Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@@ -51,7 +50,7 @@ func NewSocks5Plugin(options v1.ClientPluginOptions) (p Plugin, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *Socks5Plugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
|
func (sp *Socks5Plugin) Handle(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)
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -70,7 +69,7 @@ func NewStaticFilePlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
|||||||
return sp, nil
|
return sp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *StaticFilePlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
|
func (sp *StaticFilePlugin) Handle(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)
|
||||||
}
|
}
|
||||||
|
@@ -1,83 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
@@ -17,14 +17,12 @@
|
|||||||
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() {
|
||||||
@@ -50,11 +48,9 @@ func NewUnixDomainSocketPlugin(options v1.ClientPluginOptions) (p Plugin, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uds *UnixDomainSocketPlugin) Handle(ctx context.Context, conn io.ReadWriteCloser, _ net.Conn, extra *ExtraInfo) {
|
func (uds *UnixDomainSocketPlugin) Handle(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 {
|
||||||
|
@@ -59,12 +59,8 @@ 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, addr string) (net.Conn, error) {
|
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
|
||||||
if addr == "127.0.0.1:53" || addr == "[::1]:53" {
|
return net.Dial(network, "8.8.8.8:53")
|
||||||
addr = "8.8.8.8:53"
|
|
||||||
}
|
|
||||||
var d net.Dialer
|
|
||||||
return d.DialContext(ctx, network, addr)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
var version = "0.59.0"
|
var version = "0.58.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
@@ -3,7 +3,6 @@ package plugin
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
@@ -330,122 +329,4 @@ 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()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|