mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +08:00
269 lines
7.4 KiB
Go
269 lines
7.4 KiB
Go
// https://www.rfc-editor.org/rfc/rfc5246
|
|
// https://golang.org/src/crypto/tls/handshake_messages.go
|
|
|
|
// NOTE:
|
|
// 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"
|
|
"time"
|
|
|
|
"github.com/nadoo/glider/pkg/pool"
|
|
)
|
|
|
|
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 [lenSize]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}
|
|
return cc, nil
|
|
}
|
|
|
|
func (c *TLSObfsConn) Write(b []byte) (int, error) {
|
|
if !c.reqSent {
|
|
c.reqSent = true
|
|
return c.handshake(b)
|
|
}
|
|
|
|
buf := pool.GetBytesBuffer()
|
|
defer pool.PutBytesBuffer(buf)
|
|
|
|
n := len(b)
|
|
for i := 0; i < n; i += chunkSize {
|
|
buf.Reset()
|
|
end := i + chunkSize
|
|
if end > n {
|
|
end = n
|
|
}
|
|
|
|
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 := pool.GetBytesBuffer()
|
|
defer pool.PutBytesBuffer(buf)
|
|
|
|
bufExt := pool.GetBytesBuffer()
|
|
defer pool.PutBytesBuffer(bufExt)
|
|
|
|
bufHello := pool.GetBytesBuffer()
|
|
defer pool.PutBytesBuffer(bufHello)
|
|
|
|
// prepare extension & clientHello content
|
|
extension(b, c.obfsHost, bufExt)
|
|
clientHello(bufHello)
|
|
|
|
// 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(buf *bytes.Buffer) {
|
|
// Version: TLS 1.2 (0x0303)
|
|
buf.Write([]byte{0x03, 0x03})
|
|
|
|
// Random
|
|
// https://tools.ietf.org/id/draft-mathewson-no-gmtunixtime-00.txt
|
|
// NOTE:
|
|
// Most tls implementations do not deal with the first 4 bytes unix time,
|
|
// clients do not send current time, and server do not check it,
|
|
// golang tls client and chrome browser send random bytes instead.
|
|
//
|
|
binary.Write(buf, binary.BigEndian, uint32(time.Now().Unix()))
|
|
random := make([]byte, 28)
|
|
// The above 2 lines of codes was added to make it compatible with some server implementation,
|
|
// if we don't need the compatibility, just use the following code instead.
|
|
// 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
|
|
binary.Write(buf, binary.BigEndian, uint16(56))
|
|
// 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)
|
|
}
|
|
|
|
func extension(b []byte, server string, buf *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.WriteString(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
|
|
}
|