Skip to content

Commit 2676d11

Browse files
committed
fix false success from SOCKS server when Dispatch() fails
Postpone SOCKS server ok reply to a TCP Connect command until Dispatch() actually creates an upstream Link. Previously it was sent immediately inside Handshake(). UDP works as before.
1 parent 9844b50 commit 2676d11

File tree

2 files changed

+75
-10
lines changed

2 files changed

+75
-10
lines changed

proxy/socks/protocol.go

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
authNoMatchingMethod = 0xFF
3131

3232
statusSuccess = 0x00
33+
statusConnRefused = 0x05
3334
statusCmdNotSupport = 0x07
3435
)
3536

@@ -40,10 +41,12 @@ var addrParser = protocol.NewAddressParser(
4041
)
4142

4243
type ServerSession struct {
43-
config *ServerConfig
44-
address net.Address
45-
port net.Port
46-
clientAddress net.Address
44+
config *ServerConfig
45+
address net.Address
46+
port net.Port
47+
clientAddress net.Address
48+
deferLastReply bool
49+
flushLastReply func(bool) error
4750
}
4851

4952
func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
@@ -85,7 +88,13 @@ func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer)
8588
Port: port,
8689
Version: socks4Version,
8790
}
88-
if err := writeSocks4Response(writer, socks4RequestGranted, net.AnyIP, net.Port(0)); err != nil {
91+
if err := s.setupLastReply(func(ok bool) error {
92+
if ok {
93+
return writeSocks4Response(writer, socks4RequestGranted, net.AnyIP, net.Port(0))
94+
} else {
95+
return writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0))
96+
}
97+
}); err != nil {
8998
return nil, err
9099
}
91100
return request, nil
@@ -203,13 +212,37 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
203212
responseAddress = s.address
204213
}
205214
}
206-
if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
215+
if err := s.setupLastReply(func(ok bool) error {
216+
if ok {
217+
return writeSocks5Response(writer, statusSuccess, responseAddress, responsePort)
218+
} else {
219+
return writeSocks5Response(writer, statusConnRefused, net.AnyIP, net.Port(0))
220+
}
221+
}); err != nil {
207222
return nil, err
208223
}
209224

210225
return request, nil
211226
}
212227

228+
// Sets the callback and calls or postpones it based on the boolean field
229+
func (s *ServerSession) setupLastReply(callback func(bool) error) error {
230+
noOpCallback := func(bool) error {
231+
return nil
232+
}
233+
234+
// set the field even if we call it now because it will be called again
235+
s.flushLastReply = func(ok bool) error {
236+
s.flushLastReply = noOpCallback
237+
return callback(ok)
238+
}
239+
240+
if s.deferLastReply {
241+
return nil
242+
}
243+
return s.flushLastReply(true)
244+
}
245+
213246
// Handshake performs a Socks4/4a/5 handshake.
214247
func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
215248
buffer := buf.StackNew()

proxy/socks/server.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/v2fly/v2ray-core/v5/features"
2020
"github.com/v2fly/v2ray-core/v5/features/policy"
2121
"github.com/v2fly/v2ray-core/v5/features/routing"
22+
"github.com/v2fly/v2ray-core/v5/transport"
2223
"github.com/v2fly/v2ray-core/v5/transport/internet"
2324
"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
2425
)
@@ -90,10 +91,11 @@ func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispa
9091
}
9192

9293
svrSession := &ServerSession{
93-
config: s.config,
94-
address: inbound.Gateway.Address,
95-
port: inbound.Gateway.Port,
96-
clientAddress: inbound.Source.Address,
94+
config: s.config,
95+
address: inbound.Gateway.Address,
96+
port: inbound.Gateway.Port,
97+
clientAddress: inbound.Source.Address,
98+
deferLastReply: true,
9799
}
98100

99101
reader := &buf.BufferedReader{Reader: buf.NewReader(conn)}
@@ -129,16 +131,46 @@ func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispa
129131
})
130132
}
131133

134+
dispatcher = &handshakeFinalizingDispatcher{Feature: dispatcher, delegate: dispatcher, serverSession: svrSession}
132135
return s.transport(ctx, reader, conn, dest, dispatcher)
133136
}
134137

138+
svrSession.flushLastReply(true)
135139
if request.Command == protocol.RequestCommandUDP {
136140
return s.handleUDP(conn)
137141
}
138142

139143
return nil
140144
}
141145

146+
// Wrapper to send final SOCKS reply only after Dispatch() returns
147+
type handshakeFinalizingDispatcher struct {
148+
features.Feature // do not inherit Dispatch() in case its signature changes
149+
delegate routing.Dispatcher
150+
serverSession *ServerSession
151+
}
152+
153+
func (d *handshakeFinalizingDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) {
154+
link, err := d.delegate.Dispatch(ctx, dest)
155+
if err == nil {
156+
closeInDefer := true
157+
defer func() {
158+
if closeInDefer {
159+
common.Interrupt(link.Reader)
160+
common.Interrupt(link.Writer)
161+
}
162+
}()
163+
err = d.serverSession.flushLastReply(true)
164+
if err != nil {
165+
return nil, err
166+
}
167+
closeInDefer = false
168+
} else {
169+
d.serverSession.flushLastReply(false)
170+
}
171+
return link, err
172+
}
173+
142174
func (*Server) handleUDP(c io.Reader) error {
143175
// The TCP connection closes after this method returns. We need to wait until
144176
// the client closes it.

0 commit comments

Comments
 (0)