|
@@ -0,0 +1,120 @@
|
|
|
+package qtls
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "crypto/tls"
|
|
|
+ "net"
|
|
|
+ "net/http"
|
|
|
+
|
|
|
+ "github.com/sagernet/quic-go"
|
|
|
+ "github.com/sagernet/quic-go/http3"
|
|
|
+ M "github.com/sagernet/sing/common/metadata"
|
|
|
+ aTLS "github.com/sagernet/sing/common/tls"
|
|
|
+)
|
|
|
+
|
|
|
+type QUICConfig interface {
|
|
|
+ Dial(ctx context.Context, conn net.PacketConn, addr net.Addr, config *quic.Config) (quic.Connection, error)
|
|
|
+ DialEarly(ctx context.Context, conn net.PacketConn, addr net.Addr, config *quic.Config) (quic.EarlyConnection, error)
|
|
|
+ CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, quicConfig *quic.Config, enableDatagrams bool) http.RoundTripper
|
|
|
+}
|
|
|
+
|
|
|
+type QUICServerConfig interface {
|
|
|
+ Listen(conn net.PacketConn, config *quic.Config) (QUICListener, error)
|
|
|
+ ListenEarly(conn net.PacketConn, config *quic.Config) (QUICEarlyListener, error)
|
|
|
+ ConfigureHTTP3()
|
|
|
+}
|
|
|
+
|
|
|
+type QUICListener interface {
|
|
|
+ Accept(ctx context.Context) (quic.Connection, error)
|
|
|
+ Close() error
|
|
|
+ Addr() net.Addr
|
|
|
+}
|
|
|
+
|
|
|
+type QUICEarlyListener interface {
|
|
|
+ Accept(ctx context.Context) (quic.EarlyConnection, error)
|
|
|
+ Close() error
|
|
|
+ Addr() net.Addr
|
|
|
+}
|
|
|
+
|
|
|
+func Dial(ctx context.Context, conn net.PacketConn, addr net.Addr, config aTLS.Config, quicConfig *quic.Config) (quic.Connection, error) {
|
|
|
+ if quicTLSConfig, isQUICConfig := config.(QUICConfig); isQUICConfig {
|
|
|
+ return quicTLSConfig.Dial(ctx, conn, addr, quicConfig)
|
|
|
+ }
|
|
|
+ tlsConfig, err := config.Config()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return quic.Dial(ctx, conn, addr, tlsConfig, quicConfig)
|
|
|
+}
|
|
|
+
|
|
|
+func DialEarly(ctx context.Context, conn net.PacketConn, addr net.Addr, config aTLS.Config, quicConfig *quic.Config) (quic.EarlyConnection, error) {
|
|
|
+ if quicTLSConfig, isQUICConfig := config.(QUICConfig); isQUICConfig {
|
|
|
+ return quicTLSConfig.DialEarly(ctx, conn, addr, quicConfig)
|
|
|
+ }
|
|
|
+ tlsConfig, err := config.Config()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return quic.DialEarly(ctx, conn, addr, tlsConfig, quicConfig)
|
|
|
+}
|
|
|
+
|
|
|
+func CreateTransport(conn net.PacketConn, quicConnPtr *quic.EarlyConnection, serverAddr M.Socksaddr, config aTLS.Config, quicConfig *quic.Config, enableDatagrams bool) (http.RoundTripper, error) {
|
|
|
+ if quicTLSConfig, isQUICConfig := config.(QUICConfig); isQUICConfig {
|
|
|
+ return quicTLSConfig.CreateTransport(conn, quicConnPtr, serverAddr, quicConfig, enableDatagrams), nil
|
|
|
+ }
|
|
|
+ tlsConfig, err := config.Config()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return &http3.RoundTripper{
|
|
|
+ TLSClientConfig: tlsConfig,
|
|
|
+ QuicConfig: quicConfig,
|
|
|
+ EnableDatagrams: enableDatagrams,
|
|
|
+ Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
|
|
+ quicConn, err := quic.DialEarly(ctx, conn, serverAddr.UDPAddr(), tlsCfg, cfg)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ *quicConnPtr = quicConn
|
|
|
+ return quicConn, nil
|
|
|
+ },
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+func Listen(conn net.PacketConn, config aTLS.ServerConfig, quicConfig *quic.Config) (QUICListener, error) {
|
|
|
+ if quicTLSConfig, isQUICConfig := config.(QUICServerConfig); isQUICConfig {
|
|
|
+ return quicTLSConfig.Listen(conn, quicConfig)
|
|
|
+ }
|
|
|
+ tlsConfig, err := config.Config()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return quic.Listen(conn, tlsConfig, quicConfig)
|
|
|
+}
|
|
|
+
|
|
|
+func ListenEarly(conn net.PacketConn, config aTLS.ServerConfig, quicConfig *quic.Config) (QUICEarlyListener, error) {
|
|
|
+ if quicTLSConfig, isQUICConfig := config.(QUICServerConfig); isQUICConfig {
|
|
|
+ return quicTLSConfig.ListenEarly(conn, quicConfig)
|
|
|
+ }
|
|
|
+ tlsConfig, err := config.Config()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return quic.ListenEarly(conn, tlsConfig, quicConfig)
|
|
|
+}
|
|
|
+
|
|
|
+func ConfigureHTTP3(config aTLS.ServerConfig) error {
|
|
|
+ if len(config.NextProtos()) == 0 {
|
|
|
+ config.SetNextProtos([]string{http3.NextProtoH3})
|
|
|
+ }
|
|
|
+ if quicTLSConfig, isQUICConfig := config.(QUICServerConfig); isQUICConfig {
|
|
|
+ quicTLSConfig.ConfigureHTTP3()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ tlsConfig, err := config.Config()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ http3.ConfigureTLSConfig(tlsConfig)
|
|
|
+ return nil
|
|
|
+}
|