Skip to content

Commit 6d6296a

Browse files
committed
dialers: use protect.RDialer over *protect.RDial
1 parent 8932d65 commit 6d6296a

3 files changed

Lines changed: 48 additions & 23 deletions

File tree

intra/dialers/retrier.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const maxRetryCount = 3
4848
// be typecastable to *net.TCPConn (see: xdial.DialTCP)
4949
// inheritance: go.dev/play/p/mMiQgXsPM7Y
5050
type retrier struct {
51-
dialer *protect.RDial
51+
dialers []protect.RDialer
5252
dialerOpts settings.DialerOpts
5353
raddr *net.TCPAddr
5454
laddr *net.TCPAddr // laddr may be nil; TCPAddr.IP may be nil.
@@ -79,8 +79,9 @@ type retrier struct {
7979
// and is cleared when the first byte is received.
8080
tee []byte
8181
// retryErr is set to the error from the last retry, if any.
82-
retryErr error
83-
retryCount uint8
82+
retryErr error
83+
retryCount uint8
84+
dialerCount int
8485
// Flag indicating when retry is finished or unnecessary.
8586
retryDoneCh chan struct{} // always unbuffered
8687
}
@@ -129,7 +130,7 @@ func calcTimeout(rtt time.Duration) time.Duration {
129130
// `addr` is the destination.
130131
func DialWithSplitRetry(d *protect.RDial, laddr, raddr *net.TCPAddr) (*retrier, error) {
131132
r := &retrier{
132-
dialer: d,
133+
dialers: []protect.RDialer{d},
133134
dialerOpts: settings.GetDialerOpts(),
134135
laddr: laddr, // may be nil
135136
raddr: raddr, // must not be nil
@@ -236,6 +237,7 @@ func (r *retrier) dialLocked() (c core.DuplexConn, err error) {
236237
begin := time.Now()
237238
c, err = r.doDialLocked(strat)
238239
rtt := time.Since(begin)
240+
r.dialerCount++
239241

240242
r.conn = c // c may be nil
241243
r.timeout = calcTimeout(rtt)
@@ -251,17 +253,19 @@ func (r *retrier) dialLocked() (c core.DuplexConn, err error) {
251253
func (r *retrier) doDialLocked(dialStrat int32) (_ core.DuplexConn, err error) {
252254
var conn *net.TCPConn
253255

256+
di := r.dialerCount % len(r.dialers)
257+
254258
// r.raddr may be nil or laddr.IP may be nil.
255259
switch dialStrat {
256260
case settings.SplitNever:
257-
return r.dialer.DialTCP(r.raddr.Network(), r.laddr, r.raddr)
261+
return protect.DialTCP(r.dialers[di], r.raddr.Network(), r.laddr, r.raddr)
258262
case settings.SplitDesync:
259-
return dialWithSplitAndDesync(r.dialer, r.laddr, r.raddr)
263+
return dialWithSplitAndDesync(r.dialers[di], r.laddr, r.raddr)
260264
case settings.SplitTCP, settings.SplitTCPOrTLS:
261265
fallthrough
262266
default:
263267
}
264-
conn, err = r.dialer.DialTCP(r.raddr.Network(), r.laddr, r.raddr)
268+
conn, err = protect.DialTCP(r.dialers[di], r.raddr.Network(), r.laddr, r.raddr)
265269
if err != nil || conn == nil {
266270
return nil, err
267271
}

intra/dialers/split_and_desync.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func exceedsTTL(cmsgs []unix.SocketControlMessage) bool {
104104

105105
// tracert dials a UDP conn to the target address over a port range basePort to basePort+DESYNC_MAX_TTL, with TTL
106106
// set to 2, 3, ..., DESYNC_MAX_TTL. It does not take ownership of the conn (which must be closed by the caller).
107-
func tracert(d *protect.RDial, ipp netip.AddrPort, basePort int) (*net.UDPConn, int, error) {
107+
func tracert(d protect.RDialer, ipp netip.AddrPort, basePort int) (*net.UDPConn, int, error) {
108108
udpAddr := net.UDPAddrFromAddrPort(ipp)
109109
udpAddr.Port = 1 // unset port
110110

@@ -119,7 +119,7 @@ func tracert(d *protect.RDial, ipp netip.AddrPort, basePort int) (*net.UDPConn,
119119
}
120120

121121
var udpFD int
122-
uc, err := d.AnnounceUDP(proto, ":0")
122+
uc, err := protect.AnnounceUDP(d, proto, ":0")
123123
if err != nil {
124124
log.E("desync: err announcing udp: %v", err)
125125
return uc, udpFD, err
@@ -179,7 +179,7 @@ func tracert(d *protect.RDial, ipp netip.AddrPort, basePort int) (*net.UDPConn,
179179
// If `payload` is smaller than the initial upstream segment, it launches the attack and splits.
180180
// This traceroute is not accurate, because of time limit (TCP handshake).
181181
// Note: The path the UDP packet took to reach the destination may differ from the path the TCP packet took.
182-
func desyncWithTraceroute(d *protect.RDial, local, remote netip.AddrPort) (*overwriteSplitter, error) {
182+
func desyncWithTraceroute(d protect.RDialer, local, remote netip.AddrPort) (*overwriteSplitter, error) {
183183
const maxport = 65535
184184
measureTTL := true
185185
isIPv6 := remote.Addr().Is6()
@@ -255,7 +255,7 @@ func desyncWithTraceroute(d *protect.RDial, local, remote netip.AddrPort) (*over
255255
return oc, nil
256256
}
257257

258-
func desyncWithFixedTtl(d *protect.RDial, local, remote netip.AddrPort, initialTTL int) (*overwriteSplitter, error) {
258+
func desyncWithFixedTtl(d protect.RDialer, local, remote netip.AddrPort, initialTTL int) (*overwriteSplitter, error) {
259259
var raddr *net.TCPAddr = net.TCPAddrFromAddrPort(remote)
260260
var laddr *net.TCPAddr // nil is valid
261261
if local.IsValid() {
@@ -271,7 +271,7 @@ func desyncWithFixedTtl(d *protect.RDial, local, remote netip.AddrPort, initialT
271271
proto = "tcp6"
272272
}
273273

274-
tcpConn, err := d.DialTCP(proto, laddr, raddr)
274+
tcpConn, err := protect.DialTCP(d, proto, laddr, raddr)
275275

276276
logeif(err)("desync: dialTCP: %s => %s, do desync? %t, ttl: %d",
277277
laddr, raddr, !avoidDesync, initialTTL)
@@ -298,7 +298,7 @@ func desyncWithFixedTtl(d *protect.RDial, local, remote netip.AddrPort, initialT
298298
// then returns a TCP connection that may launch TCB Desynchronization
299299
// and split the initial upstream segment.
300300
// ref: github.com/bol-van/zapret/blob/c369f11638/docs/readme.eng.md#dpi-desync-attack
301-
func dialWithSplitAndDesync(d *protect.RDial, laddr, raddr *net.TCPAddr) (*overwriteSplitter, error) {
301+
func dialWithSplitAndDesync(d protect.RDialer, laddr, raddr *net.TCPAddr) (*overwriteSplitter, error) {
302302
remote := raddr.AddrPort() // must not be invalid
303303
local := laddr.AddrPort() // can be invalid
304304

intra/protect/xdial.go

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Listener = net.Listener
3838
type DialFn func(network, addr string) (net.Conn, error)
3939

4040
type RDialer interface {
41+
ID() string
4142
// Dial creates a connection to the given address,
4243
// the resulting net.Conn must be a *net.TCPConn if
4344
// network is "tcp" or "tcp4" or "tcp6" and must be
@@ -83,18 +84,21 @@ var (
8384
errAccept = errors.New("cannot accept network")
8485
)
8586

86-
// Handle implements RDialer.
87-
func (d *RDial) Handle() uintptr {
88-
return core.Loc(d)
89-
}
90-
9187
func (d *RDial) context() context.Context {
9288
if d.ctx != nil {
9389
return d.ctx
9490
}
9591
return context.Background()
9692
}
9793

94+
// ID implements RDialer.
95+
func (d *RDial) ID() string {
96+
if d.owner != "" {
97+
return d.owner
98+
}
99+
return "xdial" // ownerless
100+
}
101+
98102
// Dial implements RDialer.
99103
func (d *RDial) Dial(network, addr string) (net.Conn, error) {
100104
return d.dialer.DialContext(d.context(), network, addr)
@@ -190,7 +194,8 @@ func (d *RDial) Announce(network, local string) (net.PacketConn, error) {
190194
case *net.UDPConn:
191195
return x, nil
192196
default:
193-
log.T("xdial: Announce (o: %s): addr(%s) failed; %T is not net.UDPConn; other errs: %v", d.owner, local, x, err)
197+
log.T("xdial: Announce (o: %s): addr(%s) failed; %T is not net.UDPConn; other errs: %v",
198+
d.owner, local, x, err)
194199
clos(pc)
195200
return nil, errNoUDPMux
196201
}
@@ -225,13 +230,17 @@ func (d *RDial) Probe(network, local string) (PacketConn, error) {
225230
// DialTCP creates a net.TCPConn to raddr.
226231
// Helper method for d.Dial("tcp", laddr.String(), raddr.String())
227232
func (d *RDial) DialTCP(network string, laddr, raddr *net.TCPAddr) (*net.TCPConn, error) {
233+
return DialTCP(d, network, laddr, raddr)
234+
}
235+
236+
func DialTCP(d RDialer, network string, laddr, raddr *net.TCPAddr) (*net.TCPConn, error) {
228237
if c, err := d.DialBind(network, laddr.String(), raddr.String()); err != nil {
229238
return nil, err
230239
} else if tc, ok := c.(*net.TCPConn); ok {
231240
return tc, nil
232241
} else {
233242
log.T("xdial: DialTCP: (%s) to %s => %s, %T is not %T (ok? %t); other errs: %v",
234-
d.owner, laddr, raddr, c, tc, ok, err)
243+
d.ID(), laddr, raddr, c, tc, ok, err)
235244
// some proxies like wgproxy, socks5 do not vend *net.TCPConn
236245
// also errors if retrier (core.DuplexConn) is looped back here
237246
clos(c)
@@ -242,13 +251,17 @@ func (d *RDial) DialTCP(network string, laddr, raddr *net.TCPAddr) (*net.TCPConn
242251
// DialUDP creates a net.UDPConn to raddr.
243252
// Helper method for d.Dial("udp", laddr.String(), raddr.String())
244253
func (d *RDial) DialUDP(network string, laddr, raddr *net.UDPAddr) (*net.UDPConn, error) {
254+
return DialUDP(d, network, laddr, raddr)
255+
}
256+
257+
func DialUDP(d RDialer, network string, laddr, raddr *net.UDPAddr) (*net.UDPConn, error) {
245258
if c, err := d.DialBind(network, laddr.String(), raddr.String()); err != nil {
246259
return nil, err
247260
} else if uc, ok := c.(*net.UDPConn); ok {
248261
return uc, nil
249262
} else {
250263
log.T("xdial: DialUDP: (%s) to %s => %s, %T is not %T (ok? %t); other errs: %v",
251-
d.owner, laddr, raddr, c, uc, ok, err)
264+
d.ID(), laddr, raddr, c, uc, ok, err)
252265
// some proxies like wgproxy, socks5 do not vend *net.UDPConn
253266
clos(c)
254267
return nil, errNoUDP
@@ -258,13 +271,17 @@ func (d *RDial) DialUDP(network string, laddr, raddr *net.UDPAddr) (*net.UDPConn
258271
// AnnounceUDP announces the local address. network must be "udp" or "udp4" or "udp6".
259272
// Helper method for d.Announce("udp", local)
260273
func (d *RDial) AnnounceUDP(network, local string) (*net.UDPConn, error) {
274+
return AnnounceUDP(d, network, local)
275+
}
276+
277+
func AnnounceUDP(d RDialer, network, local string) (*net.UDPConn, error) {
261278
if c, err := d.Announce(network, local); err != nil {
262279
return nil, err
263280
} else if uc, ok := c.(*net.UDPConn); ok {
264281
return uc, nil
265282
} else {
266283
log.T("xdial: AnnounceUDP: (%s) from %s, %T is not %T (ok? %t); other errs: %v",
267-
d.owner, local, c, uc, ok, err)
284+
d.ID(), local, c, uc, ok, err)
268285
clos(c)
269286
return nil, errNoUDPMux
270287
}
@@ -273,13 +290,17 @@ func (d *RDial) AnnounceUDP(network, local string) (*net.UDPConn, error) {
273290
// AcceptTCP creates a listener on the local address. network must be "tcp" or "tcp4" or "tcp6".
274291
// Helper method for d.Accept("tcp", local)
275292
func (d *RDial) AcceptTCP(network string, local string) (*net.TCPListener, error) {
293+
return AcceptTCP(d, network, local)
294+
}
295+
296+
func AcceptTCP(d RDialer, network string, local string) (*net.TCPListener, error) {
276297
if ln, err := d.Accept(network, local); err != nil {
277298
return nil, err
278299
} else if tl, ok := ln.(*net.TCPListener); ok {
279300
return tl, nil
280301
} else {
281302
log.T("xdial: AcceptTCP: (%s) from %s, %T is not %T (ok? %t); other errs: %v",
282-
d.owner, local, ln, tl, ok, err)
303+
d.ID(), local, ln, tl, ok, err)
283304
clos(ln)
284305
return nil, errNoTCPMux
285306
}

0 commit comments

Comments
 (0)