Skip to content

Commit ff89bf0

Browse files
authored
feat: add gost-plugin in which only ws and mws are currently supported. (#1896)
1 parent 801f3c3 commit ff89bf0

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

adapter/outbound/shadowsocks.go

+44-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/metacubex/mihomo/component/proxydialer"
1414
"github.com/metacubex/mihomo/component/resolver"
1515
C "github.com/metacubex/mihomo/constant"
16+
gost "github.com/metacubex/mihomo/transport/gost-plugin"
1617
"github.com/metacubex/mihomo/transport/restls"
1718
obfs "github.com/metacubex/mihomo/transport/simple-obfs"
1819
shadowtls "github.com/metacubex/mihomo/transport/sing-shadowtls"
@@ -34,6 +35,7 @@ type ShadowSocks struct {
3435
obfsMode string
3536
obfsOption *simpleObfsOption
3637
v2rayOption *v2rayObfs.Option
38+
gostOption *gost.Option
3739
shadowTLSOption *shadowtls.ShadowTLSOption
3840
restlsConfig *restlsC.Config
3941
}
@@ -71,6 +73,17 @@ type v2rayObfsOption struct {
7173
V2rayHttpUpgradeFastOpen bool `obfs:"v2ray-http-upgrade-fast-open,omitempty"`
7274
}
7375

76+
type gostObfsOption struct {
77+
Mode string `obfs:"mode"`
78+
Host string `obfs:"host,omitempty"`
79+
Path string `obfs:"path,omitempty"`
80+
TLS bool `obfs:"tls,omitempty"`
81+
Fingerprint string `obfs:"fingerprint,omitempty"`
82+
Headers map[string]string `obfs:"headers,omitempty"`
83+
SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"`
84+
Mux bool `obfs:"mux,omitempty"`
85+
}
86+
7487
type shadowTLSOption struct {
7588
Password string `obfs:"password"`
7689
Host string `obfs:"host"`
@@ -97,7 +110,13 @@ func (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metada
97110
c = obfs.NewHTTPObfs(c, ss.obfsOption.Host, port)
98111
case "websocket":
99112
var err error
100-
c, err = v2rayObfs.NewV2rayObfs(ctx, c, ss.v2rayOption)
113+
if ss.v2rayOption != nil {
114+
c, err = v2rayObfs.NewV2rayObfs(ctx, c, ss.v2rayOption)
115+
} else if ss.gostOption != nil {
116+
c, err = gost.NewGostWebsocket(ctx, c, ss.gostOption)
117+
} else {
118+
return nil, fmt.Errorf("plugin options is required")
119+
}
101120
if err != nil {
102121
return nil, fmt.Errorf("%s connect error: %w", ss.addr, err)
103122
}
@@ -240,6 +259,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
240259
}
241260

242261
var v2rayOption *v2rayObfs.Option
262+
var gostOption *gost.Option
243263
var obfsOption *simpleObfsOption
244264
var shadowTLSOpt *shadowtls.ShadowTLSOption
245265
var restlsConfig *restlsC.Config
@@ -281,6 +301,28 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
281301
v2rayOption.SkipCertVerify = opts.SkipCertVerify
282302
v2rayOption.Fingerprint = opts.Fingerprint
283303
}
304+
} else if option.Plugin == "gost-plugin" {
305+
opts := gostObfsOption{Host: "bing.com", Mux: true}
306+
if err := decoder.Decode(option.PluginOpts, &opts); err != nil {
307+
return nil, fmt.Errorf("ss %s initialize gost-plugin error: %w", addr, err)
308+
}
309+
310+
if opts.Mode != "websocket" {
311+
return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
312+
}
313+
obfsMode = opts.Mode
314+
gostOption = &gost.Option{
315+
Host: opts.Host,
316+
Path: opts.Path,
317+
Headers: opts.Headers,
318+
Mux: opts.Mux,
319+
}
320+
321+
if opts.TLS {
322+
gostOption.TLS = true
323+
gostOption.SkipCertVerify = opts.SkipCertVerify
324+
gostOption.Fingerprint = opts.Fingerprint
325+
}
284326
} else if option.Plugin == shadowtls.Mode {
285327
obfsMode = shadowtls.Mode
286328
opt := &shadowTLSOption{
@@ -336,6 +378,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
336378
option: &option,
337379
obfsMode: obfsMode,
338380
v2rayOption: v2rayOption,
381+
gostOption: gostOption,
339382
obfsOption: obfsOption,
340383
shadowTLSOption: shadowTLSOpt,
341384
restlsConfig: restlsConfig,

docs/config.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,26 @@ proxies: # socks5
449449
password: "shadow_tls_password"
450450
version: 2 # support 1/2/3
451451

452+
- name: "ss5"
453+
type: ss
454+
server: server
455+
port: 443
456+
cipher: chacha20-ietf-poly1305
457+
password: "password"
458+
plugin: gost-plugin
459+
plugin-opts:
460+
mode: websocket
461+
# tls: true # wss
462+
# 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取
463+
# 配置指纹将实现 SSL Pining 效果
464+
# fingerprint: xxxx
465+
# skip-cert-verify: true
466+
# host: bing.com
467+
# path: "/"
468+
# mux: true
469+
# headers:
470+
# custom: value
471+
452472
- name: "ss-restls-tls13"
453473
type: ss
454474
server: [YOUR_SERVER_IP]

transport/gost-plugin/websocket.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package gost
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"net"
7+
"net/http"
8+
9+
"github.com/metacubex/mihomo/component/ca"
10+
"github.com/metacubex/mihomo/transport/vmess"
11+
smux "github.com/sagernet/smux"
12+
)
13+
14+
// Option is options of gost websocket
15+
type Option struct {
16+
Host string
17+
Port string
18+
Path string
19+
Headers map[string]string
20+
TLS bool
21+
SkipCertVerify bool
22+
Fingerprint string
23+
Mux bool
24+
}
25+
26+
// NewGostWebsocket return a gost websocket
27+
func NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.Conn, error) {
28+
header := http.Header{}
29+
for k, v := range option.Headers {
30+
header.Add(k, v)
31+
}
32+
33+
config := &vmess.WebsocketConfig{
34+
Host: option.Host,
35+
Port: option.Port,
36+
Path: option.Path,
37+
Headers: header,
38+
}
39+
40+
if option.TLS {
41+
config.TLS = true
42+
tlsConfig := &tls.Config{
43+
ServerName: option.Host,
44+
InsecureSkipVerify: option.SkipCertVerify,
45+
NextProtos: []string{"http/1.1"},
46+
}
47+
var err error
48+
config.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
if host := config.Headers.Get("Host"); host != "" {
54+
config.TLSConfig.ServerName = host
55+
}
56+
}
57+
58+
var err error
59+
conn, err = vmess.StreamWebsocketConn(ctx, conn, config)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
if option.Mux {
65+
config := smux.DefaultConfig()
66+
config.KeepAliveDisabled = true
67+
68+
session, err := smux.Client(conn, config)
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
stream, err := session.OpenStream()
74+
if err != nil {
75+
return nil, err
76+
}
77+
78+
conn = stream
79+
}
80+
return conn, nil
81+
}

0 commit comments

Comments
 (0)