mirror of
https://github.com/nadoo/glider.git
synced 2025-02-24 01:45:39 +08:00
ws: add support for security key check
This commit is contained in:
parent
68d8fb8b60
commit
88eee75aa7
@ -155,7 +155,7 @@ TLS scheme:
|
|||||||
tls://host:port[?skipVerify=true]
|
tls://host:port[?skipVerify=true]
|
||||||
|
|
||||||
TLS with a specified proxy protocol:
|
TLS with a specified proxy protocol:
|
||||||
tls://host:port[?skipVerify=true],proxy://scheme
|
tls://host:port[?skipVerify=true],scheme://
|
||||||
tls://host:port[?skipVerify=true],http://[user:pass@]
|
tls://host:port[?skipVerify=true],http://[user:pass@]
|
||||||
tls://host:port[?skipVerify=true],socks5://[user:pass@]
|
tls://host:port[?skipVerify=true],socks5://[user:pass@]
|
||||||
tls://host:port[?skipVerify=true],vmess://[security:]uuid@?alterID=num
|
tls://host:port[?skipVerify=true],vmess://[security:]uuid@?alterID=num
|
||||||
@ -170,7 +170,7 @@ Websocket with a specified proxy protocol:
|
|||||||
ws://host:port[/path],vmess://[security:]uuid@?alterID=num
|
ws://host:port[/path],vmess://[security:]uuid@?alterID=num
|
||||||
|
|
||||||
TLS and Websocket with a specified proxy protocol:
|
TLS and Websocket with a specified proxy protocol:
|
||||||
tls://host:port[?skipVerify=true],ws://[@/path],proxy://scheme
|
tls://host:port[?skipVerify=true],ws://[@/path],scheme://
|
||||||
tls://host:port[?skipVerify=true],ws://[@/path],http://[user:pass@]
|
tls://host:port[?skipVerify=true],ws://[@/path],http://[user:pass@]
|
||||||
tls://host:port[?skipVerify=true],ws://[@/path],socks5://[user:pass@]
|
tls://host:port[?skipVerify=true],ws://[@/path],socks5://[user:pass@]
|
||||||
tls://host:port[?skipVerify=true],ws://[@/path],vmess://[security:]uuid@?alterID=num
|
tls://host:port[?skipVerify=true],ws://[@/path],vmess://[security:]uuid@?alterID=num
|
||||||
@ -211,7 +211,10 @@ Examples:
|
|||||||
-listen on :1081 as a transparent redirect server, forward all requests via remote ssr server.
|
-listen on :1081 as a transparent redirect server, forward all requests via remote ssr server.
|
||||||
|
|
||||||
glider -listen redir://:1081 -forward "tls://1.1.1.1:443,vmess://security:uuid@?alterID=10"
|
glider -listen redir://:1081 -forward "tls://1.1.1.1:443,vmess://security:uuid@?alterID=10"
|
||||||
-listen on :1081 as a transparent redirect server, forward all requests via remote vmess server.
|
-listen on :1081 as a transparent redirect server, forward all requests via remote tls+vmess server.
|
||||||
|
|
||||||
|
glider -listen redir://:1081 -forward "ws://1.1.1.1:80,vmess://security:uuid@?alterID=10"
|
||||||
|
-listen on :1081 as a transparent redirect server, forward all requests via remote ws+vmess server.
|
||||||
|
|
||||||
glider -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443
|
glider -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443
|
||||||
-listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server.
|
-listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server.
|
||||||
|
11
conf.go
11
conf.go
@ -201,7 +201,7 @@ func usage() {
|
|||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "TLS with a specified proxy protocol:\n")
|
fmt.Fprintf(os.Stderr, "TLS with a specified proxy protocol:\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],proxy://scheme\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],scheme://\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],http://[user:pass@]\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],http://[user:pass@]\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],socks5://[user:pass@]\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],socks5://[user:pass@]\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],vmess://[security:]uuid@?alterID=num\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],vmess://[security:]uuid@?alterID=num\n")
|
||||||
@ -212,14 +212,14 @@ func usage() {
|
|||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "Websocket with a specified proxy protocol:\n")
|
fmt.Fprintf(os.Stderr, "Websocket with a specified proxy protocol:\n")
|
||||||
fmt.Fprintf(os.Stderr, " ws://host:port[/path],proxy://scheme\n")
|
fmt.Fprintf(os.Stderr, " ws://host:port[/path],scheme://\n")
|
||||||
fmt.Fprintf(os.Stderr, " ws://host:port[/path],http://[user:pass@]\n")
|
fmt.Fprintf(os.Stderr, " ws://host:port[/path],http://[user:pass@]\n")
|
||||||
fmt.Fprintf(os.Stderr, " ws://host:port[/path],socks5://[user:pass@]\n")
|
fmt.Fprintf(os.Stderr, " ws://host:port[/path],socks5://[user:pass@]\n")
|
||||||
fmt.Fprintf(os.Stderr, " ws://host:port[/path],vmess://[security:]uuid@?alterID=num\n")
|
fmt.Fprintf(os.Stderr, " ws://host:port[/path],vmess://[security:]uuid@?alterID=num\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "TLS and Websocket with a specified proxy protocol:\n")
|
fmt.Fprintf(os.Stderr, "TLS and Websocket with a specified proxy protocol:\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],proxy://scheme\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],scheme://\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],http://[user:pass@]\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],http://[user:pass@]\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],socks5://[user:pass@]\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],socks5://[user:pass@]\n")
|
||||||
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],vmess://[security:]uuid@?alterID=num\n")
|
fmt.Fprintf(os.Stderr, " tls://host:port[?skipVerify=true],ws://[@/path],vmess://[security:]uuid@?alterID=num\n")
|
||||||
@ -263,7 +263,10 @@ func usage() {
|
|||||||
fmt.Fprintf(os.Stderr, " -listen on :1081 as a transparent redirect server, forward all requests via remote ssr server.\n")
|
fmt.Fprintf(os.Stderr, " -listen on :1081 as a transparent redirect server, forward all requests via remote ssr server.\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
fmt.Fprintf(os.Stderr, " "+app+" -listen redir://:1081 -forward \"tls://1.1.1.1:443,vmess://security:uuid@?alterID=10\"\n")
|
fmt.Fprintf(os.Stderr, " "+app+" -listen redir://:1081 -forward \"tls://1.1.1.1:443,vmess://security:uuid@?alterID=10\"\n")
|
||||||
fmt.Fprintf(os.Stderr, " -listen on :1081 as a transparent redirect server, forward all requests via remote vmess server.\n")
|
fmt.Fprintf(os.Stderr, " -listen on :1081 as a transparent redirect server, forward all requests via remote tls+vmess server.\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " "+app+" -listen redir://:1081 -forward \"ws://1.1.1.1:80,vmess://security:uuid@?alterID=10\"\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " -listen on :1081 as a transparent redirect server, forward all requests via remote ws+vmess server.\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
fmt.Fprintf(os.Stderr, " "+app+" -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443\n")
|
fmt.Fprintf(os.Stderr, " "+app+" -listen tcptun://:80=2.2.2.2:80 -forward ss://method:pass@1.1.1.1:8443\n")
|
||||||
fmt.Fprintf(os.Stderr, " -listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server.\n")
|
fmt.Fprintf(os.Stderr, " -listen on :80 and forward all requests to 2.2.2.2:80 via remote ss server.\n")
|
||||||
|
6
dev.go
6
dev.go
@ -3,6 +3,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
|
|
||||||
@ -11,6 +12,9 @@ import (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go func() {
|
go func() {
|
||||||
http.ListenAndServe(":6060", nil)
|
err := http.ListenAndServe(":6060", nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Create pprof server error: %s\n", err)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -63,24 +63,19 @@ func (w *chunkedWriter) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
type chunkedReader struct {
|
type chunkedReader struct {
|
||||||
io.Reader
|
io.Reader
|
||||||
buf []byte
|
buf []byte
|
||||||
leftover []byte
|
leftBytes int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChunkedReader returns a chunked reader
|
// ChunkedReader returns a chunked reader
|
||||||
func ChunkedReader(r io.Reader) io.Reader {
|
func ChunkedReader(r io.Reader) io.Reader {
|
||||||
return &chunkedReader{
|
return &chunkedReader{
|
||||||
Reader: r,
|
Reader: r,
|
||||||
buf: make([]byte, lenSize+maxChunkSize),
|
buf: make([]byte, lenSize), // NOTE: buf only used to save header bytes now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *chunkedReader) Read(b []byte) (int, error) {
|
func (r *chunkedReader) Read(b []byte) (int, error) {
|
||||||
if len(r.leftover) > 0 {
|
if r.leftBytes == 0 {
|
||||||
n := copy(b, r.leftover)
|
|
||||||
r.leftover = r.leftover[n:]
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// get length
|
// get length
|
||||||
_, err := io.ReadFull(r.Reader, r.buf[:lenSize])
|
_, err := io.ReadFull(r.Reader, r.buf[:lenSize])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -88,21 +83,22 @@ func (r *chunkedReader) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if length == 0, then this is the end
|
// if length == 0, then this is the end
|
||||||
len := binary.BigEndian.Uint16(r.buf[:lenSize])
|
r.leftBytes = int(binary.BigEndian.Uint16(r.buf[:lenSize]))
|
||||||
if len == 0 {
|
if r.leftBytes == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get payload
|
readLen := len(b)
|
||||||
_, err = io.ReadFull(r.Reader, r.buf[:len])
|
if readLen > r.leftBytes {
|
||||||
|
readLen = r.leftBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := r.Reader.Read(b[:readLen])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m := copy(b, r.buf[:len])
|
r.leftBytes -= m
|
||||||
if m < int(len) {
|
|
||||||
r.leftover = r.buf[m:len]
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,19 @@ package ws
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/nadoo/glider/common/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
|
|
||||||
// Client ws client
|
// Client ws client
|
||||||
type Client struct {
|
type Client struct {
|
||||||
host string
|
host string
|
||||||
@ -41,31 +45,38 @@ func (c *Client) NewConn(rc net.Conn, target string) (*Conn, error) {
|
|||||||
|
|
||||||
// Handshake handshakes with the server using HTTP to request a protocol upgrade
|
// Handshake handshakes with the server using HTTP to request a protocol upgrade
|
||||||
func (c *Conn) Handshake(host, path string) error {
|
func (c *Conn) Handshake(host, path string) error {
|
||||||
c.Conn.Write([]byte("GET " + path + " HTTP/1.1\r\n"))
|
clientKey := generateClientKey()
|
||||||
// c.Conn.Write([]byte("Host: 127.0.0.1\r\n"))
|
|
||||||
c.Conn.Write([]byte("Host: " + host + "\r\n"))
|
var buf bytes.Buffer
|
||||||
c.Conn.Write([]byte("Upgrade: websocket\r\n"))
|
buf.Write([]byte("GET " + path + " HTTP/1.1\r\n"))
|
||||||
c.Conn.Write([]byte("Connection: Upgrade\r\n"))
|
buf.Write([]byte("Host: " + host + "\r\n"))
|
||||||
c.Conn.Write([]byte("Origin: http://" + host + "\r\n"))
|
buf.Write([]byte("Upgrade: websocket\r\n"))
|
||||||
c.Conn.Write([]byte("Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==\r\n"))
|
buf.Write([]byte("Connection: Upgrade\r\n"))
|
||||||
c.Conn.Write([]byte("Sec-WebSocket-Protocol: binary\r\n"))
|
buf.Write([]byte("Origin: http://" + host + "\r\n"))
|
||||||
c.Conn.Write([]byte("Sec-WebSocket-Version: 13\r\n"))
|
buf.Write([]byte("Sec-WebSocket-Key: " + clientKey + "\r\n"))
|
||||||
c.Conn.Write([]byte("\r\n"))
|
buf.Write([]byte("Sec-WebSocket-Protocol: binary\r\n"))
|
||||||
|
buf.Write([]byte("Sec-WebSocket-Version: 13\r\n"))
|
||||||
|
buf.Write([]byte("\r\n"))
|
||||||
|
|
||||||
|
if _, err := c.Conn.Write(buf.Bytes()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
tpr := textproto.NewReader(bufio.NewReader(c.Conn))
|
tpr := textproto.NewReader(bufio.NewReader(c.Conn))
|
||||||
_, code, _, ok := parseFirstLine(tpr)
|
_, code, _, ok := parseFirstLine(tpr)
|
||||||
if !ok || code != "101" {
|
if !ok || code != "101" {
|
||||||
return errors.New("error in ws handshake")
|
return errors.New("[ws] error in ws handshake parseFirstLine")
|
||||||
}
|
}
|
||||||
|
|
||||||
// respHeader, err := tpr.ReadMIMEHeader()
|
respHeader, err := tpr.ReadMIMEHeader()
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return err
|
return err
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // TODO: verify this
|
serverKey := respHeader.Get("Sec-WebSocket-Accept")
|
||||||
// respHeader.Get("Sec-WebSocket-Accept")
|
if serverKey != computeServerKey(clientKey) {
|
||||||
// fmt.Printf("respHeader: %+v\n", respHeader)
|
return errors.New("[ws] error in ws handshake, got wrong Sec-Websocket-Key")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -92,7 +103,7 @@ func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) {
|
|||||||
line, err := tp.ReadLine()
|
line, err := tp.ReadLine()
|
||||||
// log.F("first line: %s", line)
|
// log.F("first line: %s", line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[http] read first line error:%s", err)
|
// log.F("[ws] read first line error:%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,3 +115,16 @@ func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) {
|
|||||||
s2 += s1 + 1
|
s2 += s1 + 1
|
||||||
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateClientKey() string {
|
||||||
|
p := make([]byte, 16)
|
||||||
|
rand.Read(p)
|
||||||
|
return base64.StdEncoding.EncodeToString(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeServerKey(clientKey string) string {
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write([]byte(clientKey))
|
||||||
|
h.Write(keyGUID)
|
||||||
|
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
@ -137,7 +137,7 @@ type frameReader struct {
|
|||||||
func FrameReader(r io.Reader) io.Reader {
|
func FrameReader(r io.Reader) io.Reader {
|
||||||
return &frameReader{
|
return &frameReader{
|
||||||
Reader: r,
|
Reader: r,
|
||||||
buf: make([]byte, defaultFrameSize),
|
buf: make([]byte, maxFrameHeaderSize), // NOTE: buf only used to save header bytes now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,36 +155,30 @@ func (r *frameReader) Read(b []byte) (int, error) {
|
|||||||
r.leftBytes = int64(r.buf[1] & 0x7f)
|
r.leftBytes = int64(r.buf[1] & 0x7f)
|
||||||
switch r.leftBytes {
|
switch r.leftBytes {
|
||||||
case 126:
|
case 126:
|
||||||
io.ReadFull(r.Reader, r.buf[:2])
|
_, err := io.ReadFull(r.Reader, r.buf[:2])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
r.leftBytes = int64(binary.BigEndian.Uint16(r.buf[0:]))
|
r.leftBytes = int64(binary.BigEndian.Uint16(r.buf[0:]))
|
||||||
case 127:
|
case 127:
|
||||||
io.ReadFull(r.Reader, r.buf[:8])
|
_, err := io.ReadFull(r.Reader, r.buf[:8])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
r.leftBytes = int64(binary.BigEndian.Uint64(r.buf[0:]))
|
r.leftBytes = int64(binary.BigEndian.Uint64(r.buf[0:]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var n, m int
|
readLen := int64(len(b))
|
||||||
var err error
|
if readLen > r.leftBytes {
|
||||||
if r.leftBytes > int64(len(r.buf)) {
|
readLen = r.leftBytes
|
||||||
if len(b) < len(r.buf) {
|
|
||||||
n, err = r.Reader.Read(r.buf[:len(b)])
|
|
||||||
} else {
|
|
||||||
n, err = r.Reader.Read(r.buf)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if int64(len(b)) < r.leftBytes {
|
|
||||||
n, err = io.ReadFull(r.Reader, r.buf[:len(b)])
|
|
||||||
} else {
|
|
||||||
n, err = io.ReadFull(r.Reader, r.buf[:r.leftBytes])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m, err := r.Reader.Read(b[:readLen])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m = copy(b, r.buf[:n])
|
|
||||||
r.leftBytes -= int64(m)
|
r.leftBytes -= int64(m)
|
||||||
|
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user