mirror of
https://github.com/nadoo/glider.git
synced 2025-02-24 01:45:39 +08:00
obfs: added simple-obfs support. #79
This commit is contained in:
parent
da055c9078
commit
750862abdb
23
README.md
23
README.md
@ -28,6 +28,7 @@ Listen (local proxy server):
|
|||||||
- UDP over TCP tunnel
|
- UDP over TCP tunnel
|
||||||
- TLS, use it together with above proxy protocols(tcp)
|
- TLS, use it together with above proxy protocols(tcp)
|
||||||
- Unix domain socket, use it together with above proxy protocols(tcp)
|
- Unix domain socket, use it together with above proxy protocols(tcp)
|
||||||
|
- KCP protocol, use it together with above proxy protocols(tcp)
|
||||||
|
|
||||||
Forward (local proxy client/upstream proxy server):
|
Forward (local proxy client/upstream proxy server):
|
||||||
|
|
||||||
@ -39,6 +40,8 @@ Forward (local proxy client/upstream proxy server):
|
|||||||
- TLS, use it together with above proxy protocols(tcp)
|
- TLS, use it together with above proxy protocols(tcp)
|
||||||
- Websocket, use it together with above proxy protocols(tcp)
|
- Websocket, use it together with above proxy protocols(tcp)
|
||||||
- Unix domain socket, use it together with above proxy protocols(tcp)
|
- Unix domain socket, use it together with above proxy protocols(tcp)
|
||||||
|
- KCP protocol, use it together with above proxy protocols(tcp)
|
||||||
|
- Simple-Obfs, use it together with above proxy protocols(tcp)
|
||||||
|
|
||||||
DNS Forwarding Server (udp2tcp):
|
DNS Forwarding Server (udp2tcp):
|
||||||
|
|
||||||
@ -113,7 +116,7 @@ glider -config CONFIGPATH -listen :8080 -verbose
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
glider v0.6.9 usage:
|
glider v0.6.10 usage:
|
||||||
-checkduration int
|
-checkduration int
|
||||||
proxy check interval(seconds) (default 30)
|
proxy check interval(seconds) (default 30)
|
||||||
-checkwebsite string
|
-checkwebsite string
|
||||||
@ -166,10 +169,12 @@ Available Schemes:
|
|||||||
udptun: udp tunnel
|
udptun: udp tunnel
|
||||||
uottun: udp over tcp tunnel
|
uottun: udp over tcp tunnel
|
||||||
unix: unix domain socket
|
unix: unix domain socket
|
||||||
|
kcp: kcp protocol
|
||||||
|
simple-obfs: simple-obfs protocol
|
||||||
|
|
||||||
Available schemes for different modes:
|
Available schemes for different modes:
|
||||||
listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix
|
listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix kcp
|
||||||
forward: ss socks5 http ssr vmess tls ws unix
|
forward: ss socks5 http ssr vmess tls ws unix kcp simple-bfs
|
||||||
|
|
||||||
SS scheme:
|
SS scheme:
|
||||||
ss://method:pass@host:port
|
ss://method:pass@host:port
|
||||||
@ -227,6 +232,18 @@ TLS and Websocket with a specified proxy protocol:
|
|||||||
Unix domain socket scheme:
|
Unix domain socket scheme:
|
||||||
unix://path
|
unix://path
|
||||||
|
|
||||||
|
KCP scheme:
|
||||||
|
kcp://CRYPT:KEY@host:port[?dataShards=NUM&parityShards=NUM]
|
||||||
|
|
||||||
|
Available crypt types for KCP:
|
||||||
|
none, sm4, tea, xor, aes, aes-128, aes-192, blowfish, twofish, cast5, 3des, xtea, salsa20
|
||||||
|
|
||||||
|
Simple-Obfs scheme:
|
||||||
|
simple-obfs://host:port[?type=TYPE&host=HOST&uri=URI&ua=UA]
|
||||||
|
|
||||||
|
Available types for simple-obfs:
|
||||||
|
http, tls
|
||||||
|
|
||||||
DNS forwarding server:
|
DNS forwarding server:
|
||||||
dns=:53
|
dns=:53
|
||||||
dnsserver=8.8.8.8:53
|
dnsserver=8.8.8.8:53
|
||||||
|
22
conf.go
22
conf.go
@ -122,11 +122,13 @@ func usage() {
|
|||||||
fmt.Fprintf(os.Stderr, " udptun: udp tunnel\n")
|
fmt.Fprintf(os.Stderr, " udptun: udp tunnel\n")
|
||||||
fmt.Fprintf(os.Stderr, " uottun: udp over tcp tunnel\n")
|
fmt.Fprintf(os.Stderr, " uottun: udp over tcp tunnel\n")
|
||||||
fmt.Fprintf(os.Stderr, " unix: unix domain socket\n")
|
fmt.Fprintf(os.Stderr, " unix: unix domain socket\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " kcp: kcp protocol\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " simple-obfs: simple-obfs protocol\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "Available schemes for different modes:\n")
|
fmt.Fprintf(os.Stderr, "Available schemes for different modes:\n")
|
||||||
fmt.Fprintf(os.Stderr, " listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix\n")
|
fmt.Fprintf(os.Stderr, " listen: mixed ss socks5 http redir redir6 tcptun udptun uottun tls unix kcp\n")
|
||||||
fmt.Fprintf(os.Stderr, " forward: ss socks5 http ssr vmess tls ws unix\n")
|
fmt.Fprintf(os.Stderr, " forward: ss socks5 http ssr vmess tls ws unix kcp simple-bfs\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "SS scheme:\n")
|
fmt.Fprintf(os.Stderr, "SS scheme:\n")
|
||||||
@ -198,6 +200,22 @@ func usage() {
|
|||||||
fmt.Fprintf(os.Stderr, " unix://path\n")
|
fmt.Fprintf(os.Stderr, " unix://path\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "KCP scheme:\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " kcp://CRYPT:KEY@host:port[?dataShards=NUM&parityShards=NUM]\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "Available crypt types for KCP:\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " none, sm4, tea, xor, aes, aes-128, aes-192, blowfish, twofish, cast5, 3des, xtea, salsa20\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "Simple-Obfs scheme:\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " simple-obfs://host:port[?type=TYPE&host=HOST&uri=URI&ua=UA]\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "Available types for simple-obfs:\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " http, tls\n")
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "DNS forwarding server:\n")
|
fmt.Fprintf(os.Stderr, "DNS forwarding server:\n")
|
||||||
fmt.Fprintf(os.Stderr, " dns=:53\n")
|
fmt.Fprintf(os.Stderr, " dns=:53\n")
|
||||||
fmt.Fprintf(os.Stderr, " dnsserver=8.8.8.8:53\n")
|
fmt.Fprintf(os.Stderr, " dnsserver=8.8.8.8:53\n")
|
||||||
|
@ -64,6 +64,9 @@ listen=socks5://:1080
|
|||||||
# socks5 over unix domain socket
|
# socks5 over unix domain socket
|
||||||
# listen=unix:///tmp/glider.socket,socks5://
|
# listen=unix:///tmp/glider.socket,socks5://
|
||||||
|
|
||||||
|
# socks5 over kcp
|
||||||
|
# listen=kcp://aes:key@127.0.0.1:8444?dataShards=10&parityShards=3,socks5://
|
||||||
|
|
||||||
# FORWARDERS
|
# FORWARDERS
|
||||||
# ----------
|
# ----------
|
||||||
# Forwarders, we can setup multiple forwarders.
|
# Forwarders, we can setup multiple forwarders.
|
||||||
@ -113,6 +116,12 @@ listen=socks5://:1080
|
|||||||
# ss over tls
|
# ss over tls
|
||||||
# forward=tls://1.1.1.1:443,ss://AEAD_CHACHA20_POLY1305:pass@
|
# forward=tls://1.1.1.1:443,ss://AEAD_CHACHA20_POLY1305:pass@
|
||||||
|
|
||||||
|
# ss over kcp
|
||||||
|
# forward=kcp://aes:key@127.0.0.1:8444?dataShards=10&parityShards=3,ss://AEAD_CHACHA20_POLY1305:pass@
|
||||||
|
|
||||||
|
# ss with simple-obfs
|
||||||
|
# forward=simple-obfs://1.1.1.1:443?type=tls&host=apple.com,ss://AEAD_CHACHA20_POLY1305:pass@
|
||||||
|
|
||||||
# socks5 over unix domain socket
|
# socks5 over unix domain socket
|
||||||
# forward=unix:///tmp/glider.socket,socks5://
|
# forward=unix:///tmp/glider.socket,socks5://
|
||||||
|
|
||||||
|
1
main.go
1
main.go
@ -16,6 +16,7 @@ import (
|
|||||||
_ "github.com/nadoo/glider/proxy/http"
|
_ "github.com/nadoo/glider/proxy/http"
|
||||||
_ "github.com/nadoo/glider/proxy/kcp"
|
_ "github.com/nadoo/glider/proxy/kcp"
|
||||||
_ "github.com/nadoo/glider/proxy/mixed"
|
_ "github.com/nadoo/glider/proxy/mixed"
|
||||||
|
_ "github.com/nadoo/glider/proxy/obfs"
|
||||||
_ "github.com/nadoo/glider/proxy/socks5"
|
_ "github.com/nadoo/glider/proxy/socks5"
|
||||||
_ "github.com/nadoo/glider/proxy/ss"
|
_ "github.com/nadoo/glider/proxy/ss"
|
||||||
_ "github.com/nadoo/glider/proxy/ssr"
|
_ "github.com/nadoo/glider/proxy/ssr"
|
||||||
|
@ -89,7 +89,7 @@ func (f *Forwarder) Dial(network, addr string) (c net.Conn, err error) {
|
|||||||
f.IncFailures()
|
f.IncFailures()
|
||||||
if f.Failures() >= f.MaxFailures() {
|
if f.Failures() >= f.MaxFailures() {
|
||||||
f.Disable()
|
f.Disable()
|
||||||
log.F("[forwarder] %s reaches maxfailures, set to DISABLED", f.addr)
|
log.F("[forwarder] %s reaches maxfailures.", f.addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,6 +258,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
|||||||
|
|
||||||
_, code, _, ok := parseFirstLine(respTP)
|
_, code, _, ok := parseFirstLine(respTP)
|
||||||
if ok && code == "200" {
|
if ok && code == "200" {
|
||||||
|
// TODO: check here
|
||||||
respTP.ReadMIMEHeader()
|
respTP.ReadMIMEHeader()
|
||||||
return rc, err
|
return rc, err
|
||||||
} else if code == "407" {
|
} else if code == "407" {
|
||||||
|
@ -69,7 +69,7 @@ func NewKCP(s string, dialer proxy.Dialer) (*KCP, error) {
|
|||||||
|
|
||||||
parityShards, err := strconv.ParseUint(pShards, 10, 32)
|
parityShards, err := strconv.ParseUint(pShards, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[kcp] parse dataShards err: %s", err)
|
log.F("[kcp] parse parityShards err: %s", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
proxy/obfs/http.go
Normal file
78
proxy/obfs/http.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package obfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPObfs struct
|
||||||
|
type HTTPObfs struct {
|
||||||
|
obfsHost string
|
||||||
|
obfsURI string
|
||||||
|
obfsUA string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPObfs returns a HTTPObfs object
|
||||||
|
func NewHTTPObfs(obfsHost, obfsURI, obfsUA string) *HTTPObfs {
|
||||||
|
return &HTTPObfs{obfsHost, obfsURI, obfsUA}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPObfsConn struct
|
||||||
|
type HTTPObfsConn struct {
|
||||||
|
*HTTPObfs
|
||||||
|
|
||||||
|
net.Conn
|
||||||
|
reader io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn returns a new obfs connection
|
||||||
|
func (p *HTTPObfs) NewConn(c net.Conn) (net.Conn, error) {
|
||||||
|
cc := &HTTPObfsConn{
|
||||||
|
Conn: c,
|
||||||
|
HTTPObfs: p,
|
||||||
|
}
|
||||||
|
|
||||||
|
// send http header to remote server
|
||||||
|
_, err := cc.writeHeader()
|
||||||
|
return cc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HTTPObfsConn) writeHeader() (int, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.Write([]byte("GET " + c.obfsURI + " HTTP/1.1\r\n"))
|
||||||
|
buf.Write([]byte("Host: " + c.obfsHost + "\r\n"))
|
||||||
|
buf.Write([]byte("User-Agent: " + c.obfsUA + "\r\n"))
|
||||||
|
buf.Write([]byte("Upgrade: websocket\r\n"))
|
||||||
|
buf.Write([]byte("Connection: Upgrade\r\n"))
|
||||||
|
|
||||||
|
p := make([]byte, 16)
|
||||||
|
rand.Read(p)
|
||||||
|
buf.Write([]byte("Sec-WebSocket-Key: " + base64.StdEncoding.EncodeToString(p) + "\r\n"))
|
||||||
|
|
||||||
|
buf.Write([]byte("\r\n"))
|
||||||
|
|
||||||
|
return c.Conn.Write(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HTTPObfsConn) Read(b []byte) (n int, err error) {
|
||||||
|
if c.reader == nil {
|
||||||
|
r := bufio.NewReader(c.Conn)
|
||||||
|
c.reader = r
|
||||||
|
for {
|
||||||
|
l, _, err := r.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(l) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.reader.Read(b)
|
||||||
|
}
|
109
proxy/obfs/obfs.go
Normal file
109
proxy/obfs/obfs.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Package obfs implements simple-obfs of ss
|
||||||
|
package obfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/nadoo/glider/common/log"
|
||||||
|
"github.com/nadoo/glider/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Obfs struct
|
||||||
|
type Obfs struct {
|
||||||
|
dialer proxy.Dialer
|
||||||
|
addr string
|
||||||
|
|
||||||
|
obfsType string
|
||||||
|
obfsHost string
|
||||||
|
obfsURI string
|
||||||
|
obfsUA string
|
||||||
|
|
||||||
|
obfsConn func(c net.Conn) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proxy.RegisterDialer("simple-obfs", NewObfsDialer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewObfs returns a proxy struct
|
||||||
|
func NewObfs(s string, dialer proxy.Dialer) (*Obfs, error) {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
log.F("parse url err: %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := u.Host
|
||||||
|
|
||||||
|
query := u.Query()
|
||||||
|
obfsType := query.Get("type")
|
||||||
|
if obfsType == "" {
|
||||||
|
obfsType = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
obfsHost := query.Get("host")
|
||||||
|
if obfsHost == "" {
|
||||||
|
return nil, errors.New("[obfs] host cannot be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
obfsURI := query.Get("uri")
|
||||||
|
if obfsURI == "" {
|
||||||
|
obfsURI = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
obfsUA := query.Get("ua")
|
||||||
|
if obfsUA == "" {
|
||||||
|
obfsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36"
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &Obfs{
|
||||||
|
dialer: dialer,
|
||||||
|
addr: addr,
|
||||||
|
obfsType: obfsType,
|
||||||
|
obfsHost: obfsHost,
|
||||||
|
obfsURI: obfsURI,
|
||||||
|
obfsUA: obfsUA,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch obfsType {
|
||||||
|
case "http":
|
||||||
|
httpObfs := NewHTTPObfs(obfsHost, obfsURI, obfsUA)
|
||||||
|
p.obfsConn = httpObfs.NewConn
|
||||||
|
case "tls":
|
||||||
|
tlsObfs := NewTLSObfs(obfsHost)
|
||||||
|
p.obfsConn = tlsObfs.NewConn
|
||||||
|
default:
|
||||||
|
return nil, errors.New("[obfs] unknown obfs type: " + obfsType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewObfsDialer returns a proxy dialer
|
||||||
|
func NewObfsDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||||
|
return NewObfs(s, dialer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns forwarder's address
|
||||||
|
func (s *Obfs) Addr() string { return s.addr }
|
||||||
|
|
||||||
|
// NextDialer returns the next dialer
|
||||||
|
func (s *Obfs) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||||
|
|
||||||
|
// Dial connects to the address addr on the network net via the proxy
|
||||||
|
func (s *Obfs) Dial(network, addr string) (net.Conn, error) {
|
||||||
|
c, err := s.dialer.Dial("tcp", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
log.F("[obfs] dial to %s error: %s", s.addr, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.obfsConn(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialUDP connects to the given address via the proxy
|
||||||
|
func (s *Obfs) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||||
|
return nil, nil, errors.New("obfs client does not support udp now")
|
||||||
|
}
|
257
proxy/obfs/tls.go
Normal file
257
proxy/obfs/tls.go
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
// https://www.ietf.org/rfc/rfc5246.txt
|
||||||
|
// https://golang.org/src/crypto/tls/handshake_messages.go
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// https://github.com/shadowsocks/simple-obfs/blob/master/src/obfs_tls.c
|
||||||
|
// The official obfs-server only checks 6 static bytes of client hello packet,
|
||||||
|
// so if we send a malformed packet, e.g: set a wrong length number of extensions,
|
||||||
|
// obfs-server will treat it as a correct packet, but in wireshak, it's malformed.
|
||||||
|
|
||||||
|
package obfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lenSize = 2
|
||||||
|
chunkSize = 1 << 13 // 8192
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSObfs struct
|
||||||
|
type TLSObfs struct {
|
||||||
|
obfsHost string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSObfs returns a TLSObfs object
|
||||||
|
func NewTLSObfs(obfsHost string) *TLSObfs {
|
||||||
|
return &TLSObfs{obfsHost: obfsHost}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSObfsConn struct
|
||||||
|
type TLSObfsConn struct {
|
||||||
|
*TLSObfs
|
||||||
|
|
||||||
|
net.Conn
|
||||||
|
reqSent bool
|
||||||
|
reader *bufio.Reader
|
||||||
|
buf []byte
|
||||||
|
leftBytes int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn returns a new obfs connection
|
||||||
|
func (p *TLSObfs) NewConn(c net.Conn) (net.Conn, error) {
|
||||||
|
cc := &TLSObfsConn{
|
||||||
|
Conn: c,
|
||||||
|
TLSObfs: p,
|
||||||
|
buf: make([]byte, lenSize),
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSObfsConn) Write(b []byte) (int, error) {
|
||||||
|
if !c.reqSent {
|
||||||
|
c.reqSent = true
|
||||||
|
return c.handshake(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(b)
|
||||||
|
for i := 0; i < n; i += chunkSize {
|
||||||
|
end := i + chunkSize
|
||||||
|
if end > n {
|
||||||
|
end = n
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.Write([]byte{0x17, 0x03, 0x03})
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(len(b[i:end])))
|
||||||
|
buf.Write(b[i:end])
|
||||||
|
|
||||||
|
_, err := c.Conn.Write(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSObfsConn) Read(b []byte) (int, error) {
|
||||||
|
if c.reader == nil {
|
||||||
|
c.reader = bufio.NewReader(c.Conn)
|
||||||
|
// Server Hello
|
||||||
|
// TLSv1.2 Record Layer: Handshake Protocol: Server Hello (96 bytes)
|
||||||
|
// TLSv1.2 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec (6 bytes)
|
||||||
|
c.reader.Discard(102)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.leftBytes == 0 {
|
||||||
|
// TLSv1.2 Record Layer:
|
||||||
|
// 1st packet: handshake encrypted message / following packets: application data
|
||||||
|
// 1 byte: Content Type: Handshake (22) / Application Data (23)
|
||||||
|
// 2 bytes: Version: TLS 1.2 (0x0303)
|
||||||
|
c.reader.Discard(3)
|
||||||
|
|
||||||
|
// get length
|
||||||
|
_, err := io.ReadFull(c.reader, c.buf[:lenSize])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.leftBytes = int(binary.BigEndian.Uint16(c.buf[:lenSize]))
|
||||||
|
}
|
||||||
|
|
||||||
|
readLen := len(b)
|
||||||
|
if readLen > c.leftBytes {
|
||||||
|
readLen = c.leftBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := c.reader.Read(b[:readLen])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.leftBytes -= m
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSObfsConn) handshake(b []byte) (int, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
// prepare extension & clientHello content
|
||||||
|
bufExt, bufHello := extention(b, c.obfsHost), clientHello()
|
||||||
|
|
||||||
|
// prepare lengths
|
||||||
|
extLen := bufExt.Len()
|
||||||
|
helloLen := bufHello.Len() + 2 + extLen // 2: len(extContentLength)
|
||||||
|
handshakeLen := 4 + helloLen // 1: len(0x01) + 3: len(clientHelloContentLength)
|
||||||
|
|
||||||
|
// TLS Record Layer Begin
|
||||||
|
// Content Type: Handshake (22)
|
||||||
|
buf.WriteByte(0x16)
|
||||||
|
|
||||||
|
// Version: TLS 1.0 (0x0301)
|
||||||
|
buf.Write([]byte{0x03, 0x01})
|
||||||
|
|
||||||
|
// length
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(handshakeLen))
|
||||||
|
|
||||||
|
// Handshake Begin
|
||||||
|
// Handshake Type: Client Hello (1)
|
||||||
|
buf.WriteByte(0x01)
|
||||||
|
|
||||||
|
// length: uint24(3 bytes), but golang doesn't have this type
|
||||||
|
buf.Write([]byte{uint8(helloLen >> 16), uint8(helloLen >> 8), uint8(helloLen)})
|
||||||
|
|
||||||
|
// clientHello content
|
||||||
|
buf.Write(bufHello.Bytes())
|
||||||
|
|
||||||
|
// Extension Begin
|
||||||
|
// ext content length
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(extLen))
|
||||||
|
|
||||||
|
// ext content
|
||||||
|
buf.Write(bufExt.Bytes())
|
||||||
|
|
||||||
|
_, err := c.Conn.Write(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientHello() *bytes.Buffer {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
// Version: TLS 1.2 (0x0303)
|
||||||
|
buf.Write([]byte{0x03, 0x03})
|
||||||
|
|
||||||
|
// Random
|
||||||
|
// https://tools.ietf.org/id/draft-mathewson-no-gmtunixtime-00.txt
|
||||||
|
random := make([]byte, 32)
|
||||||
|
rand.Read(random)
|
||||||
|
buf.Write(random)
|
||||||
|
|
||||||
|
// Session ID Length: 32
|
||||||
|
buf.WriteByte(32)
|
||||||
|
// Session ID
|
||||||
|
sessionID := make([]byte, 32)
|
||||||
|
rand.Read(sessionID)
|
||||||
|
buf.Write(sessionID)
|
||||||
|
|
||||||
|
// https://github.com/shadowsocks/simple-obfs/blob/7659eeccf473aa41eb294e92c32f8f60a8747325/src/obfs_tls.c#L57
|
||||||
|
// Cipher Suites Length: 56
|
||||||
|
buf.Write([]byte{0x00, 0x38})
|
||||||
|
// Cipher Suites (28 suites)
|
||||||
|
buf.Write([]byte{
|
||||||
|
0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f,
|
||||||
|
0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a,
|
||||||
|
0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x00, 0x3d,
|
||||||
|
0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Compression Methods Length: 1
|
||||||
|
buf.WriteByte(0x01)
|
||||||
|
// Compression Methods (1 method)
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func extention(b []byte, server string) *bytes.Buffer {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
// Extension: SessionTicket TLS
|
||||||
|
buf.Write([]byte{0x00, 0x23}) // type
|
||||||
|
// NOTE: send some data in sessionticket, the server will treat it as data too
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(len(b))) // length
|
||||||
|
buf.Write(b)
|
||||||
|
|
||||||
|
// Extension: server_name
|
||||||
|
buf.Write([]byte{0x00, 0x00}) // type
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(len(server)+5)) // length
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(len(server)+3)) // Server Name list length
|
||||||
|
buf.WriteByte(0x00) // Server Name Type: host_name (0)
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(len(server))) // Server Name length
|
||||||
|
buf.Write([]byte(server))
|
||||||
|
|
||||||
|
// https://github.com/shadowsocks/simple-obfs/blob/7659eeccf473aa41eb294e92c32f8f60a8747325/src/obfs_tls.c#L88
|
||||||
|
// Extension: ec_point_formats (len=4)
|
||||||
|
buf.Write([]byte{0x00, 0x0b}) // type
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(4)) // length
|
||||||
|
buf.WriteByte(0x03) // format length
|
||||||
|
buf.Write([]byte{0x01, 0x00, 0x02})
|
||||||
|
|
||||||
|
// Extension: supported_groups (len=10)
|
||||||
|
buf.Write([]byte{0x00, 0x0a}) // type
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(10)) // length
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(8)) // Supported Groups List Length: 8
|
||||||
|
buf.Write([]byte{0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18})
|
||||||
|
|
||||||
|
// Extension: signature_algorithms (len=32)
|
||||||
|
buf.Write([]byte{0x00, 0x0d}) // type
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(32)) // length
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(30)) // Signature Hash Algorithms Length: 30
|
||||||
|
buf.Write([]byte{
|
||||||
|
0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02,
|
||||||
|
0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Extension: encrypt_then_mac (len=0)
|
||||||
|
buf.Write([]byte{0x00, 0x16}) // type
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(0)) // length
|
||||||
|
|
||||||
|
// Extension: extended_master_secret (len=0)
|
||||||
|
buf.Write([]byte{0x00, 0x17}) // type
|
||||||
|
binary.Write(buf, binary.BigEndian, uint16(0)) // length
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
@ -81,7 +81,6 @@ func NewTLSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
|||||||
InsecureSkipVerify: p.skipVerify,
|
InsecureSkipVerify: p.skipVerify,
|
||||||
ClientSessionCache: stdtls.NewLRUClientSessionCache(64),
|
ClientSessionCache: stdtls.NewLRUClientSessionCache(64),
|
||||||
MinVersion: stdtls.VersionTLS10,
|
MinVersion: stdtls.VersionTLS10,
|
||||||
MaxVersion: stdtls.VersionTLS12,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, err
|
return p, err
|
||||||
@ -111,7 +110,6 @@ func NewTLSServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
|||||||
p.tlsConfig = &stdtls.Config{
|
p.tlsConfig = &stdtls.Config{
|
||||||
Certificates: []stdtls.Certificate{cert},
|
Certificates: []stdtls.Certificate{cert},
|
||||||
MinVersion: stdtls.VersionTLS10,
|
MinVersion: stdtls.VersionTLS10,
|
||||||
MaxVersion: stdtls.VersionTLS12,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.server, err = proxy.ServerFromURL(transport[1], dialer)
|
p.server, err = proxy.ServerFromURL(transport[1], dialer)
|
||||||
|
Loading…
Reference in New Issue
Block a user