github.com/igoogolx/clash@v1.19.8/transport/vmess/conn.go (about)

     1  package vmess
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/hmac"
     7  	"crypto/md5"
     8  	"crypto/rand"
     9  	"crypto/sha256"
    10  	"encoding/binary"
    11  	"errors"
    12  	"hash/fnv"
    13  	"io"
    14  	mathRand "math/rand"
    15  	"net"
    16  	"time"
    17  
    18  	"github.com/Dreamacro/protobytes"
    19  	"golang.org/x/crypto/chacha20poly1305"
    20  )
    21  
    22  // Conn wrapper a net.Conn with vmess protocol
    23  type Conn struct {
    24  	net.Conn
    25  	reader      io.Reader
    26  	writer      io.Writer
    27  	dst         *DstAddr
    28  	id          *ID
    29  	reqBodyIV   []byte
    30  	reqBodyKey  []byte
    31  	respBodyIV  []byte
    32  	respBodyKey []byte
    33  	respV       byte
    34  	security    byte
    35  	option      byte
    36  	isAead      bool
    37  	isVless     bool
    38  
    39  	received bool
    40  }
    41  
    42  func (vc *Conn) Write(b []byte) (int, error) {
    43  	return vc.writer.Write(b)
    44  }
    45  
    46  func (vc *Conn) Read(b []byte) (int, error) {
    47  	if vc.received {
    48  		return vc.reader.Read(b)
    49  	}
    50  
    51  	if err := vc.recvResponse(); err != nil {
    52  		return 0, err
    53  	}
    54  	vc.received = true
    55  	return vc.reader.Read(b)
    56  }
    57  
    58  func (vc *Conn) sendRequest() error {
    59  	if vc.isVless {
    60  		buf := protobytes.BytesWriter{}
    61  		buf.PutUint8(0)                  // Protocol Version
    62  		buf.PutSlice(vc.id.UUID.Bytes()) // UUID
    63  		buf.PutUint8(0)                  // Addons Length
    64  		// buf.PutString("")             // Addons Data
    65  
    66  		// Command
    67  		if vc.dst.UDP {
    68  			buf.PutUint8(CommandUDP)
    69  		} else {
    70  			buf.PutUint8(CommandTCP)
    71  		}
    72  
    73  		// Port AddrType Addr
    74  		buf.PutUint16be(uint16(vc.dst.Port))
    75  		buf.PutUint8(vc.dst.AddrType)
    76  		buf.PutSlice(vc.dst.Addr)
    77  
    78  		_, err := vc.Conn.Write(buf.Bytes())
    79  		return err
    80  	}
    81  
    82  	timestamp := time.Now()
    83  
    84  	mbuf := protobytes.BytesWriter{}
    85  
    86  	if !vc.isAead {
    87  		h := hmac.New(md5.New, vc.id.UUID.Bytes())
    88  		binary.Write(h, binary.BigEndian, uint64(timestamp.Unix()))
    89  		mbuf.PutSlice(h.Sum(nil))
    90  	}
    91  
    92  	buf := protobytes.BytesWriter{}
    93  
    94  	// Ver IV Key V Opt
    95  	buf.PutUint8(Version)
    96  	buf.PutSlice(vc.reqBodyIV[:])
    97  	buf.PutSlice(vc.reqBodyKey[:])
    98  	buf.PutUint8(vc.respV)
    99  	buf.PutUint8(vc.option)
   100  
   101  	p := mathRand.Intn(16)
   102  	// P Sec Reserve Cmd
   103  	buf.PutUint8(byte(p<<4) | vc.security)
   104  	buf.PutUint8(0)
   105  	if vc.dst.UDP {
   106  		buf.PutUint8(CommandUDP)
   107  	} else {
   108  		buf.PutUint8(CommandTCP)
   109  	}
   110  
   111  	// Port AddrType Addr
   112  	buf.PutUint16be(uint16(vc.dst.Port))
   113  	buf.PutUint8(vc.dst.AddrType)
   114  	buf.PutSlice(vc.dst.Addr)
   115  
   116  	// padding
   117  	if p > 0 {
   118  		buf.ReadFull(rand.Reader, p)
   119  	}
   120  
   121  	fnv1a := fnv.New32a()
   122  	fnv1a.Write(buf.Bytes())
   123  	buf.PutSlice(fnv1a.Sum(nil))
   124  
   125  	if !vc.isAead {
   126  		block, err := aes.NewCipher(vc.id.CmdKey)
   127  		if err != nil {
   128  			return err
   129  		}
   130  
   131  		stream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp))
   132  		stream.XORKeyStream(buf.Bytes(), buf.Bytes())
   133  		mbuf.PutSlice(buf.Bytes())
   134  		_, err = vc.Conn.Write(mbuf.Bytes())
   135  		return err
   136  	}
   137  
   138  	var fixedLengthCmdKey [16]byte
   139  	copy(fixedLengthCmdKey[:], vc.id.CmdKey)
   140  	vmessout := sealVMessAEADHeader(fixedLengthCmdKey, buf.Bytes(), timestamp)
   141  	_, err := vc.Conn.Write(vmessout)
   142  	return err
   143  }
   144  
   145  func (vc *Conn) recvResponse() error {
   146  	if vc.isVless {
   147  		var buffer [2]byte
   148  		if _, err := io.ReadFull(vc.Conn, buffer[:]); err != nil {
   149  			return err
   150  		}
   151  
   152  		if buffer[0] != 0 {
   153  			return errors.New("unexpected response version")
   154  		}
   155  
   156  		length := int64(buffer[1])
   157  		if length != 0 { // addon data length > 0
   158  			io.CopyN(io.Discard, vc.Conn, length) // just discard
   159  		}
   160  
   161  		return nil
   162  	}
   163  
   164  	var buf []byte
   165  	if !vc.isAead {
   166  		block, err := aes.NewCipher(vc.respBodyKey[:])
   167  		if err != nil {
   168  			return err
   169  		}
   170  
   171  		stream := cipher.NewCFBDecrypter(block, vc.respBodyIV[:])
   172  		buf = make([]byte, 4)
   173  		_, err = io.ReadFull(vc.Conn, buf)
   174  		if err != nil {
   175  			return err
   176  		}
   177  		stream.XORKeyStream(buf, buf)
   178  	} else {
   179  		aeadResponseHeaderLengthEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderLenKey)[:16]
   180  		aeadResponseHeaderLengthEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderLenIV)[:12]
   181  
   182  		aeadResponseHeaderLengthEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)
   183  		aeadResponseHeaderLengthEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)
   184  
   185  		aeadEncryptedResponseHeaderLength := make([]byte, 18)
   186  		if _, err := io.ReadFull(vc.Conn, aeadEncryptedResponseHeaderLength); err != nil {
   187  			return err
   188  		}
   189  
   190  		decryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil)
   191  		if err != nil {
   192  			return err
   193  		}
   194  
   195  		decryptedResponseHeaderLength := binary.BigEndian.Uint16(decryptedResponseHeaderLengthBinaryBuffer)
   196  		aeadResponseHeaderPayloadEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderPayloadKey)[:16]
   197  		aeadResponseHeaderPayloadEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderPayloadIV)[:12]
   198  		aeadResponseHeaderPayloadEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)
   199  		aeadResponseHeaderPayloadEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)
   200  
   201  		encryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16)
   202  		if _, err := io.ReadFull(vc.Conn, encryptedResponseHeaderBuffer); err != nil {
   203  			return err
   204  		}
   205  
   206  		buf, err = aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil)
   207  		if err != nil {
   208  			return err
   209  		}
   210  
   211  		if len(buf) < 4 {
   212  			return errors.New("unexpected buffer length")
   213  		}
   214  	}
   215  
   216  	if buf[0] != vc.respV {
   217  		return errors.New("unexpected response header")
   218  	}
   219  
   220  	if buf[2] != 0 {
   221  		return errors.New("dynamic port is not supported now")
   222  	}
   223  
   224  	return nil
   225  }
   226  
   227  func hashTimestamp(t time.Time) []byte {
   228  	md5hash := md5.New()
   229  	ts := make([]byte, 8)
   230  	binary.BigEndian.PutUint64(ts, uint64(t.Unix()))
   231  	md5hash.Write(ts)
   232  	md5hash.Write(ts)
   233  	md5hash.Write(ts)
   234  	md5hash.Write(ts)
   235  	return md5hash.Sum(nil)
   236  }
   237  
   238  // newConn return a Conn instance
   239  func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool, isVless bool) (*Conn, error) {
   240  	var (
   241  		reqBodyKey  []byte
   242  		reqBodyIV   []byte
   243  		respBodyKey []byte
   244  		respBodyIV  []byte
   245  		respV       byte
   246  		option      byte
   247  	)
   248  
   249  	if !isVless {
   250  		randBytes := make([]byte, 33)
   251  		rand.Read(randBytes)
   252  		reqBodyIV = make([]byte, 16)
   253  		reqBodyKey = make([]byte, 16)
   254  		copy(reqBodyIV[:], randBytes[:16])
   255  		copy(reqBodyKey[:], randBytes[16:32])
   256  		respV = randBytes[32]
   257  		option = OptionChunkStream
   258  
   259  		if isAead {
   260  			bodyKey := sha256.Sum256(reqBodyKey)
   261  			bodyIV := sha256.Sum256(reqBodyIV)
   262  			respBodyKey = bodyKey[:16]
   263  			respBodyIV = bodyIV[:16]
   264  		} else {
   265  			bodyKey := md5.Sum(reqBodyKey)
   266  			bodyIV := md5.Sum(reqBodyIV)
   267  			respBodyKey = bodyKey[:]
   268  			respBodyIV = bodyIV[:]
   269  		}
   270  	}
   271  
   272  	var writer io.Writer
   273  	var reader io.Reader
   274  	switch security {
   275  	case SecurityZero:
   276  		security = SecurityNone
   277  		if !dst.UDP {
   278  			reader = conn
   279  			writer = conn
   280  			option = 0
   281  		} else {
   282  			reader = newChunkReader(conn)
   283  			writer = newChunkWriter(conn)
   284  		}
   285  	case SecurityNone:
   286  		reader = newChunkReader(conn)
   287  		writer = newChunkWriter(conn)
   288  	case SecurityAES128GCM:
   289  		block, _ := aes.NewCipher(reqBodyKey[:])
   290  		aead, _ := cipher.NewGCM(block)
   291  		writer = newAEADWriter(conn, aead, reqBodyIV[:])
   292  
   293  		block, _ = aes.NewCipher(respBodyKey[:])
   294  		aead, _ = cipher.NewGCM(block)
   295  		reader = newAEADReader(conn, aead, respBodyIV[:])
   296  	case SecurityCHACHA20POLY1305:
   297  		key := make([]byte, 32)
   298  		t := md5.Sum(reqBodyKey[:])
   299  		copy(key, t[:])
   300  		t = md5.Sum(key[:16])
   301  		copy(key[16:], t[:])
   302  		aead, _ := chacha20poly1305.New(key)
   303  		writer = newAEADWriter(conn, aead, reqBodyIV[:])
   304  
   305  		t = md5.Sum(respBodyKey[:])
   306  		copy(key, t[:])
   307  		t = md5.Sum(key[:16])
   308  		copy(key[16:], t[:])
   309  		aead, _ = chacha20poly1305.New(key)
   310  		reader = newAEADReader(conn, aead, respBodyIV[:])
   311  	}
   312  
   313  	c := &Conn{
   314  		Conn:        conn,
   315  		id:          id,
   316  		dst:         dst,
   317  		reqBodyIV:   reqBodyIV,
   318  		reqBodyKey:  reqBodyKey,
   319  		respV:       respV,
   320  		respBodyIV:  respBodyIV[:],
   321  		respBodyKey: respBodyKey[:],
   322  		reader:      reader,
   323  		writer:      writer,
   324  		security:    security,
   325  		option:      option,
   326  		isAead:      isAead,
   327  		isVless:     isVless,
   328  	}
   329  	if err := c.sendRequest(); err != nil {
   330  		return nil, err
   331  	}
   332  	return c, nil
   333  }