Skip to content

Commit 4af85da

Browse files
authored
type http/tcpmux proxy support route_by_http_user, tcpmux support passthourgh mode (fatedier#2932)
1 parent bd89eab commit 4af85da

File tree

22 files changed

+605
-282
lines changed

22 files changed

+605
-282
lines changed

Release.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
### New
21

3-
* Added new parameter `config_dir` in frpc to run multiple client instances in one process.
4-
5-
### Fix
6-
7-
* Equal sign in environment variables causes parsing error.

conf/frpc_full.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ subdomain = web01
216216
custom_domains = web01.yourdomain.com
217217
# locations is only available for http type
218218
locations = /,/pic
219+
# route requests to this service if http basic auto user is abc
220+
# route_by_http_user = abc
219221
host_header_rewrite = example.com
220222
# params with prefix "header_" will be used to update http request headers
221223
header_X-From-Where = frp
@@ -348,3 +350,4 @@ multiplexer = httpconnect
348350
local_ip = 127.0.0.1
349351
local_port = 10701
350352
custom_domains = tunnel1
353+
# route_by_http_user = user1

conf/frps_full.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ vhost_https_port = 443
3030
# HTTP CONNECT requests. By default, this value is 0.
3131
# tcpmux_httpconnect_port = 1337
3232

33+
# If tcpmux_passthrough is true, frps won't do any update on traffic.
34+
# tcpmux_passthrough = false
35+
3336
# set dashboard_addr and dashboard_port to view dashboard of frps
3437
# dashboard_addr's default value is same with bind_addr
3538
# dashboard is available only if dashboard_port is set

pkg/config/proxy.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ type HTTPProxyConf struct {
162162
HTTPPwd string `ini:"http_pwd" json:"http_pwd"`
163163
HostHeaderRewrite string `ini:"host_header_rewrite" json:"host_header_rewrite"`
164164
Headers map[string]string `ini:"-" json:"headers"`
165+
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
165166
}
166167

167168
// HTTPS
@@ -178,8 +179,9 @@ type TCPProxyConf struct {
178179

179180
// TCPMux
180181
type TCPMuxProxyConf struct {
181-
BaseProxyConf `ini:",extends"`
182-
DomainConf `ini:",extends"`
182+
BaseProxyConf `ini:",extends"`
183+
DomainConf `ini:",extends"`
184+
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
183185

184186
Multiplexer string `ini:"multiplexer"`
185187
}
@@ -576,7 +578,7 @@ func (cfg *TCPMuxProxyConf) Compare(cmp ProxyConf) bool {
576578
return false
577579
}
578580

579-
if cfg.Multiplexer != cmpConf.Multiplexer {
581+
if cfg.Multiplexer != cmpConf.Multiplexer || cfg.RouteByHTTPUser != cmpConf.RouteByHTTPUser {
580582
return false
581583
}
582584

@@ -601,6 +603,7 @@ func (cfg *TCPMuxProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
601603
cfg.CustomDomains = pMsg.CustomDomains
602604
cfg.SubDomain = pMsg.SubDomain
603605
cfg.Multiplexer = pMsg.Multiplexer
606+
cfg.RouteByHTTPUser = pMsg.RouteByHTTPUser
604607
}
605608

606609
func (cfg *TCPMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
@@ -610,6 +613,7 @@ func (cfg *TCPMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
610613
pMsg.CustomDomains = cfg.CustomDomains
611614
pMsg.SubDomain = cfg.SubDomain
612615
pMsg.Multiplexer = cfg.Multiplexer
616+
pMsg.RouteByHTTPUser = cfg.RouteByHTTPUser
613617
}
614618

615619
func (cfg *TCPMuxProxyConf) CheckForCli() (err error) {
@@ -724,6 +728,7 @@ func (cfg *HTTPProxyConf) Compare(cmp ProxyConf) bool {
724728
cfg.HTTPUser != cmpConf.HTTPUser ||
725729
cfg.HTTPPwd != cmpConf.HTTPPwd ||
726730
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
731+
cfg.RouteByHTTPUser != cmpConf.RouteByHTTPUser ||
727732
!reflect.DeepEqual(cfg.Headers, cmpConf.Headers) {
728733
return false
729734
}
@@ -754,6 +759,7 @@ func (cfg *HTTPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
754759
cfg.HTTPUser = pMsg.HTTPUser
755760
cfg.HTTPPwd = pMsg.HTTPPwd
756761
cfg.Headers = pMsg.Headers
762+
cfg.RouteByHTTPUser = pMsg.RouteByHTTPUser
757763
}
758764

759765
func (cfg *HTTPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
@@ -767,6 +773,7 @@ func (cfg *HTTPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
767773
pMsg.HTTPUser = cfg.HTTPUser
768774
pMsg.HTTPPwd = cfg.HTTPPwd
769775
pMsg.Headers = cfg.Headers
776+
pMsg.RouteByHTTPUser = cfg.RouteByHTTPUser
770777
}
771778

772779
func (cfg *HTTPProxyConf) CheckForCli() (err error) {

pkg/config/server.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ type ServerCommonConf struct {
6262
// requests on one single port. If it's not - it will listen on this value for
6363
// HTTP CONNECT requests. By default, this value is 0.
6464
TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port" json:"tcpmux_httpconnect_port" validate:"gte=0,lte=65535"`
65+
// If TCPMuxPassthrough is true, frps won't do any update on traffic.
66+
TCPMuxPassthrough bool `ini:"tcpmux_passthrough" json:"tcpmux_passthrough"`
6567
// VhostHTTPTimeout specifies the response header timeout for the Vhost
6668
// HTTP server, in seconds. By default, this value is 60.
6769
VhostHTTPTimeout int64 `ini:"vhost_http_timeout" json:"vhost_http_timeout"`
@@ -188,6 +190,7 @@ func GetDefaultServerConf() ServerCommonConf {
188190
VhostHTTPPort: 0,
189191
VhostHTTPSPort: 0,
190192
TCPMuxHTTPConnectPort: 0,
193+
TCPMuxPassthrough: false,
191194
VhostHTTPTimeout: 60,
192195
DashboardAddr: "0.0.0.0",
193196
DashboardPort: 0,

pkg/msg/msg.go

Lines changed: 68 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -62,133 +62,134 @@ var (
6262

6363
// When frpc start, client send this message to login to server.
6464
type Login struct {
65-
Version string `json:"version"`
66-
Hostname string `json:"hostname"`
67-
Os string `json:"os"`
68-
Arch string `json:"arch"`
69-
User string `json:"user"`
70-
PrivilegeKey string `json:"privilege_key"`
71-
Timestamp int64 `json:"timestamp"`
72-
RunID string `json:"run_id"`
73-
Metas map[string]string `json:"metas"`
65+
Version string `json:"version,omitempty"`
66+
Hostname string `json:"hostname,omitempty"`
67+
Os string `json:"os,omitempty"`
68+
Arch string `json:"arch,omitempty"`
69+
User string `json:"user,omitempty"`
70+
PrivilegeKey string `json:"privilege_key,omitempty"`
71+
Timestamp int64 `json:"timestamp,omitempty"`
72+
RunID string `json:"run_id,omitempty"`
73+
Metas map[string]string `json:"metas,omitempty"`
7474

7575
// Some global configures.
76-
PoolCount int `json:"pool_count"`
76+
PoolCount int `json:"pool_count,omitempty"`
7777
}
7878

7979
type LoginResp struct {
80-
Version string `json:"version"`
81-
RunID string `json:"run_id"`
82-
ServerUDPPort int `json:"server_udp_port"`
83-
Error string `json:"error"`
80+
Version string `json:"version,omitempty"`
81+
RunID string `json:"run_id,omitempty"`
82+
ServerUDPPort int `json:"server_udp_port,omitempty"`
83+
Error string `json:"error,omitempty"`
8484
}
8585

8686
// When frpc login success, send this message to frps for running a new proxy.
8787
type NewProxy struct {
88-
ProxyName string `json:"proxy_name"`
89-
ProxyType string `json:"proxy_type"`
90-
UseEncryption bool `json:"use_encryption"`
91-
UseCompression bool `json:"use_compression"`
92-
Group string `json:"group"`
93-
GroupKey string `json:"group_key"`
94-
Metas map[string]string `json:"metas"`
88+
ProxyName string `json:"proxy_name,omitempty"`
89+
ProxyType string `json:"proxy_type,omitempty"`
90+
UseEncryption bool `json:"use_encryption,omitempty"`
91+
UseCompression bool `json:"use_compression,omitempty"`
92+
Group string `json:"group,omitempty"`
93+
GroupKey string `json:"group_key,omitempty"`
94+
Metas map[string]string `json:"metas,omitempty"`
9595

9696
// tcp and udp only
97-
RemotePort int `json:"remote_port"`
97+
RemotePort int `json:"remote_port,omitempty"`
9898

9999
// http and https only
100-
CustomDomains []string `json:"custom_domains"`
101-
SubDomain string `json:"subdomain"`
102-
Locations []string `json:"locations"`
103-
HTTPUser string `json:"http_user"`
104-
HTTPPwd string `json:"http_pwd"`
105-
HostHeaderRewrite string `json:"host_header_rewrite"`
106-
Headers map[string]string `json:"headers"`
100+
CustomDomains []string `json:"custom_domains,omitempty"`
101+
SubDomain string `json:"subdomain,omitempty"`
102+
Locations []string `json:"locations,omitempty"`
103+
HTTPUser string `json:"http_user,omitempty"`
104+
HTTPPwd string `json:"http_pwd,omitempty"`
105+
HostHeaderRewrite string `json:"host_header_rewrite,omitempty"`
106+
Headers map[string]string `json:"headers,omitempty"`
107+
RouteByHTTPUser string `json:"route_by_http_user,omitempty"`
107108

108109
// stcp
109-
Sk string `json:"sk"`
110+
Sk string `json:"sk,omitempty"`
110111

111112
// tcpmux
112-
Multiplexer string `json:"multiplexer"`
113+
Multiplexer string `json:"multiplexer,omitempty"`
113114
}
114115

115116
type NewProxyResp struct {
116-
ProxyName string `json:"proxy_name"`
117-
RemoteAddr string `json:"remote_addr"`
118-
Error string `json:"error"`
117+
ProxyName string `json:"proxy_name,omitempty"`
118+
RemoteAddr string `json:"remote_addr,omitempty"`
119+
Error string `json:"error,omitempty"`
119120
}
120121

121122
type CloseProxy struct {
122-
ProxyName string `json:"proxy_name"`
123+
ProxyName string `json:"proxy_name,omitempty"`
123124
}
124125

125126
type NewWorkConn struct {
126-
RunID string `json:"run_id"`
127-
PrivilegeKey string `json:"privilege_key"`
128-
Timestamp int64 `json:"timestamp"`
127+
RunID string `json:"run_id,omitempty"`
128+
PrivilegeKey string `json:"privilege_key,omitempty"`
129+
Timestamp int64 `json:"timestamp,omitempty"`
129130
}
130131

131132
type ReqWorkConn struct {
132133
}
133134

134135
type StartWorkConn struct {
135-
ProxyName string `json:"proxy_name"`
136-
SrcAddr string `json:"src_addr"`
137-
DstAddr string `json:"dst_addr"`
138-
SrcPort uint16 `json:"src_port"`
139-
DstPort uint16 `json:"dst_port"`
140-
Error string `json:"error"`
136+
ProxyName string `json:"proxy_name,omitempty"`
137+
SrcAddr string `json:"src_addr,omitempty"`
138+
DstAddr string `json:"dst_addr,omitempty"`
139+
SrcPort uint16 `json:"src_port,omitempty"`
140+
DstPort uint16 `json:"dst_port,omitempty"`
141+
Error string `json:"error,omitempty"`
141142
}
142143

143144
type NewVisitorConn struct {
144-
ProxyName string `json:"proxy_name"`
145-
SignKey string `json:"sign_key"`
146-
Timestamp int64 `json:"timestamp"`
147-
UseEncryption bool `json:"use_encryption"`
148-
UseCompression bool `json:"use_compression"`
145+
ProxyName string `json:"proxy_name,omitempty"`
146+
SignKey string `json:"sign_key,omitempty"`
147+
Timestamp int64 `json:"timestamp,omitempty"`
148+
UseEncryption bool `json:"use_encryption,omitempty"`
149+
UseCompression bool `json:"use_compression,omitempty"`
149150
}
150151

151152
type NewVisitorConnResp struct {
152-
ProxyName string `json:"proxy_name"`
153-
Error string `json:"error"`
153+
ProxyName string `json:"proxy_name,omitempty"`
154+
Error string `json:"error,omitempty"`
154155
}
155156

156157
type Ping struct {
157-
PrivilegeKey string `json:"privilege_key"`
158-
Timestamp int64 `json:"timestamp"`
158+
PrivilegeKey string `json:"privilege_key,omitempty"`
159+
Timestamp int64 `json:"timestamp,omitempty"`
159160
}
160161

161162
type Pong struct {
162-
Error string `json:"error"`
163+
Error string `json:"error,omitempty"`
163164
}
164165

165166
type UDPPacket struct {
166-
Content string `json:"c"`
167-
LocalAddr *net.UDPAddr `json:"l"`
168-
RemoteAddr *net.UDPAddr `json:"r"`
167+
Content string `json:"c,omitempty"`
168+
LocalAddr *net.UDPAddr `json:"l,omitempty"`
169+
RemoteAddr *net.UDPAddr `json:"r,omitempty"`
169170
}
170171

171172
type NatHoleVisitor struct {
172-
ProxyName string `json:"proxy_name"`
173-
SignKey string `json:"sign_key"`
174-
Timestamp int64 `json:"timestamp"`
173+
ProxyName string `json:"proxy_name,omitempty"`
174+
SignKey string `json:"sign_key,omitempty"`
175+
Timestamp int64 `json:"timestamp,omitempty"`
175176
}
176177

177178
type NatHoleClient struct {
178-
ProxyName string `json:"proxy_name"`
179-
Sid string `json:"sid"`
179+
ProxyName string `json:"proxy_name,omitempty"`
180+
Sid string `json:"sid,omitempty"`
180181
}
181182

182183
type NatHoleResp struct {
183-
Sid string `json:"sid"`
184-
VisitorAddr string `json:"visitor_addr"`
185-
ClientAddr string `json:"client_addr"`
186-
Error string `json:"error"`
184+
Sid string `json:"sid,omitempty"`
185+
VisitorAddr string `json:"visitor_addr,omitempty"`
186+
ClientAddr string `json:"client_addr,omitempty"`
187+
Error string `json:"error,omitempty"`
187188
}
188189

189190
type NatHoleClientDetectOK struct {
190191
}
191192

192193
type NatHoleSid struct {
193-
Sid string `json:"sid"`
194+
Sid string `json:"sid,omitempty"`
194195
}

pkg/util/tcpmux/httpconnect.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,24 @@ import (
2424

2525
"github.com/fatedier/frp/pkg/util/util"
2626
"github.com/fatedier/frp/pkg/util/vhost"
27+
gnet "github.com/fatedier/golib/net"
2728
)
2829

2930
type HTTPConnectTCPMuxer struct {
3031
*vhost.Muxer
32+
33+
passthrough bool
34+
authRequired bool // Not supported until we really need this.
3135
}
3236

33-
func NewHTTPConnectTCPMuxer(listener net.Listener, timeout time.Duration) (*HTTPConnectTCPMuxer, error) {
34-
mux, err := vhost.NewMuxer(listener, getHostFromHTTPConnect, nil, sendHTTPOk, nil, timeout)
35-
return &HTTPConnectTCPMuxer{mux}, err
37+
func NewHTTPConnectTCPMuxer(listener net.Listener, passthrough bool, timeout time.Duration) (*HTTPConnectTCPMuxer, error) {
38+
ret := &HTTPConnectTCPMuxer{passthrough: passthrough, authRequired: false}
39+
mux, err := vhost.NewMuxer(listener, ret.getHostFromHTTPConnect, nil, ret.sendConnectResponse, nil, timeout)
40+
ret.Muxer = mux
41+
return ret, err
3642
}
3743

38-
func readHTTPConnectRequest(rd io.Reader) (host string, err error) {
44+
func (muxer *HTTPConnectTCPMuxer) readHTTPConnectRequest(rd io.Reader) (host string, httpUser string, err error) {
3945
bufioReader := bufio.NewReader(rd)
4046

4147
req, err := http.ReadRequest(bufioReader)
@@ -49,20 +55,40 @@ func readHTTPConnectRequest(rd io.Reader) (host string, err error) {
4955
}
5056

5157
host, _ = util.CanonicalHost(req.Host)
58+
proxyAuth := req.Header.Get("Proxy-Authorization")
59+
if proxyAuth != "" {
60+
httpUser, _, _ = util.ParseBasicAuth(proxyAuth)
61+
}
5262
return
5363
}
5464

55-
func sendHTTPOk(c net.Conn) error {
65+
func (muxer *HTTPConnectTCPMuxer) sendConnectResponse(c net.Conn, reqInfo map[string]string) error {
66+
if muxer.passthrough {
67+
return nil
68+
}
5669
return util.OkResponse().Write(c)
5770
}
5871

59-
func getHostFromHTTPConnect(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
72+
func (muxer *HTTPConnectTCPMuxer) getHostFromHTTPConnect(c net.Conn) (net.Conn, map[string]string, error) {
6073
reqInfoMap := make(map[string]string, 0)
61-
host, err := readHTTPConnectRequest(c)
74+
sc, rd := gnet.NewSharedConn(c)
75+
76+
host, httpUser, err := muxer.readHTTPConnectRequest(rd)
6277
if err != nil {
6378
return nil, reqInfoMap, err
6479
}
80+
6581
reqInfoMap["Host"] = host
6682
reqInfoMap["Scheme"] = "tcp"
67-
return c, reqInfoMap, nil
83+
reqInfoMap["HTTPUser"] = httpUser
84+
85+
var outConn net.Conn = c
86+
if muxer.passthrough {
87+
outConn = sc
88+
if muxer.authRequired && httpUser == "" {
89+
util.ProxyUnauthorizedResponse().Write(c)
90+
outConn = c
91+
}
92+
}
93+
return outConn, reqInfoMap, nil
6894
}

0 commit comments

Comments
 (0)