glider/proxy/ws/ws.go

141 lines
3.3 KiB
Go
Raw Normal View History

2018-07-21 22:56:37 +08:00
package ws
import (
2020-10-19 20:45:57 +08:00
"crypto/rand"
"crypto/sha1"
"crypto/tls"
2020-10-19 20:45:57 +08:00
"encoding/base64"
"fmt"
2021-07-06 20:31:39 +08:00
"net"
2018-07-21 22:56:37 +08:00
"net/url"
2020-10-19 20:45:57 +08:00
"strings"
2018-07-21 22:56:37 +08:00
"github.com/nadoo/glider/pkg/pool"
2018-07-21 22:56:37 +08:00
"github.com/nadoo/glider/proxy"
)
2020-10-19 20:45:57 +08:00
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)
}
2019-09-07 17:17:38 +08:00
// WS is the base ws proxy struct.
2018-07-21 22:56:37 +08:00
type WS struct {
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
}
2018-07-21 22:56:37 +08:00
// NewWS returns a websocket proxy.
func NewWS(s string, d proxy.Dialer, p proxy.Proxy, withTLS bool) (*WS, error) {
2018-07-21 22:56:37 +08:00
u, err := url.Parse(s)
if err != nil {
return nil, fmt.Errorf("parse url err: %s", err)
2018-07-21 22:56:37 +08:00
}
addr := u.Host
if addr == "" && d != nil {
addr = d.Addr()
}
if _, p, _ := net.SplitHostPort(addr); p == "" {
if withTLS {
addr = net.JoinHostPort(addr, "443")
} else {
2021-07-06 20:31:39 +08:00
addr = net.JoinHostPort(addr, "80")
}
}
2021-07-06 20:31:39 +08:00
query := u.Query()
2020-10-19 20:45:57 +08:00
w := &WS{
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"),
}
2020-10-19 20:45:57 +08:00
if w.host == "" {
w.host = w.addr
}
2020-10-19 20:45:57 +08:00
if w.path == "" {
w.path = "/"
2018-07-21 22:56:37 +08:00
}
if w.serverName == "" {
w.serverName = w.addr[:strings.LastIndex(w.addr, ":")]
}
2020-10-19 20:45:57 +08:00
return w, nil
2018-07-21 22:56:37 +08:00
}
2020-10-19 20:45:57 +08:00
// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts.
// TODO: move to separate http lib package for reuse(also for http proxy module)
func parseFirstLine(line string) (r1, r2, r3 string, ok bool) {
s1 := strings.Index(line, " ")
s2 := strings.Index(line[s1+1:], " ")
if s1 < 0 || s2 < 0 {
return
}
2020-10-19 20:45:57 +08:00
s2 += s1 + 1
return line[:s1], line[s1+1 : s2], line[s2+1:], true
}
2020-10-19 20:45:57 +08:00
func generateClientKey() string {
p := pool.GetBuffer(16)
defer pool.PutBuffer(p)
rand.Read(p)
return base64.StdEncoding.EncodeToString(p)
}
2020-10-19 20:45:57 +08:00
func computeServerKey(clientKey string) string {
h := sha1.New()
h.Write([]byte(clientKey))
h.Write(keyGUID)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
2022-02-15 21:34:55 +08:00
func init() {
proxy.AddUsage("ws", `
Websocket client scheme:
ws://host:port[/path][?host=HOST][&origin=ORIGIN]
wss://host:port[/path][?serverName=SERVERNAME][&skipVerify=true][&cert=PATH][&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@]
TLS and Websocket with a specified proxy protocol:
tls://host:port[?skipVerify=true][&serverName=SERVERNAME],ws://[@/path[?host=HOST]],scheme://
tls://host:port[?skipVerify=true],ws://[@/path[?host=HOST]],http://[user:pass@]
tls://host:port[?skipVerify=true],ws://[@/path[?host=HOST]],socks5://[user:pass@]
tls://host:port[?skipVerify=true],ws://[@/path[?host=HOST]],vmess://[security:]uuid@?alterID=num
`)
}