From 0eccf52399f6bf22a46b72ea331d2452b9c6763c Mon Sep 17 00:00:00 2001 From: database64128 Date: Wed, 16 Dec 2020 04:23:40 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=A6=20Fix=20socks=20client=20UDP=20out?= =?UTF-8?q?bound's=20wrong=20destination=20(#522)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - When you connect to a non-localhost socks5 server, in the response of a UdpAssociate from a socks5 server, the remote address may be `::` or `0.0.0.0`. The previous behavior is to connect to the remote address in the response, which obviously fails. - This commit changes the behavior to dial to the outbound server's address when the remote address in the response is `::` or `0.0.0.0`. - Rename `cmdUDPPort` to `cmdUDPAssociate` for clarity. --- proxy/socks/client.go | 12 +++++++++++- proxy/socks/protocol.go | 6 +++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/proxy/socks/client.go b/proxy/socks/client.go index 836caf205..feb6e4ae7 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -53,14 +53,19 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter if outbound == nil || !outbound.Target.IsValid() { return newError("target not specified.") } + // Destination of the inner request. destination := outbound.Target + // Outbound server. var server *protocol.ServerSpec + // Outbound server's destination. + var dest net.Destination + // Connection to the outbound server. var conn internet.Connection if err := retry.ExponentialBackoff(5, 100).On(func() error { server = c.serverPicker.PickServer() - dest := server.Destination() + dest = server.Destination() rawConn, err := dialer.Dial(ctx, dest) if err != nil { return err @@ -103,6 +108,11 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter if err != nil { return newError("failed to establish connection to server").AtWarning().Base(err) } + if udpRequest != nil { + if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 { + udpRequest.Address = dest.Address + } + } if err := conn.SetDeadline(time.Time{}); err != nil { newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx)) diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 47ad3f30f..9fb268c5d 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -18,7 +18,7 @@ const ( cmdTCPConnect = 0x01 cmdTCPBind = 0x02 - cmdUDPPort = 0x03 + cmdUDPAssociate = 0x03 cmdTorResolve = 0xF0 cmdTorResolvePTR = 0xF1 @@ -164,7 +164,7 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri case cmdTCPConnect, cmdTorResolve, cmdTorResolvePTR: // We don't have a solution for Tor case now. Simply treat it as connect command. request.Command = protocol.RequestCommandTCP - case cmdUDPPort: + case cmdUDPAssociate: if !s.config.UdpEnabled { writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) return nil, newError("UDP is not enabled.") @@ -448,7 +448,7 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i command := byte(cmdTCPConnect) if request.Command == protocol.RequestCommandUDP { - command = byte(cmdUDPPort) + command = byte(cmdUDPAssociate) } common.Must2(b.Write([]byte{socks5Version, command, 0x00 /* reserved */})) if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {