Browse Source

implement header and auth for quic

Darien Raymond 7 years ago
parent
commit
3335f77c70

+ 47 - 0
transport/internet/quic/config.go

@@ -0,0 +1,47 @@
+package quic
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/sha256"
+
+	"golang.org/x/crypto/chacha20poly1305"
+	"v2ray.com/core/common"
+	"v2ray.com/core/common/protocol"
+	"v2ray.com/core/transport/internet"
+)
+
+func getAuth(config *Config) (cipher.AEAD, error) {
+	security := config.Security.GetSecurityType()
+	if security == protocol.SecurityType_NONE {
+		return nil, nil
+	}
+
+	salted := []byte(config.Key + "v2ray-quic-salt")
+	key := sha256.Sum256(salted)
+
+	if security == protocol.SecurityType_AES128_GCM {
+		block, err := aes.NewCipher(key[:16])
+		common.Must(err)
+		return cipher.NewGCM(block)
+	}
+
+	if security == protocol.SecurityType_CHACHA20_POLY1305 {
+		return chacha20poly1305.New(key[:])
+	}
+
+	return nil, newError("unsupported security type")
+}
+
+func getHeader(config *Config) (internet.PacketHeader, error) {
+	if config.Header == nil {
+		return nil, nil
+	}
+
+	msg, err := config.Header.GetInstance()
+	if err != nil {
+		return nil, err
+	}
+
+	return internet.CreatePacketHeader(msg)
+}

+ 32 - 21
transport/internet/quic/config.pb.go

@@ -4,6 +4,7 @@ import (
 	fmt "fmt"
 	proto "github.com/golang/protobuf/proto"
 	math "math"
+	protocol "v2ray.com/core/common/protocol"
 	serial "v2ray.com/core/common/serial"
 )
 
