wss: added a new scheme wss for convenience

This commit is contained in:
nadoo 2021-07-09 19:17:16 +08:00
parent a62674838e
commit 15f9e74e39
11 changed files with 166 additions and 75 deletions

View File

@ -64,10 +64,11 @@ we can set up local listeners as proxy servers, and forward requests to internet
|KCP | |√|√| |transport client & server
|Unix |√|√|√|√|transport client & server
|Smux |√| |√| |transport client & server
|Websocket |√| |√| |transport client & server
|Websocket(WS)|√| |√| |transport client & server
|WS Secure |√| |√| |transport client & server
|Simple-Obfs | | |√| |transport client only
|Redir |√| | | |linux only
|Redir6 |√| | | |linux only(ipv6)
|Redir |√| | | |linux redirect proxy
|Redir6 |√| | | |linux redirect proxy(ipv6)
|Reject | | |√|√|reject all requests
</details>
@ -174,8 +175,8 @@ glider -verbose -listen :8443 -forward SCHEME://HOST:PORT
```bash
Available schemes:
listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcp udp tls ws unix smux kcp
forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws unix smux kcp simple-obfs
listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcp udp tls ws wss unix smux kcp
forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws wss unix smux kcp simple-obfs
Socks5 scheme:
socks://[user:pass@]host:port
@ -235,15 +236,16 @@ Proxy over tls server:
Websocket client scheme:
ws://host:port[/path][?host=HOST][&origin=ORIGIN]
wss://host:port[/path][?serverName=SERVERNAME][&skipVerify=true][&host=HOST][&origin=ORIGIN]
Websocket server scheme:
ws://:port[/path][?host=HOST]
wss://:port[/path]?cert=PATH&key=PATH[?host=HOST]
Websocket with a specified proxy protocol:
ws://host:port[/path][?host=HOST],scheme://
ws://host:port[/path][?host=HOST],http://[user:pass@]
ws://host:port[/path][?host=HOST],socks5://[user:pass@]
ws://host:port[/path][?host=HOST],vmess://[security:]uuid@?alterID=num
TLS and Websocket with a specified proxy protocol:
tls://host:port[?skipVerify=true][&serverName=SERVERNAME],ws://[@/path[?host=HOST]],scheme://
@ -315,37 +317,37 @@ Config file format(see `./glider.conf.example` as an example):
```bash
Examples:
./glider -config glider.conf
glider -config glider.conf
-run glider with specified config file.
./glider -listen :8443 -verbose
glider -listen :8443 -verbose
-listen on :8443, serve as http/socks5 proxy on the same port, in verbose mode.
./glider -listen ss://AEAD_AES_128_GCM:pass@:8443 -verbose
glider -listen ss://AEAD_AES_128_GCM:pass@:8443 -verbose
-listen on 0.0.0.0:8443 as a ss server.
./glider -listen tls://:443?cert=crtFilePath&key=keyFilePath,http:// -verbose
glider -listen tls://:443?cert=crtFilePath&key=keyFilePath,http:// -verbose
-listen on :443 as a https(http over tls) proxy server.
./glider -listen http://:8080 -forward socks5://127.0.0.1:1080
glider -listen http://:8080 -forward socks5://127.0.0.1:1080
-listen on :8080 as a http proxy server, forward all requests via socks5 server.
./glider -listen socks5://:1080 -forward "tls://abc.com:443,vmess://security:uuid@?alterID=10"
glider -listen socks5://:1080 -forward "tls://abc.com:443,vmess://security:uuid@?alterID=10"
-listen on :1080 as a socks5 server, forward all requests via remote tls+vmess server.
./glider -listen socks5://:1080 -forward ss://method:pass@server1:port1 -forward ss://method:pass@server2:port2 -strategy rr
glider -listen socks5://:1080 -forward ss://method:pass@server1:port1 -forward ss://method:pass@server2:port2 -strategy rr
-listen on :1080 as socks5 server, forward requests via server1 and server2 in round robin mode.
./glider -listen tcp://:80 -forward tcp://2.2.2.2:80
glider -listen tcp://:80 -forward tcp://2.2.2.2:80
-tcp tunnel: listen on :80 and forward all requests to 2.2.2.2:80.
./glider -listen udp://:53 -forward ss://method:pass@1.1.1.1:8443,udp://8.8.8.8:53
glider -listen udp://:53 -forward ss://method:pass@1.1.1.1:8443,udp://8.8.8.8:53
-listen on :53 and forward all udp requests to 8.8.8.8:53 via remote ss server.
./glider -listen socks5://:1080 -listen http://:8080 -forward ss://method:pass@1.1.1.1:8443
glider -listen socks5://:1080 -listen http://:8080 -forward ss://method:pass@1.1.1.1:8443
-listen on :1080 as socks5 server, :8080 as http proxy server, forward all requests via remote ss server.
./glider -verbose -listen -dns=:53 -dnsserver=8.8.8.8:53 -forward ss://method:pass@server:port -dnsrecord=www.example.com/1.2.3.4
glider -verbose -listen -dns=:53 -dnsserver=8.8.8.8:53 -forward ss://method:pass@server:port -dnsrecord=www.example.com/1.2.3.4
-listen on :53 as dns server, forward to 8.8.8.8:53 via ss server.
```

View File

@ -132,8 +132,8 @@ func usage() {
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "Available schemes:\n")
fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcp udp tls ws unix smux kcp\n")
fmt.Fprintf(w, " forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws unix smux kcp simple-obfs\n")
fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcp udp tls ws wss unix smux kcp\n")
fmt.Fprintf(w, " forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws wss unix smux kcp simple-obfs\n")
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "Socks5 scheme:\n")
@ -208,17 +208,18 @@ func usage() {
fmt.Fprintf(w, "Websocket client scheme:\n")
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST][&origin=ORIGIN]\n")
fmt.Fprintf(w, " wss://host:port[/path][?serverName=SERVERNAME][&skipVerify=true][&host=HOST][&origin=ORIGIN]\n")
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "Websocket server scheme:\n")
fmt.Fprintf(w, " ws://:port[/path][?host=HOST]\n")
fmt.Fprintf(w, " wss://:port[/path]?cert=PATH&key=PATH[?host=HOST]\n")
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "Websocket with a specified proxy protocol:\n")
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],scheme://\n")
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],http://[user:pass@]\n")
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],socks5://[user:pass@]\n")
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],vmess://[security:]uuid@?alterID=num\n")
fmt.Fprintf(w, "\n")
fmt.Fprintf(w, "TLS and Websocket with a specified proxy protocol:\n")

2
go.mod
View File

@ -14,7 +14,7 @@ require (
github.com/mdlayher/raw v0.0.0-20210412142147-51b895745faf // indirect
github.com/nadoo/conflag v0.2.3
github.com/nadoo/ipset v0.3.0
github.com/tjfoc/gmsm v1.4.0 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
github.com/xtaci/kcp-go/v5 v5.6.1
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e

4
go.sum
View File

@ -86,8 +86,8 @@ github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6H
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.0 h1:8nbaiZG+iVdh+fXVw0DZoZZa7a4TGm3Qab+xdrdzj8s=
github.com/tjfoc/gmsm v1.4.0/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 h1:XMAtQHwKjWHIRwg+8Nj/rzUomQY1q6cM3ncA0wP8GU4=
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=

View File

@ -8,9 +8,12 @@ import (
// F is the main log function.
var F = func(string, ...interface{}) {}
func init() {
stdlog.SetFlags(stdlog.Flags() | stdlog.Lshortfile)
}
// Debugf prints debug log.
func Debugf(f string, v ...interface{}) {
stdlog.SetFlags(stdlog.LstdFlags | stdlog.Lshortfile)
stdlog.Output(2, fmt.Sprintf(f, v...))
}

View File

@ -2,6 +2,7 @@ package trojan
import (
"crypto/tls"
"fmt"
"net"
"github.com/nadoo/glider/log"
@ -14,10 +15,8 @@ import (
func NewClearTextDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
t, err := NewTrojan(s, d, nil)
if err != nil {
log.F("[trojan] create instance error: %s", err)
return nil, err
return nil, fmt.Errorf("[trojanc] create instance error: %s", err)
}
t.withTLS = false
return t, err
}
@ -26,8 +25,7 @@ func NewClearTextDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
func NewTrojanDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
t, err := NewTrojan(s, d, nil)
if err != nil {
log.F("[trojan] create instance error: %s", err)
return nil, err
return nil, fmt.Errorf("[trojan] create instance error: %s", err)
}
t.tlsConfig = &tls.Config{

View File

@ -19,8 +19,7 @@ import (
func NewClearTextServer(s string, p proxy.Proxy) (proxy.Server, error) {
t, err := NewTrojan(s, nil, p)
if err != nil {
log.F("[trojan] create instance error: %s", err)
return nil, err
return nil, fmt.Errorf("[trojanc] create instance error: %s", err)
}
t.withTLS = false
@ -31,8 +30,7 @@ func NewClearTextServer(s string, p proxy.Proxy) (proxy.Server, error) {
func NewTrojanServer(s string, p proxy.Proxy) (proxy.Server, error) {
t, err := NewTrojan(s, nil, p)
if err != nil {
log.F("[trojan] create instance error: %s", err)
return nil, err
return nil, fmt.Errorf("[trojan] create instance error: %s", err)
}
if t.certFile == "" || t.keyFile == "" {
@ -41,8 +39,8 @@ func NewTrojanServer(s string, p proxy.Proxy) (proxy.Server, error) {
cert, err := tls.LoadX509KeyPair(t.certFile, t.keyFile)
if err != nil {
log.F("[trojan] unable to load cert: %s, key %s", t.certFile, t.keyFile)
return nil, err
return nil, fmt.Errorf("[trojan] unable to load cert: %s, key %s, error: %s",
t.certFile, t.keyFile, err)
}
t.tlsConfig = &tls.Config{

View File

@ -55,7 +55,7 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
serverName: query.Get("serverName"),
certFile: query.Get("cert"),
keyFile: query.Get("key"),
// fallback: "127.0.0.1:80",
fallback: query.Get("fallback"),
}
if _, port, _ := net.SplitHostPort(t.addr); port == "" {
@ -76,9 +76,5 @@ func NewTrojan(s string, d proxy.Dialer, p proxy.Proxy) (*Trojan, error) {
hash.Write([]byte(pass))
hex.Encode(t.pass[:], hash.Sum(nil))
if fb := u.Query().Get("fallback"); fb != "" {
t.fallback = fb
}
return t, nil
}

View File

@ -1,7 +1,9 @@
package ws
import (
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"net/textproto"
@ -10,13 +12,30 @@ import (
"github.com/nadoo/glider/proxy"
)
func init() {
proxy.RegisterDialer("ws", NewWSDialer)
}
// NewWSDialer returns a ws proxy dialer.
func NewWSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
return NewWS(s, d, nil)
w, err := NewWS(s, d, nil, false)
if err != nil {
return nil, fmt.Errorf("[ws] create instance error: %s", err)
}
return w, err
}
// NewWSSDialer returns a wss proxy dialer.
func NewWSSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
w, err := NewWS(s, d, nil, true)
if err != nil {
return nil, fmt.Errorf("[wss] create instance error: %s", err)
}
w.tlsConfig = &tls.Config{
ServerName: w.serverName,
InsecureSkipVerify: w.skipVerify,
NextProtos: []string{"http/1.1"},
MinVersion: tls.VersionTLS12,
}
return w, err
}
// Addr returns forwarder's address.
@ -34,6 +53,14 @@ func (s *WS) Dial(network, addr string) (net.Conn, error) {
return nil, err
}
if s.withTLS {
tlsConn := tls.Client(rc, s.tlsConfig)
if err := tlsConn.Handshake(); err != nil {
return nil, err
}
rc = tlsConn
}
return s.NewClientConn(rc)
}

View File

@ -1,7 +1,9 @@
package ws
import (
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"net/textproto"
@ -12,16 +14,12 @@ import (
"github.com/nadoo/glider/proxy"
)
func init() {
proxy.RegisterServer("ws", NewWSServer)
}
// NewWSServer returns a ws transport server.
func NewWSServer(s string, p proxy.Proxy) (proxy.Server, error) {
schemes := strings.SplitN(s, ",", 2)
w, err := NewWS(schemes[0], nil, p)
w, err := NewWS(schemes[0], nil, p, false)
if err != nil {
return nil, err
return nil, fmt.Errorf("[ws] create instance error: %s", err)
}
if len(schemes) > 1 {
@ -34,6 +32,39 @@ func NewWSServer(s string, p proxy.Proxy) (proxy.Server, error) {
return w, nil
}
// NewWSSServer returns a wss transport server.
func NewWSSServer(s string, p proxy.Proxy) (proxy.Server, error) {
schemes := strings.SplitN(s, ",", 2)
w, err := NewWS(schemes[0], nil, p, true)
if err != nil {
return nil, fmt.Errorf("[wss] create instance error: %s", err)
}
if len(schemes) > 1 {
w.server, err = proxy.ServerFromURL(schemes[1], p)
if err != nil {
return nil, err
}
}
if w.certFile == "" || w.keyFile == "" {
return nil, errors.New("[wss] cert and key file path must be spcified")
}
cert, err := tls.LoadX509KeyPair(w.certFile, w.keyFile)
if err != nil {
return nil, fmt.Errorf("[ws] unable to load cert: %s, key %s, error: %s",
w.certFile, w.keyFile, err)
}
w.tlsConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
return w, nil
}
// ListenAndServe listens on server's addr and serves connections.
func (s *WS) ListenAndServe() {
l, err := net.Listen("tcp", s.addr)
@ -43,7 +74,7 @@ func (s *WS) ListenAndServe() {
}
defer l.Close()
log.F("[ws] listening TCP on %s", s.addr)
log.F("[ws] listening TCP on %s, with TLS: %v", s.addr, s.withTLS)
for {
c, err := l.Accept()
@ -58,6 +89,16 @@ func (s *WS) ListenAndServe() {
// Serve serves a connection.
func (s *WS) Serve(cc net.Conn) {
if s.withTLS {
tlsConn := tls.Server(cc, s.tlsConfig)
err := tlsConn.Handshake()
if err != nil {
log.F("[ws] error in tls handshake: %s", err)
return
}
cc = tlsConn
}
c, err := s.NewServerConn(cc)
if err != nil {
log.F("[ws] handshake error: %s", err)

View File

@ -3,65 +3,90 @@ package ws
import (
"crypto/rand"
"crypto/sha1"
"crypto/tls"
"encoding/base64"
"fmt"
"net"
"net/url"
"strings"
"github.com/nadoo/glider/log"
"github.com/nadoo/glider/pool"
"github.com/nadoo/glider/proxy"
)
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
func init() {
proxy.RegisterDialer("ws", NewWSDialer)
proxy.RegisterServer("ws", NewWSServer)
proxy.RegisterDialer("wss", NewWSSDialer)
proxy.RegisterServer("wss", NewWSSServer)
}
// WS is the base ws proxy struct.
type WS struct {
dialer proxy.Dialer
proxy proxy.Proxy
addr string
host string
path string
origin string
server proxy.Server
dialer proxy.Dialer
proxy proxy.Proxy
addr string
host string
path string
origin string
withTLS bool
tlsConfig *tls.Config
serverName string
skipVerify bool
certFile string
keyFile string
server proxy.Server
}
// NewWS returns a websocket proxy.
func NewWS(s string, d proxy.Dialer, p proxy.Proxy) (*WS, error) {
func NewWS(s string, d proxy.Dialer, p proxy.Proxy, withTLS bool) (*WS, error) {
u, err := url.Parse(s)
if err != nil {
log.F("[ws] parse url err: %s", err)
return nil, err
return nil, fmt.Errorf("parse url err: %s", err)
}
addr := u.Host
if addr != "" {
if _, port, _ := net.SplitHostPort(addr); port == "" {
if addr == "" && d != nil {
addr = d.Addr()
}
if _, p, _ := net.SplitHostPort(addr); p == "" {
if withTLS {
addr = net.JoinHostPort(addr, "443")
} else {
addr = net.JoinHostPort(addr, "80")
}
} else if d != nil {
addr = d.Addr()
}
query := u.Query()
w := &WS{
dialer: d,
proxy: p,
addr: addr,
path: u.Path,
host: query.Get("host"),
origin: query.Get("origin"),
dialer: d,
proxy: p,
addr: addr,
path: u.Path,
host: query.Get("host"),
origin: query.Get("origin"),
withTLS: withTLS,
skipVerify: query.Get("skipVerify") == "true",
serverName: query.Get("serverName"),
certFile: query.Get("cert"),
keyFile: query.Get("key"),
}
if w.host == "" {
w.host = addr
w.host = w.addr
}
if w.path == "" {
w.path = "/"
}
if w.serverName == "" {
w.serverName = w.addr[:strings.LastIndex(w.addr, ":")]
}
return w, nil
}