123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package shadowtls
- import (
- "context"
- "crypto/hmac"
- "crypto/sha1"
- "crypto/tls"
- "encoding/binary"
- "fmt"
- "hash"
- "io"
- "net"
- "github.com/metacubex/mihomo/common/pool"
- C "github.com/metacubex/mihomo/constant"
- )
- const (
- chunkSize = 1 << 13
- Mode string = "shadow-tls"
- hashLen int = 8
- tlsHeaderLen int = 5
- )
- var (
- DefaultALPN = []string{"h2", "http/1.1"}
- )
- // ShadowTLS is shadow-tls implementation
- type ShadowTLS struct {
- net.Conn
- password []byte
- remain int
- firstRequest bool
- tlsConfig *tls.Config
- }
- type HashedConn struct {
- net.Conn
- hasher hash.Hash
- }
- func newHashedStream(conn net.Conn, password []byte) HashedConn {
- return HashedConn{
- Conn: conn,
- hasher: hmac.New(sha1.New, password),
- }
- }
- func (h HashedConn) Read(b []byte) (n int, err error) {
- n, err = h.Conn.Read(b)
- h.hasher.Write(b[:n])
- return
- }
- func (s *ShadowTLS) read(b []byte) (int, error) {
- var buf [tlsHeaderLen]byte
- _, err := io.ReadFull(s.Conn, buf[:])
- if err != nil {
- return 0, fmt.Errorf("shadowtls read failed %w", err)
- }
- if buf[0] != 0x17 || buf[1] != 0x3 || buf[2] != 0x3 {
- return 0, fmt.Errorf("invalid shadowtls header %v", buf)
- }
- length := int(binary.BigEndian.Uint16(buf[3:]))
- if length > len(b) {
- n, err := s.Conn.Read(b)
- if err != nil {
- return n, err
- }
- s.remain = length - n
- return n, nil
- }
- return io.ReadFull(s.Conn, b[:length])
- }
- func (s *ShadowTLS) Read(b []byte) (int, error) {
- if s.remain > 0 {
- length := s.remain
- if length > len(b) {
- length = len(b)
- }
- n, err := io.ReadFull(s.Conn, b[:length])
- if err != nil {
- return n, fmt.Errorf("shadowtls Read failed with %w", err)
- }
- s.remain -= n
- return n, nil
- }
- return s.read(b)
- }
- func (s *ShadowTLS) Write(b []byte) (int, error) {
- length := len(b)
- for i := 0; i < length; i += chunkSize {
- end := i + chunkSize
- if end > length {
- end = length
- }
- n, err := s.write(b[i:end])
- if err != nil {
- return n, fmt.Errorf("shadowtls Write failed with %w, i=%d, end=%d, n=%d", err, i, end, n)
- }
- }
- return length, nil
- }
- func (s *ShadowTLS) write(b []byte) (int, error) {
- var hashVal []byte
- if s.firstRequest {
- hashedConn := newHashedStream(s.Conn, s.password)
- tlsConn := tls.Client(hashedConn, s.tlsConfig)
- // fix tls handshake not timeout
- ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
- defer cancel()
- if err := tlsConn.HandshakeContext(ctx); err != nil {
- return 0, fmt.Errorf("tls connect failed with %w", err)
- }
- hashVal = hashedConn.hasher.Sum(nil)[:hashLen]
- s.firstRequest = false
- }
- buf := pool.GetBuffer()
- defer pool.PutBuffer(buf)
- buf.Write([]byte{0x17, 0x03, 0x03})
- binary.Write(buf, binary.BigEndian, uint16(len(b)+len(hashVal)))
- buf.Write(hashVal)
- buf.Write(b)
- _, err := s.Conn.Write(buf.Bytes())
- if err != nil {
- // return 0 because errors occur here make the
- // whole situation irrecoverable
- return 0, err
- }
- return len(b), nil
- }
- // NewShadowTLS return a ShadowTLS
- func NewShadowTLS(conn net.Conn, password string, tlsConfig *tls.Config) net.Conn {
- return &ShadowTLS{
- Conn: conn,
- password: []byte(password),
- firstRequest: true,
- tlsConfig: tlsConfig,
- }
- }
|