@@ -19,11 +20,12 @@ var _ = math.Inf
 const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
 
 type Config struct {
-	Key                  string               `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
-	Header               *serial.TypedMessage `protobuf:"bytes,2,opt,name=header,proto3" json:"header,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}             `json:"-"`
-	XXX_unrecognized     []byte               `json:"-"`
-	XXX_sizecache        int32                `json:"-"`
+	Key                  string                   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+	Security             *protocol.SecurityConfig `protobuf:"bytes,2,opt,name=security,proto3" json:"security,omitempty"`
+	Header               *serial.TypedMessage     `protobuf:"bytes,3,opt,name=header,proto3" json:"header,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                 `json:"-"`
+	XXX_unrecognized     []byte                   `json:"-"`
+	XXX_sizecache        int32                    `json:"-"`
 }
 
 func (m *Config) Reset()         { *m = Config{} }
@@ -58,6 +60,13 @@ func (m *Config) GetKey() string {
 	return ""
 }
 
+func (m *Config) GetSecurity() *protocol.SecurityConfig {
+	if m != nil {
+		return m.Security
+	}
+	return nil
+}
+
 func (m *Config) GetHeader() *serial.TypedMessage {
 	if m != nil {
 		return m.Header
@@ -74,20 +83,22 @@ func init() {
 }
 
 var fileDescriptor_462e2eb906061b36 = []byte{
-	// 226 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x8e, 0xb1, 0x4b, 0x03, 0x31,
-	0x14, 0x87, 0x49, 0x95, 0x03, 0xe3, 0x22, 0x37, 0x15, 0xa7, 0x72, 0x43, 0xe9, 0xf4, 0x22, 0xd7,
-	0xdd, 0xc1, 0x4e, 0x0e, 0x82, 0x1e, 0xd5, 0xa1, 0x8b, 0xc4, 0xf4, 0x59, 0x83, 0x26, 0xef, 0x7c,
-	0x49, 0x85, 0xfc, 0x4b, 0xfe, 0x95, 0x92, 0xc6, 0x3b, 0xc4, 0xa5, 0x53, 0x32, 0xfc, 0xbe, 0xef,
-	0x7d, 0x72, 0xf9, 0xd5, 0xb2, 0x4e, 0x60, 0xc8, 0x29, 0x43, 0x8c, 0x2a, 0xb2, 0xf6, 0xa1, 0x27,
-	0x8e, 0xca, 0xfa, 0x88, 0xec, 0x31, 0xaa, 0xcf, 0xbd, 0x35, 0xca, 0x90, 0x7f, 0xb5, 0x3b, 0xe8,
-	0x99, 0x22, 0xd5, 0xcd, 0x00, 0x31, 0xc2, 0x08, 0xc0, 0x00, 0x40, 0x06, 0x2e, 0xaf, 0xfe, 0x89,
-	0x0d, 0x39, 0x47, 0x5e, 0x05, 0x64, 0xab, 0x3f, 0x54, 0x4c, 0x3d, 0x6e, 0x9f, 0x1d, 0x86, 0xa0,
-	0x77, 0x58, 0xac, 0xcd, 0x46, 0x56, 0xab, 0xc3, 0x95, 0xfa, 0x42, 0x9e, 0xbc, 0x63, 0x9a, 0x8a,
-	0x99, 0x58, 0x9c, 0x75, 0xf9, 0x5b, 0x5f, 0xcb, 0xea, 0x0d, 0xf5, 0x16, 0x79, 0x3a, 0x99, 0x89,
-	0xc5, 0x79, 0x3b, 0x87, 0x3f, 0x09, 0x45, 0x0d, 0x45, 0x0d, 0xeb, 0xac, 0xbe, 0x2b, 0xe6, 0xee,
-	0x97, 0xba, 0x79, 0x94, 0x73, 0x43, 0x0e, 0x8e, 0x77, 0xdf, 0x8b, 0xcd, 0x69, 0x7e, 0xbf, 0x27,
-	0xcd, 0x53, 0xdb, 0xe9, 0x04, 0xab, 0x3c, 0x5e, 0x8f, 0xe3, 0xdb, 0x61, 0xfc, 0xb0, 0xb7, 0xe6,
-	0xa5, 0x3a, 0x94, 0x2f, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x9a, 0x6a, 0xd1, 0x56, 0x46, 0x01,
-	0x00, 0x00,
+	// 269 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0xc1, 0x4b, 0xc3, 0x30,
+	0x14, 0xc6, 0xe9, 0x26, 0x45, 0xe3, 0x45, 0x7a, 0x2a, 0x3b, 0x8d, 0x1e, 0xc6, 0x10, 0x79, 0x91,
+	0xee, 0xee, 0xc1, 0x81, 0xe0, 0x41, 0xd0, 0x3a, 0x3d, 0x78, 0x91, 0x98, 0x3d, 0x67, 0x70, 0xed,
+	0xab, 0x2f, 0xa9, 0xd0, 0x7f, 0xc7, 0xa3, 0x7f, 0xa5, 0xa4, 0x59, 0x8b, 0xc8, 0xc0, 0x53, 0x0b,
+	0xf9, 0x7e, 0xbf, 0x7c, 0x5f, 0xc4, 0xe2, 0x33, 0x67, 0xd5, 0x82, 0xa6, 0x52, 0x6a, 0x62, 0x94,
+	0x8e, 0x55, 0x65, 0x6b, 0x62, 0x27, 0x4d, 0xe5, 0x90, 0x2b, 0x74, 0xf2, 0xa3, 0x31, 0x5a, 0x6a,
+	0xaa, 0x5e, 0xcd, 0x06, 0x6a, 0x26, 0x47, 0x49, 0xd6, 0x43, 0x8c, 0x30, 0x00, 0xd0, 0x03, 0xe0,
+	0x81, 0xc9, 0xf9, 0x1f, 0xb1, 0xa6, 0xb2, 0xa4, 0x4a, 0x5a, 0x64, 0xa3, 0xb6, 0xd2, 0xb5, 0x35,
+	0xae, 0x9f, 0x4b, 0xb4, 0x56, 0x6d, 0x30, 0x58, 0x27, 0x67, 0xfb, 0x89, 0xee, 0x50, 0xd3, 0x56,
+	0xbe, 0xa1, 0x5a, 0x23, 0xdb, 0x90, 0xce, 0xbe, 0x22, 0x11, 0x2f, 0xbb, 0x52, 0xc9, 0x89, 0x18,
+	0xbf, 0x63, 0x9b, 0x46, 0xd3, 0x68, 0x7e, 0x54, 0xf8, 0xdf, 0xe4, 0x4a, 0x1c, 0x5a, 0xd4, 0x0d,
+	0x1b, 0xd7, 0xa6, 0xa3, 0x69, 0x34, 0x3f, 0xce, 0x4f, 0xe1, 0x57, 0xe7, 0x60, 0x86, 0xde, 0x0c,
+	0xf7, 0xbb, 0x6c, 0xf0, 0x15, 0x03, 0x9b, 0x5c, 0x88, 0x38, 0xdc, 0x9a, 0x8e, 0x3b, 0xcb, 0x6c,
+	0x8f, 0x25, 0x2c, 0x82, 0x95, 0x5f, 0x74, 0x13, 0x06, 0x15, 0x3b, 0xea, 0xf2, 0x41, 0xcc, 0x34,
+	0x95, 0xf0, 0xff, 0x73, 0xdd, 0x46, 0x4f, 0x07, 0xfe, 0xfb, 0x3d, 0xca, 0x1e, 0xf3, 0x42, 0xb5,
+	0xb0, 0xf4, 0xe1, 0xd5, 0x10, 0xbe, 0xee, 0xc3, 0x77, 0x8d, 0xd1, 0x2f, 0x71, 0xd7, 0x7c, 0xf1,
+	0x13, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x05, 0x0f, 0xf6, 0xbd, 0x01, 0x00, 0x00,
 }

+ 3 - 1
transport/internet/quic/config.proto

@@ -7,8 +7,10 @@ option java_package = "com.v2ray.core.transport.internet.quic";
 option java_multiple_files = true;
 
 import "v2ray.com/core/common/serial/typed_message.proto";
+import "v2ray.com/core/common/protocol/headers.proto";
 
 message Config {
   string key = 1;
-  v2ray.core.common.serial.TypedMessage header = 2;
+  v2ray.core.common.protocol.SecurityConfig security = 2;
+  v2ray.core.common.serial.TypedMessage header = 3;
 }

+ 76 - 15
transport/internet/quic/conn.go

@@ -1,10 +1,14 @@
 package quic
 
 import (
+	"crypto/cipher"
+	"crypto/rand"
+	"errors"
 	"time"
 
 	quic "github.com/lucas-clemente/quic-go"
 
+	"v2ray.com/core/common"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/transport/internet"
 )
@@ -12,43 +16,100 @@ import (
 type sysConn struct {
 	conn   net.PacketConn
 	header internet.PacketHeader
+	auth   cipher.AEAD
 }
 
-func (c *sysConn) ReadFrom(p []byte) (int, net.Addr, error) {
-	if c.header == nil {
-		return c.conn.ReadFrom(p)
+func wrapSysConn(rawConn net.PacketConn, config *Config) (*sysConn, error) {
+	header, err := getHeader(config)
+	if err != nil {
+		return nil, err
 	}
+	auth, err := getAuth(config)
+	if err != nil {
+		return nil, err
+	}
+	return &sysConn{
+		conn:   rawConn,
+		header: header,
+		auth:   auth,
+	}, nil
+}
+
+var errCipherError = errors.New("cipher error")
 
-	overhead := int(c.header.Size())
+func (c *sysConn) readFromInternal(p []byte) (int, net.Addr, error) {
 	buffer := getBuffer()
 	defer putBuffer(buffer)
 
-	nBytes, addr, err := c.conn.ReadFrom(buffer[:len(p)+overhead])
+	nBytes, addr, err := c.conn.ReadFrom(buffer)
 	if err != nil {
 		return 0, nil, err
 	}
 
-	copy(p, buffer[overhead:nBytes])
+	payload := buffer[:nBytes]
+	if c.header != nil {
+		payload = payload[c.header.Size():]
+	}
+
+	if c.auth == nil {
+		n := copy(p, payload)
+		return n, addr, nil
+	}
 
-	return nBytes - overhead, addr, nil
+	nonce := payload[:c.auth.NonceSize()]
+	payload = payload[c.auth.NonceSize():]
+
+	p, err = c.auth.Open(p[:0], nonce, payload, nil)
+	if err != nil {
+		return 0, nil, errCipherError
+	}
+
+	return len(p), addr, nil
+}
+
+func (c *sysConn) ReadFrom(p []byte) (int, net.Addr, error) {
+	if c.header == nil && c.auth == nil {
+		return c.conn.ReadFrom(p)
+	}
+
+	for {
+		n, addr, err := c.readFromInternal(p)
+		if err != nil && err != errCipherError {
+			return 0, nil, err
+		}
+		if err == nil {
+			return n, addr, nil
+		}
+	}
 }
 
 func (c *sysConn) WriteTo(p []byte, addr net.Addr) (int, error) {
-	if c.header == nil {
+	if c.header == nil && c.auth == nil {
 		return c.conn.WriteTo(p, addr)
 	}
 
 	buffer := getBuffer()
 	defer putBuffer(buffer)
 
-	overhead := int(c.header.Size())
-	c.header.Serialize(buffer)
-	copy(buffer[overhead:], p)
-	nBytes, err := c.conn.WriteTo(buffer[:len(p)+overhead], addr)
-	if err != nil {
-		return 0, err
+	payload := buffer
+	n := 0
+	if c.header != nil {
+		c.header.Serialize(payload)
+		n = int(c.header.Size())
 	}
-	return nBytes - overhead, nil
+
+	if c.auth == nil {
+		nBytes := copy(payload[n:], p)
+		n += nBytes
+	} else {
+		nounce := payload[n : n+c.auth.NonceSize()]
+		common.Must2(rand.Read(nounce))
+		n += c.auth.NonceSize()
+		pp := c.auth.Seal(payload[:n], nounce, p, nil)
+		n = len(pp)
+	}
+
+	return c.conn.WriteTo(payload[:n], addr)
 }
 
 func (c *sysConn) Close() error {

+ 18 - 6
transport/internet/quic/dialer.go

@@ -18,7 +18,7 @@ type clientSessions struct {
 	sessions map[net.Destination]quic.Session
 }
 
-func (s *clientSessions) getSession(destAddr net.Addr, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (quic.Session, error) {
+func (s *clientSessions) getSession(destAddr net.Addr, config *Config, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (quic.Session, error) {
 	s.access.Lock()
 	defer s.access.Unlock()
 
@@ -32,7 +32,7 @@ func (s *clientSessions) getSession(destAddr net.Addr, tlsConfig *tls.Config, so
 		return session, nil
 	}
 
-	conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
+	rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
 		IP:   []byte{0, 0, 0, 0},
 		Port: 0,
 	}, sockopt)
@@ -40,14 +40,21 @@ func (s *clientSessions) getSession(destAddr net.Addr, tlsConfig *tls.Config, so
 		return nil, err
 	}
 
-	config := &quic.Config{
+	quicConfig := &quic.Config{
 		Versions:           []quic.VersionNumber{quic.VersionMilestone0_10_0},
 		ConnectionIDLength: 12,
 		KeepAlive:          true,
 	}
 
-	session, err := quic.DialContext(context.Background(), conn, destAddr, "", tlsConfig.GetTLSConfig(tls.WithDestination(dest)), config)
+	conn, err := wrapSysConn(rawConn, config)
 	if err != nil {
+		rawConn.Close()
+		return nil, err
+	}
+
+	session, err := quic.DialContext(context.Background(), conn, destAddr, "", tlsConfig.GetTLSConfig(tls.WithDestination(dest)), quicConfig)
+	if err != nil {
+		rawConn.Close()
 		return nil, err
 	}
 
@@ -60,7 +67,10 @@ var client clientSessions
 func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
 	tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
 	if tlsConfig == nil {
-		return nil, newError("TLS not enabled for QUIC")
+		tlsConfig = &tls.Config{
+			ServerName:    internalDomain,
+			AllowInsecure: true,
+		}
 	}
 
 	destAddr, err := net.ResolveUDPAddr("udp", dest.NetAddr())
@@ -68,7 +78,9 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
 		return nil, err
 	}
 
-	session, err := client.getSession(destAddr, tlsConfig, streamSettings.SocketSettings)
+	config := streamSettings.ProtocolSettings.(*Config)
+
+	session, err := client.getSession(destAddr, config, tlsConfig, streamSettings.SocketSettings)
 	if err != nil {
 		return nil, err
 	}

+ 5 - 2
transport/internet/quic/errors.generated.go

@@ -2,5 +2,8 @@ package quic
 
 import "v2ray.com/core/common/errors"
 
-type errPathObjHolder struct {}
-func newError(values ...interface{}) *errors.Error { return errors.New(values...).WithPathObj(errPathObjHolder{}) }
+type errPathObjHolder struct{}
+
+func newError(values ...interface{}) *errors.Error {
+	return errors.New(values...).WithPathObj(errPathObjHolder{})
+}

+ 15 - 4
transport/internet/quic/hub.go

@@ -6,6 +6,7 @@ import (
 	quic "github.com/lucas-clemente/quic-go"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/net"
+	"v2ray.com/core/common/protocol/tls/cert"
 	"v2ray.com/core/transport/internet"
 	"v2ray.com/core/transport/internet/tls"
 )
@@ -66,10 +67,13 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
 
 	tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
 	if tlsConfig == nil {
-		return nil, newError("TLS config not enabled for QUIC")
+		tlsConfig = &tls.Config{
+			Certificate: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.DNSNames(internalDomain), cert.CommonName(internalDomain)))},
+		}
 	}
 
-	conn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
+	config := streamSettings.ProtocolSettings.(*Config)
+	rawConn, err := internet.ListenSystemPacket(context.Background(), &net.UDPAddr{
 		IP:   address.IP(),
 		Port: int(port),
 	}, streamSettings.SocketSettings)
@@ -78,15 +82,22 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
 		return nil, err
 	}
 
-	config := &quic.Config{
+	quicConfig := &quic.Config{
 		Versions:           []quic.VersionNumber{quic.VersionMilestone0_10_0},
 		ConnectionIDLength: 12,
 		KeepAlive:          true,
 		AcceptCookie:       func(net.Addr, *quic.Cookie) bool { return true },
 	}
 
-	qListener, err := quic.Listen(conn, tlsConfig.GetTLSConfig(), config)
+	conn, err := wrapSysConn(rawConn, config)
+	if err != nil {
+		conn.Close()
+		return nil, err
+	}
+
+	qListener, err := quic.Listen(conn, tlsConfig.GetTLSConfig(), quicConfig)
 	if err != nil {
+		rawConn.Close()
 		return nil, err
 	}
 

+ 1 - 0
transport/internet/quic/quic.go

@@ -14,6 +14,7 @@ import (
 //
 
 const protocolName = "quic"
+const internalDomain = "quic.internal.v2ray.com"
 
 func init() {
 	common.Must(internet.RegisterProtocolConfigCreatorByName(protocolName, func() interface{} {

+ 139 - 0
transport/internet/quic/quic_test.go

@@ -9,9 +9,12 @@ import (
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/buf"
 	"v2ray.com/core/common/net"
+	"v2ray.com/core/common/protocol"
 	"v2ray.com/core/common/protocol/tls/cert"
+	"v2ray.com/core/common/serial"
 	"v2ray.com/core/testing/servers/udp"
 	"v2ray.com/core/transport/internet"
+	"v2ray.com/core/transport/internet/headers/wireguard"
 	"v2ray.com/core/transport/internet/quic"
 	"v2ray.com/core/transport/internet/tls"
 	. "v2ray.com/ext/assert"
@@ -87,3 +90,139 @@ func TestQuicConnection(t *testing.T) {
 	common.Must2(b2.ReadFullFrom(conn, N))
 	assert(b2.Bytes(), Equals, b1)
 }
+
+func TestQuicConnectionWithoutTLS(t *testing.T) {
+	assert := With(t)
+
+	port := udp.PickPort()
+
+	listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
+		ProtocolName:     "quic",
+		ProtocolSettings: &quic.Config{},
+	}, func(conn internet.Connection) {
+		go func() {
+			defer conn.Close()
+
+			b := buf.New()
+			defer b.Release()
+
+			for {
+				b.Clear()
+				if _, err := b.ReadFrom(conn); err != nil {
+					return
+				}
+				nBytes, err := conn.Write(b.Bytes())
+				assert(err, IsNil)
+				assert(int32(nBytes), Equals, b.Len())
+			}
+		}()
+	})
+	assert(err, IsNil)
+
+	defer listener.Close()
+
+	time.Sleep(time.Second)
+
+	dctx := context.Background()
+	conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
+		ProtocolName:     "quic",
+		ProtocolSettings: &quic.Config{},
+	})
+	assert(err, IsNil)
+	defer conn.Close()
+
+	const N = 1024
+	b1 := make([]byte, N)
+	common.Must2(rand.Read(b1))
+	b2 := buf.New()
+
+	nBytes, err := conn.Write(b1)
+	assert(nBytes, Equals, N)
+	assert(err, IsNil)
+
+	b2.Clear()
+	common.Must2(b2.ReadFullFrom(conn, N))
+	assert(b2.Bytes(), Equals, b1)
+
+	nBytes, err = conn.Write(b1)
+	assert(nBytes, Equals, N)
+	assert(err, IsNil)
+
+	b2.Clear()
+	common.Must2(b2.ReadFullFrom(conn, N))
+	assert(b2.Bytes(), Equals, b1)
+}
+
+func TestQuicConnectionAuthHeader(t *testing.T) {
+	assert := With(t)
+
+	port := udp.PickPort()
+
+	listener, err := quic.Listen(context.Background(), net.LocalHostIP, port, &internet.MemoryStreamConfig{
+		ProtocolName: "quic",
+		ProtocolSettings: &quic.Config{
+			Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}),
+			Key:    "abcd",
+			Security: &protocol.SecurityConfig{
+				Type: protocol.SecurityType_AES128_GCM,
+			},
+		},
+	}, func(conn internet.Connection) {
+		go func() {
+			defer conn.Close()
+
+			b := buf.New()
+			defer b.Release()
+
+			for {
+				b.Clear()
+				if _, err := b.ReadFrom(conn); err != nil {
+					return
+				}
+				nBytes, err := conn.Write(b.Bytes())
+				assert(err, IsNil)
+				assert(int32(nBytes), Equals, b.Len())
+			}
+		}()
+	})
+	assert(err, IsNil)
+
+	defer listener.Close()
+
+	time.Sleep(time.Second)
+
+	dctx := context.Background()
+	conn, err := quic.Dial(dctx, net.TCPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
+		ProtocolName: "quic",
+		ProtocolSettings: &quic.Config{
+			Header: serial.ToTypedMessage(&wireguard.WireguardConfig{}),
+			Key:    "abcd",
+			Security: &protocol.SecurityConfig{
+				Type: protocol.SecurityType_AES128_GCM,
+			},
+		},
+	})
+	assert(err, IsNil)
+	defer conn.Close()
+
+	const N = 1024
+	b1 := make([]byte, N)
+	common.Must2(rand.Read(b1))
+	b2 := buf.New()
+
+	nBytes, err := conn.Write(b1)
+	assert(nBytes, Equals, N)
+	assert(err, IsNil)
+
+	b2.Clear()
+	common.Must2(b2.ReadFullFrom(conn, N))
+	assert(b2.Bytes(), Equals, b1)
+
+	nBytes, err = conn.Write(b1)
+	assert(nBytes, Equals, N)
+	assert(err, IsNil)
+
+	b2.Clear()
+	common.Must2(b2.ReadFullFrom(conn, N))
+	assert(b2.Bytes(), Equals, b1)
+}

+ 1 - 1
vendor/github.com/lucas-clemente/quic-go/internal/protocol/protocol.go

@@ -65,7 +65,7 @@ type ApplicationErrorCode uint16
 // ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header,
 // UDP adds an additional 8 bytes.  This is a total overhead of 48 bytes.
 // Ethernet's max packet size is 1500 bytes,  1500 - 48 = 1452.
-const MaxReceivePacketSize ByteCount = 1452 - 32
+const MaxReceivePacketSize ByteCount = 1452 - 64
 
 // DefaultTCPMSS is the default maximum packet size used in the Linux TCP implementation.
 // Used in QUIC for congestion window computations in bytes.