Fix: trojan/vmess grpc broken

This commit is contained in:
Dreamacro 2021-04-07 22:57:46 +08:00
parent cc96187f58
commit 3d5a0d9f73

View file

@ -4,6 +4,7 @@
package gun package gun
import ( import (
"bufio"
"crypto/tls" "crypto/tls"
"encoding/binary" "encoding/binary"
"errors" "errors"
@ -15,14 +16,13 @@ import (
"sync" "sync"
"time" "time"
"github.com/Dreamacro/clash/common/pool"
"go.uber.org/atomic" "go.uber.org/atomic"
"golang.org/x/net/http2" "golang.org/x/net/http2"
) )
var ( var (
ErrInvalidLength = errors.New("invalid length") ErrInvalidLength = errors.New("invalid length")
ErrSmallBuffer = errors.New("buffer too small")
) )
var ( var (
@ -42,9 +42,8 @@ type Conn struct {
once sync.Once once sync.Once
close *atomic.Bool close *atomic.Bool
err error err error
remain int
buf []byte br *bufio.Reader
offset int
} }
type Config struct { type Config struct {
@ -73,55 +72,78 @@ func (g *Conn) Read(b []byte) (n int, err error) {
return 0, g.err return 0, g.err
} }
if g.buf != nil { if g.br != nil {
n = copy(b, g.buf[g.offset:]) remain := g.br.Buffered()
g.offset += n if len(b) < remain {
if g.offset == len(g.buf) { remain = len(b)
g.offset = 0
g.buf = nil
} }
n, err = g.br.Read(b[:remain])
if g.br.Buffered() == 0 {
g.br = nil
}
return
} else if g.remain != 0 {
size := g.remain
if len(b) < size {
size = len(b)
}
n, err = g.response.Body.Read(b[:size])
g.remain -= n
return return
} else if g.response == nil { } else if g.response == nil {
return 0, net.ErrClosed return 0, net.ErrClosed
} }
buf := make([]byte, 5) // 0x00 grpclength(uint32) 0x0A uleb128 payload
buf := make([]byte, 6)
_, err = io.ReadFull(g.response.Body, buf) _, err = io.ReadFull(g.response.Body, buf)
if err != nil { if err != nil {
return 0, err return 0, err
} }
grpcPayloadLen := binary.BigEndian.Uint32(buf[1:])
if grpcPayloadLen > pool.RelayBufferSize {
return 0, ErrInvalidLength
}
buf = pool.Get(int(grpcPayloadLen)) br := bufio.NewReaderSize(g.response.Body, 16)
_, err = io.ReadFull(g.response.Body, buf) protobufPayloadLen, err := binary.ReadUvarint(br)
if err != nil { if err != nil {
pool.Put(buf)
return 0, io.ErrUnexpectedEOF
}
protobufPayloadLen, protobufLengthLen := decodeUleb128(buf[1:])
if protobufLengthLen == 0 {
pool.Put(buf)
return 0, ErrInvalidLength
}
if grpcPayloadLen != uint32(protobufPayloadLen)+uint32(protobufLengthLen)+1 {
pool.Put(buf)
return 0, ErrInvalidLength return 0, ErrInvalidLength
} }
if len(b) >= int(grpcPayloadLen)-1-int(protobufLengthLen) { bufferedSize := br.Buffered()
n = copy(b, buf[1+protobufLengthLen:]) if len(b) < bufferedSize {
pool.Put(buf) n, err = br.Read(b)
g.br = br
g.remain = int(protobufPayloadLen) - n - g.br.Buffered()
return return
} }
n = copy(b, buf[1+protobufLengthLen:])
g.offset = n + 1 + int(protobufLengthLen) _, err = br.Read(b[:bufferedSize])
g.buf = buf if err != nil {
return return
} }
offset := int(protobufPayloadLen)
if len(b) < int(protobufPayloadLen) {
offset = len(b)
}
if offset == bufferedSize {
return bufferedSize, nil
}
n, err = io.ReadFull(g.response.Body, b[bufferedSize:offset])
if err != nil {
return
}
remain := int(protobufPayloadLen) - offset
if remain > 0 {
g.remain = remain
}
return offset, nil
}
func (g *Conn) Write(b []byte) (n int, err error) { func (g *Conn) Write(b []byte) (n int, err error) {
protobufHeader := appendUleb128([]byte{0x0A}, uint64(len(b))) protobufHeader := appendUleb128([]byte{0x0A}, uint64(len(b)))
grpcHeader := make([]byte, 5) grpcHeader := make([]byte, 5)