Skip to content

Commit 2384484

Browse files
Wss support (#3)
* added wss support * changed entrypoints in Dockerfiles to include config from .ini files Signed-off-by: Daniel Soifer <[email protected]>
1 parent 2158253 commit 2384484

File tree

8 files changed

+66
-16
lines changed

8 files changed

+66
-16
lines changed

client/control.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"crypto/tls"
2020
"io"
21+
"math"
2122
"net"
2223
"runtime/debug"
2324
"strconv"
@@ -242,9 +243,21 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
242243
}
243244
dialOptions := []libdial.DialOption{}
244245
protocol := ctl.clientCfg.Protocol
245-
if protocol == "websocket" {
246+
var websocketAfterHook *libdial.AfterHook
247+
if protocol == "websocket" || protocol == "wss" {
248+
if protocol == "wss" {
249+
websocketAfterHook = &libdial.AfterHook{
250+
Priority: math.MaxUint64, // in case of wss, we first want to make the TLS handshake and then switch protocols from https to wss
251+
Hook: frpNet.DialHookWebsocket(true),
252+
}
253+
} else {
254+
websocketAfterHook = &libdial.AfterHook{
255+
Priority: 0, // in case of ws, we first want to switch protocols from http to ws, and only then make the TLS handshake in case TLS is enabled
256+
Hook: frpNet.DialHookWebsocket(false),
257+
}
258+
}
259+
246260
protocol = "tcp"
247-
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
248261
}
249262
if ctl.clientCfg.ConnectServerLocalIP != "" {
250263
dialOptions = append(dialOptions, libdial.WithLocalAddr(ctl.clientCfg.ConnectServerLocalIP))
@@ -255,11 +268,18 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
255268
libdial.WithKeepAlive(time.Duration(ctl.clientCfg.DialServerKeepAlive)*time.Second),
256269
libdial.WithProxy(proxyType, addr),
257270
libdial.WithProxyAuth(auth),
258-
libdial.WithTLSConfig(tlsConfig),
271+
libdial.WithTLSConfig(tlsConfig), // TLS AfterHook has math.MaxUint64 priority
259272
libdial.WithAfterHook(libdial.AfterHook{
260-
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, ctl.clientCfg.DisableCustomTLSFirstByte),
273+
Priority: 1, // should be executed before TLS AfterHook but after the rest of the AfterHooks (except for wss)
274+
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, ctl.clientCfg.DisableCustomTLSFirstByte),
261275
}),
262276
)
277+
if websocketAfterHook != nil {
278+
// websocketAfterHook must be appended after TLS AfterHook because they both might have the
279+
// same priority of math.MaxUint64 in case of wss but TLS AfterHook must be executed first
280+
dialOptions = append(dialOptions, libdial.WithAfterHook(*websocketAfterHook))
281+
}
282+
263283
conn, err = libdial.Dial(
264284
net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)),
265285
dialOptions...,

client/service.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"crypto/tls"
2020
"fmt"
2121
"io"
22+
"math"
2223
"math/rand"
2324
"net"
2425
"runtime"
@@ -256,9 +257,21 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
256257
}
257258
dialOptions := []libdial.DialOption{}
258259
protocol := svr.cfg.Protocol
259-
if protocol == "websocket" {
260+
var websocketAfterHook *libdial.AfterHook
261+
if protocol == "websocket" || protocol == "wss" {
262+
if protocol == "wss" {
263+
websocketAfterHook = &libdial.AfterHook{
264+
Priority: math.MaxUint64, // in case of wss, we first want to make the TLS handshake and then switch protocols from https to wss
265+
Hook: frpNet.DialHookWebsocket(true),
266+
}
267+
} else {
268+
websocketAfterHook = &libdial.AfterHook{
269+
Priority: 0, // in case of ws, we first want to switch protocols from http to ws, and only then make the TLS handshake in case TLS is enabled
270+
Hook: frpNet.DialHookWebsocket(false),
271+
}
272+
}
273+
260274
protocol = "tcp"
261-
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
262275
}
263276
if svr.cfg.ConnectServerLocalIP != "" {
264277
dialOptions = append(dialOptions, libdial.WithLocalAddr(svr.cfg.ConnectServerLocalIP))
@@ -269,11 +282,18 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
269282
libdial.WithKeepAlive(time.Duration(svr.cfg.DialServerKeepAlive)*time.Second),
270283
libdial.WithProxy(proxyType, addr),
271284
libdial.WithProxyAuth(auth),
272-
libdial.WithTLSConfig(tlsConfig),
285+
libdial.WithTLSConfig(tlsConfig), // TLS AfterHook has math.MaxUint64 priority
273286
libdial.WithAfterHook(libdial.AfterHook{
274-
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, svr.cfg.DisableCustomTLSFirstByte),
287+
Priority: 1, // should be executed before TLS AfterHook but after the rest of the AfterHooks (except for wss)
288+
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, svr.cfg.DisableCustomTLSFirstByte),
275289
}),
276290
)
291+
if websocketAfterHook != nil {
292+
// websocketAfterHook must be appended after TLS AfterHook because they both might have the
293+
// same priority of math.MaxUint64 in case of wss but TLS AfterHook must be executed first
294+
dialOptions = append(dialOptions, libdial.WithAfterHook(*websocketAfterHook))
295+
}
296+
277297
conn, err = libdial.Dial(
278298
net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)),
279299
dialOptions...,

