|
|
|
@ -5,6 +5,8 @@ package gost |
|
|
|
import ( |
|
|
|
"bufio" |
|
|
|
"bytes" |
|
|
|
"crypto/rand" |
|
|
|
"crypto/tls" |
|
|
|
"errors" |
|
|
|
"fmt" |
|
|
|
"io" |
|
|
|
@ -20,6 +22,7 @@ import ( |
|
|
|
pt "git.torproject.org/pluggable-transports/goptlib.git" |
|
|
|
"git.torproject.org/pluggable-transports/obfs4.git/transports/base" |
|
|
|
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4" |
|
|
|
dissector "github.com/ginuerzh/tls-dissector" |
|
|
|
) |
|
|
|
|
|
|
|
type obfsHTTPTransporter struct { |
|
|
|
@ -249,6 +252,315 @@ func (c *obfsHTTPConn) Write(b []byte) (n int, err error) { |
|
|
|
return c.Conn.Write(b) |
|
|
|
} |
|
|
|
|
|
|
|
type obfsTLSTransporter struct { |
|
|
|
tcpTransporter |
|
|
|
} |
|
|
|
|
|
|
|
// ObfsTLSTransporter creates a Transporter that is used by TLS obfuscating.
|
|
|
|
func ObfsTLSTransporter() Transporter { |
|
|
|
return &obfsTLSTransporter{} |
|
|
|
} |
|
|
|
|
|
|
|
func (tr *obfsTLSTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) { |
|
|
|
opts := &HandshakeOptions{} |
|
|
|
for _, option := range options { |
|
|
|
option(opts) |
|
|
|
} |
|
|
|
return ClientObfsTLSConn(conn, opts.Host), nil |
|
|
|
} |
|
|
|
|
|
|
|
type obfsTLSListener struct { |
|
|
|
net.Listener |
|
|
|
} |
|
|
|
|
|
|
|
// ObfsTLSListener creates a Listener for TLS obfuscating server.
|
|
|
|
func ObfsTLSListener(addr string) (Listener, error) { |
|
|
|
laddr, err := net.ResolveTCPAddr("tcp", addr) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
ln, err := net.ListenTCP("tcp", laddr) |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
return &obfsTLSListener{Listener: tcpKeepAliveListener{ln}}, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (l *obfsTLSListener) Accept() (net.Conn, error) { |
|
|
|
conn, err := l.Listener.Accept() |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
return ServerObfsTLSConn(conn, ""), nil |
|
|
|
} |
|
|
|
|
|
|
|
var ( |
|
|
|
cipherSuites = []uint16{ |
|
|
|
0xc02c, 0xc030, 0x009f, 0xcca9, 0xcca8, 0xccaa, 0xc02b, 0xc02f, |
|
|
|
0x009e, 0xc024, 0xc028, 0x006b, 0xc023, 0xc027, 0x0067, 0xc00a, |
|
|
|
0xc014, 0x0039, 0xc009, 0xc013, 0x0033, 0x009d, 0x009c, 0x003d, |
|
|
|
0x003c, 0x0035, 0x002f, 0x00ff, |
|
|
|
} |
|
|
|
|
|
|
|
compressionMethods = []uint8{0x00} |
|
|
|
|
|
|
|
algorithms = []uint16{ |
|
|
|
0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402, |
|
|
|
0x0403, 0x0301, 0x0302, 0x0303, 0x0201, 0x0202, 0x0203, |
|
|
|
} |
|
|
|
) |
|
|
|
|
|
|
|
type obfsTLSConn struct { |
|
|
|
net.Conn |
|
|
|
rbuf bytes.Buffer |
|
|
|
wbuf bytes.Buffer |
|
|
|
host string |
|
|
|
isServer bool |
|
|
|
handshaked chan struct{} |
|
|
|
handshakeMutex sync.Mutex |
|
|
|
} |
|
|
|
|
|
|
|
func ClientObfsTLSConn(conn net.Conn, host string) net.Conn { |
|
|
|
return &obfsTLSConn{ |
|
|
|
Conn: conn, |
|
|
|
host: host, |
|
|
|
handshaked: make(chan struct{}), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func ServerObfsTLSConn(conn net.Conn, host string) net.Conn { |
|
|
|
return &obfsTLSConn{ |
|
|
|
Conn: conn, |
|
|
|
host: host, |
|
|
|
isServer: true, |
|
|
|
handshaked: make(chan struct{}), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (c *obfsTLSConn) Handshaked() bool { |
|
|
|
select { |
|
|
|
case <-c.handshaked: |
|
|
|
return true |
|
|
|
default: |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (c *obfsTLSConn) Handshake(payload []byte) (err error) { |
|
|
|
c.handshakeMutex.Lock() |
|
|
|
defer c.handshakeMutex.Unlock() |
|
|
|
|
|
|
|
if c.Handshaked() { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if c.isServer { |
|
|
|
err = c.serverHandshake() |
|
|
|
} else { |
|
|
|
err = c.clientHandshake(payload) |
|
|
|
} |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
close(c.handshaked) |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (c *obfsTLSConn) clientHandshake(payload []byte) error { |
|
|
|
clientMsg := &dissector.ClientHelloMsg{ |
|
|
|
Version: tls.VersionTLS12, |
|
|
|
SessionID: make([]byte, 32), |
|
|
|
CipherSuites: cipherSuites, |
|
|
|
CompressionMethods: compressionMethods, |
|
|
|
Extensions: []dissector.Extension{ |
|
|
|
&dissector.SessionTicketExtension{ |
|
|
|
Data: payload, |
|
|
|
}, |
|
|
|
&dissector.ServerNameExtension{ |
|
|
|
Name: c.host, |
|
|
|
}, |
|
|
|
&dissector.ECPointFormatsExtension{ |
|
|
|
Formats: []uint8{0x01, 0x00, 0x02}, |
|
|
|
}, |
|
|
|
&dissector.SupportedGroupsExtension{ |
|
|
|
Groups: []uint16{0x001d, 0x0017, 0x0019, 0x0018}, |
|
|
|
}, |
|
|
|
&dissector.SignatureAlgorithmsExtension{ |
|
|
|
Algorithms: algorithms, |
|
|
|
}, |
|
|
|
&dissector.EncryptThenMacExtension{}, |
|
|
|
&dissector.ExtendedMasterSecretExtension{}, |
|
|
|
}, |
|
|
|
} |
|
|
|
clientMsg.Random.Time = uint32(time.Now().Unix()) |
|
|
|
rand.Read(clientMsg.Random.Opaque[:]) |
|
|
|
rand.Read(clientMsg.SessionID) |
|
|
|
b, err := clientMsg.Encode() |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
record := &dissector.Record{ |
|
|
|
Type: dissector.Handshake, |
|
|
|
Version: tls.VersionTLS10, |
|
|
|
Opaque: b, |
|
|
|
} |
|
|
|
if _, err := record.WriteTo(c.Conn); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
// server hello handshake message
|
|
|
|
if _, err := record.ReadFrom(c.Conn); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if record.Type != dissector.Handshake { |
|
|
|
return dissector.ErrBadType |
|
|
|
} |
|
|
|
|
|
|
|
// change cipher spec message
|
|
|
|
if _, err := record.ReadFrom(c.Conn); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if record.Type != dissector.ChangeCipherSpec { |
|
|
|
return dissector.ErrBadType |
|
|
|
} |
|
|
|
|
|
|
|
// encrypted handshake message
|
|
|
|
if _, err := record.ReadFrom(c.Conn); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
if record.Type != dissector.Handshake { |
|
|
|
return dissector.ErrBadType |
|
|
|
} |
|
|
|
|
|
|
|
_, err = c.rbuf.Write(record.Opaque) |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
func (c *obfsTLSConn) serverHandshake() error { |
|
|
|
record := &dissector.Record{} |
|
|
|
if _, err := record.ReadFrom(c.Conn); err != nil { |
|
|
|
log.Log(err) |
|
|
|
return err |
|
|
|
} |
|
|
|
if record.Type != dissector.Handshake { |
|
|
|
return dissector.ErrBadType |
|
|
|
} |
|
|
|
|
|
|
|
clientMsg := &dissector.ClientHelloMsg{} |
|
|
|
if err := clientMsg.Decode(record.Opaque); err != nil { |
|
|
|
log.Log(err) |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
for _, ext := range clientMsg.Extensions { |
|
|
|
if ext.Type() == dissector.ExtSessionTicket { |
|
|
|
b, err := ext.Encode() |
|
|
|
if err != nil { |
|
|
|
log.Log(err) |
|
|
|
return err |
|
|
|
} |
|
|
|
c.rbuf.Write(b) |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
serverMsg := &dissector.ServerHelloMsg{ |
|
|
|
Version: tls.VersionTLS12, |
|
|
|
SessionID: clientMsg.SessionID, |
|
|
|
CipherSuite: 0xcca8, |
|
|
|
CompressionMethod: 0x00, |
|
|
|
Extensions: []dissector.Extension{ |
|
|
|
&dissector.RenegotiationInfoExtension{}, |
|
|
|
&dissector.ExtendedMasterSecretExtension{}, |
|
|
|
&dissector.ECPointFormatsExtension{ |
|
|
|
Formats: []uint8{0x00}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
serverMsg.Random.Time = uint32(time.Now().Unix()) |
|
|
|
rand.Read(serverMsg.Random.Opaque[:]) |
|
|
|
b, err := serverMsg.Encode() |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
record = &dissector.Record{ |
|
|
|
Type: dissector.Handshake, |
|
|
|
Version: tls.VersionTLS10, |
|
|
|
Opaque: b, |
|
|
|
} |
|
|
|
|
|
|
|
if _, err := record.WriteTo(&c.wbuf); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
|
|
|
|
record = &dissector.Record{ |
|
|
|
Type: dissector.ChangeCipherSpec, |
|
|
|
Version: tls.VersionTLS12, |
|
|
|
Opaque: []byte{0x01}, |
|
|
|
} |
|
|
|
if _, err := record.WriteTo(&c.wbuf); err != nil { |
|
|
|
return err |
|
|
|
} |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (c *obfsTLSConn) Read(b []byte) (n int, err error) { |
|
|
|
if c.isServer { // NOTE: only Write performs the handshake operation on client side.
|
|
|
|
if err = c.Handshake(nil); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
select { |
|
|
|
case <-c.handshaked: |
|
|
|
} |
|
|
|
|
|
|
|
if c.rbuf.Len() > 0 { |
|
|
|
return c.rbuf.Read(b) |
|
|
|
} |
|
|
|
record := &dissector.Record{} |
|
|
|
if _, err = record.ReadFrom(c.Conn); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
n = copy(b, record.Opaque) |
|
|
|
_, err = c.rbuf.Write(record.Opaque[n:]) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
func (c *obfsTLSConn) Write(b []byte) (n int, err error) { |
|
|
|
n = len(b) |
|
|
|
if !c.Handshaked() { |
|
|
|
if err = c.Handshake(b); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
if !c.isServer { // the data b has been sended during handshake phase.
|
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
record := &dissector.Record{ |
|
|
|
Type: dissector.AppData, |
|
|
|
Version: tls.VersionTLS12, |
|
|
|
Opaque: b, |
|
|
|
} |
|
|
|
|
|
|
|
if c.wbuf.Len() > 0 { |
|
|
|
record.Type = dissector.Handshake |
|
|
|
record.WriteTo(&c.wbuf) |
|
|
|
_, err = c.wbuf.WriteTo(c.Conn) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if _, err = record.WriteTo(c.Conn); err != nil { |
|
|
|
return |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
type obfs4Context struct { |
|
|
|
cf base.ClientFactory |
|
|
|
cargs interface{} // type obfs4ClientArgs
|
|
|
|
|