129 lines
2.6 KiB
Go
129 lines
2.6 KiB
Go
package sni
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"hash/crc32"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
|
|
dissector "github.com/go-gost/tls-dissector"
|
|
)
|
|
|
|
type sniClientConn struct {
|
|
host string
|
|
obfuscated bool
|
|
net.Conn
|
|
}
|
|
|
|
func (c *sniClientConn) Write(p []byte) (int, error) {
|
|
b, err := c.obfuscate(p)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if _, err = c.Conn.Write(b); err != nil {
|
|
return 0, err
|
|
}
|
|
return len(p), nil
|
|
}
|
|
|
|
func (c *sniClientConn) obfuscate(p []byte) ([]byte, error) {
|
|
if c.host == "" {
|
|
return p, nil
|
|
}
|
|
|
|
if c.obfuscated {
|
|
return p, nil
|
|
}
|
|
|
|
if p[0] == dissector.Handshake {
|
|
b, err := readClientHelloRecord(bytes.NewReader(p), c.host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c.obfuscated = true
|
|
return b, nil
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
br := bufio.NewReader(bytes.NewReader(p))
|
|
for {
|
|
s, err := br.ReadString('\n')
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
return nil, err
|
|
}
|
|
if s != "" {
|
|
buf.Write([]byte(s))
|
|
}
|
|
break
|
|
}
|
|
|
|
// end of HTTP header
|
|
if s == "\r\n" {
|
|
buf.Write([]byte(s))
|
|
// drain the remain bytes.
|
|
io.Copy(buf, br)
|
|
break
|
|
}
|
|
|
|
if strings.HasPrefix(s, "Host") {
|
|
s = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(s, "Host:"), "\r\n"))
|
|
host := encodeServerName(s)
|
|
buf.WriteString("Host: " + c.host + "\r\n")
|
|
buf.WriteString("Gost-Target: " + host + "\r\n")
|
|
// drain the remain bytes.
|
|
io.Copy(buf, br)
|
|
break
|
|
}
|
|
buf.Write([]byte(s))
|
|
}
|
|
c.obfuscated = true
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func readClientHelloRecord(r io.Reader, host string) ([]byte, error) {
|
|
record, err := dissector.ReadRecord(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clientHello := dissector.ClientHelloMsg{}
|
|
if err := clientHello.Decode(record.Opaque); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, ext := range clientHello.Extensions {
|
|
if ext.Type() == dissector.ExtServerName {
|
|
snExtension := ext.(*dissector.ServerNameExtension)
|
|
if host != "" {
|
|
e, _ := dissector.NewExtension(0xFFFE, []byte(encodeServerName(snExtension.Name)))
|
|
clientHello.Extensions = append(clientHello.Extensions, e)
|
|
snExtension.Name = host
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
record.Opaque, err = clientHello.Encode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
if _, err := record.WriteTo(buf); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func encodeServerName(name string) string {
|
|
buf := &bytes.Buffer{}
|
|
binary.Write(buf, binary.BigEndian, crc32.ChecksumIEEE([]byte(name)))
|
|
buf.WriteString(base64.RawURLEncoding.EncodeToString([]byte(name)))
|
|
return base64.RawURLEncoding.EncodeToString(buf.Bytes())
|
|
}
|