cmd/frpc/sub/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func init() {
8585
func RegisterCommonFlags(cmd *cobra.Command) {
8686
cmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
8787
cmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
88-
cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket")
88+
cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket or wss")
8989
cmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
9090
cmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
9191
cmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")

conf/frpc_full.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ user = your_name
8787
login_fail_exit = true
8888

8989
# communication protocol used to connect to server
90-
# now it supports tcp, kcp and websocket, default is tcp
90+
# now it supports tcp, kcp, websocket and wss, default is tcp
9191
protocol = tcp
9292

9393
# set client binding ip when connect server, default is empty.

dockerfiles/Dockerfile-for-frpc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ FROM alpine:3
99

1010
COPY --from=building /building/bin/frpc /usr/bin/frpc
1111

12-
ENTRYPOINT ["/usr/bin/frpc"]
12+
ENTRYPOINT /usr/bin/frpc -c /etc/frp/frpc.ini

dockerfiles/Dockerfile-for-frps

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ FROM alpine:3
99

1010
COPY --from=building /building/bin/frps /usr/bin/frps
1111

12-
ENTRYPOINT ["/usr/bin/frps"]
12+
ENTRYPOINT /usr/bin/frps -c /etc/frp/frps.ini

pkg/config/client.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ func (cfg *ClientCommonConf) Validate() error {
215215
}
216216

217217
if cfg.TLSEnable == false {
218+
if cfg.Protocol == "wss" {
219+
return fmt.Errorf("tls_enable must be true for wss support")
220+
}
221+
218222
if cfg.TLSCertFile != "" {
219223
fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
220224
}
@@ -228,7 +232,7 @@ func (cfg *ClientCommonConf) Validate() error {
228232
}
229233
}
230234

231-
if cfg.Protocol != "tcp" && cfg.Protocol != "kcp" && cfg.Protocol != "websocket" {
235+
if cfg.Protocol != "tcp" && cfg.Protocol != "kcp" && cfg.Protocol != "websocket" && cfg.Protocol != "wss" {
232236
return fmt.Errorf("invalid protocol")
233237
}
234238

pkg/util/net/dial.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,21 @@ func DialHookCustomTLSHeadByte(enableTLS bool, disableCustomTLSHeadByte bool) li
2121
}
2222
}
2323

24-
func DialHookWebsocket() libdial.AfterHookFunc {
24+
func DialHookWebsocket(isSecure bool) libdial.AfterHookFunc {
2525
return func(ctx context.Context, c net.Conn, addr string) (context.Context, net.Conn, error) {
26-
addr = "ws://" + addr + FrpWebsocketPath
26+
addrScheme := "ws"
27+
originScheme := "http"
28+
if isSecure {
29+
addrScheme = "wss"
30+
originScheme = "https"
31+
}
32+
addr = addrScheme + "://" + addr + FrpWebsocketPath
2733
uri, err := url.Parse(addr)
2834
if err != nil {
2935
return nil, nil, err
3036
}
3137

32-
origin := "http://" + uri.Host
38+
origin := originScheme + "://" + uri.Host
3339
cfg, err := websocket.NewConfig(addr, origin)
3440
if err != nil {
3541
return nil, nil, err

0 commit comments

Comments
 (0)