github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/outbound/tuic.go (about) 1 //go:build with_quic 2 3 package outbound 4 5 import ( 6 "context" 7 "net" 8 "os" 9 "time" 10 11 "github.com/inazumav/sing-box/adapter" 12 "github.com/inazumav/sing-box/common/dialer" 13 "github.com/inazumav/sing-box/common/tls" 14 C "github.com/inazumav/sing-box/constant" 15 "github.com/inazumav/sing-box/log" 16 "github.com/inazumav/sing-box/option" 17 "github.com/inazumav/sing-box/transport/tuic" 18 "github.com/sagernet/sing/common" 19 "github.com/sagernet/sing/common/bufio" 20 E "github.com/sagernet/sing/common/exceptions" 21 M "github.com/sagernet/sing/common/metadata" 22 N "github.com/sagernet/sing/common/network" 23 "github.com/sagernet/sing/common/uot" 24 25 "github.com/gofrs/uuid/v5" 26 ) 27 28 var ( 29 _ adapter.Outbound = (*TUIC)(nil) 30 _ adapter.InterfaceUpdateListener = (*TUIC)(nil) 31 ) 32 33 type TUIC struct { 34 myOutboundAdapter 35 client *tuic.Client 36 udpStream bool 37 } 38 39 func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (*TUIC, error) { 40 options.UDPFragmentDefault = true 41 if options.TLS == nil || !options.TLS.Enabled { 42 return nil, C.ErrTLSRequired 43 } 44 tlsConfig, err := tls.NewClient(ctx, options.Server, common.PtrValueOrDefault(options.TLS)) 45 if err != nil { 46 return nil, err 47 } 48 userUUID, err := uuid.FromString(options.UUID) 49 if err != nil { 50 return nil, E.Cause(err, "invalid uuid") 51 } 52 var tuicUDPStream bool 53 if options.UDPOverStream && options.UDPRelayMode != "" { 54 return nil, E.New("udp_over_stream is conflict with udp_relay_mode") 55 } 56 switch options.UDPRelayMode { 57 case "native": 58 case "quic": 59 tuicUDPStream = true 60 } 61 outboundDialer, err := dialer.New(router, options.DialerOptions) 62 if err != nil { 63 return nil, err 64 } 65 client, err := tuic.NewClient(tuic.ClientOptions{ 66 Context: ctx, 67 Dialer: outboundDialer, 68 ServerAddress: options.ServerOptions.Build(), 69 TLSConfig: tlsConfig, 70 UUID: userUUID, 71 Password: options.Password, 72 CongestionControl: options.CongestionControl, 73 UDPStream: tuicUDPStream, 74 ZeroRTTHandshake: options.ZeroRTTHandshake, 75 Heartbeat: time.Duration(options.Heartbeat), 76 }) 77 if err != nil { 78 return nil, err 79 } 80 return &TUIC{ 81 myOutboundAdapter: myOutboundAdapter{ 82 protocol: C.TypeTUIC, 83 network: options.Network.Build(), 84 router: router, 85 logger: logger, 86 tag: tag, 87 dependencies: withDialerDependency(options.DialerOptions), 88 }, 89 client: client, 90 udpStream: options.UDPOverStream, 91 }, nil 92 } 93 94 func (h *TUIC) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { 95 switch N.NetworkName(network) { 96 case N.NetworkTCP: 97 h.logger.InfoContext(ctx, "outbound connection to ", destination) 98 return h.client.DialConn(ctx, destination) 99 case N.NetworkUDP: 100 if h.udpStream { 101 h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination) 102 streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version)) 103 if err != nil { 104 return nil, err 105 } 106 return uot.NewLazyConn(streamConn, uot.Request{ 107 IsConnect: true, 108 Destination: destination, 109 }), nil 110 } else { 111 conn, err := h.ListenPacket(ctx, destination) 112 if err != nil { 113 return nil, err 114 } 115 return bufio.NewBindPacketConn(conn, destination), nil 116 } 117 default: 118 return nil, E.New("unsupported network: ", network) 119 } 120 } 121 122 func (h *TUIC) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { 123 if h.udpStream { 124 h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination) 125 streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version)) 126 if err != nil { 127 return nil, err 128 } 129 return uot.NewLazyConn(streamConn, uot.Request{ 130 IsConnect: false, 131 Destination: destination, 132 }), nil 133 } else { 134 h.logger.InfoContext(ctx, "outbound packet connection to ", destination) 135 return h.client.ListenPacket(ctx) 136 } 137 } 138 139 func (h *TUIC) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { 140 return NewConnection(ctx, h, conn, metadata) 141 } 142 143 func (h *TUIC) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { 144 return NewPacketConnection(ctx, h, conn, metadata) 145 } 146 147 func (h *TUIC) InterfaceUpdated() { 148 _ = h.client.CloseWithError(E.New("network changed")) 149 } 150 151 func (h *TUIC) Close() error { 152 return h.client.CloseWithError(os.ErrClosed) 153 }