55 "encoding/hex"
66 "fmt"
77 "net"
8+ "os"
89 "time"
910
1011 "github.com/sagernet/sing-box/adapter"
@@ -14,6 +15,7 @@ import (
1415 "github.com/sagernet/sing/common/logger"
1516 M "github.com/sagernet/sing/common/metadata"
1617 N "github.com/sagernet/sing/common/network"
18+ "github.com/sagernet/sing/common/uot"
1719
1820 "github.com/getlantern/lantern-box/constant"
1921 "github.com/getlantern/lantern-box/option"
@@ -29,10 +31,24 @@ func RegisterOutbound(registry *outbound.Registry) {
2931// Outbound represents a Samizdat outbound adapter.
3032type Outbound struct {
3133 outbound.Adapter
32- logger logger.ContextLogger
34+ logger logger.ContextLogger
35+ client * samizdat.Client
36+ uotClient * uot.Client
37+ }
38+
39+ // samizdatDialer adapts a samizdat.Client to the N.Dialer interface for uot.Client.
40+ type samizdatDialer struct {
3341 client * samizdat.Client
3442}
3543
44+ func (d * samizdatDialer ) DialContext (ctx context.Context , network string , destination M.Socksaddr ) (net.Conn , error ) {
45+ return d .client .DialContext (ctx , network , destination .String ())
46+ }
47+
48+ func (d * samizdatDialer ) ListenPacket (ctx context.Context , destination M.Socksaddr ) (net.PacketConn , error ) {
49+ return nil , os .ErrInvalid
50+ }
51+
3652// NewOutbound creates a new Samizdat outbound adapter.
3753func NewOutbound (
3854 ctx context.Context ,
@@ -107,16 +123,23 @@ func NewOutbound(
107123 return nil , fmt .Errorf ("creating samizdat client: %w" , err )
108124 }
109125
110- return & Outbound {
126+ o := & Outbound {
111127 Adapter : outbound .NewAdapterWithDialerOptions (
112128 constant .TypeSamizdat ,
113129 tag ,
114- []string {N .NetworkTCP },
130+ []string {N .NetworkTCP , N . NetworkUDP },
115131 options .DialerOptions ,
116132 ),
117133 logger : logger ,
118134 client : client ,
119- }, nil
135+ }
136+
137+ o .uotClient = & uot.Client {
138+ Dialer : & samizdatDialer {client : client },
139+ Version : uot .Version ,
140+ }
141+
142+ return o , nil
120143}
121144
122145// DialContext dials a connection to the destination through the Samizdat proxy.
@@ -125,23 +148,29 @@ func (o *Outbound) DialContext(ctx context.Context, network string, destination
125148 metadata .Outbound = o .Tag ()
126149 metadata .Destination = destination
127150
128- o .logger .InfoContext (ctx , "connecting to " , destination )
129- conn , err := o .client .DialContext (ctx , network , destination .String ())
130- if err != nil {
131- return nil , fmt .Errorf ("samizdat dial to %s: %w" , destination , err )
151+ switch N .NetworkName (network ) {
152+ case N .NetworkTCP :
153+ o .logger .InfoContext (ctx , "outbound connection to " , destination )
154+ return o .client .DialContext (ctx , network , destination .String ())
155+ case N .NetworkUDP :
156+ o .logger .InfoContext (ctx , "outbound UoT packet connection to " , destination )
157+ return o .uotClient .DialContext (ctx , network , destination )
132158 }
133-
134- return conn , nil
159+ return nil , fmt .Errorf ("unsupported network: %s" , network )
135160}
136161
137- // ListenPacket is not supported by Samizdat (TCP-only protocol) .
162+ // ListenPacket creates a UoT packet connection through the Samizdat proxy .
138163func (o * Outbound ) ListenPacket (ctx context.Context , destination M.Socksaddr ) (net.PacketConn , error ) {
139- return nil , fmt .Errorf ("samizdat does not support UDP" )
164+ ctx , metadata := adapter .ExtendContext (ctx )
165+ metadata .Outbound = o .Tag ()
166+ metadata .Destination = destination
167+ o .logger .InfoContext (ctx , "outbound UoT packet connection to " , destination )
168+ return o .uotClient .ListenPacket (ctx , destination )
140169}
141170
142171// Network returns the supported network types.
143172func (o * Outbound ) Network () []string {
144- return []string {N .NetworkTCP }
173+ return []string {N .NetworkTCP , N . NetworkUDP }
145174}
146175
147176// Close shuts down the Samizdat client.
0 commit comments