Explorar o código

Add UDPFilter to Socks5 server when `auth == password` (#3371)

Co-authored-by: RPRX <[email protected]>
风扇滑翔翼 %!s(int64=2) %!d(string=hai) anos
pai
achega
9ee9a0634e
Modificáronse 2 ficheiros con 42 adicións e 0 borrados
  1. 11 0
      proxy/socks/server.go
  2. 31 0
      proxy/socks/udpfilter.go

+ 11 - 0
proxy/socks/server.go

@@ -27,6 +27,7 @@ type Server struct {
 	config        *ServerConfig
 	policyManager policy.Manager
 	cone          bool
+	udpFilter     *UDPFilter
 }
 
 // NewServer creates a new Server object.
@@ -37,6 +38,9 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
 		policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
 		cone:          ctx.Value("cone").(bool),
 	}
+	if config.AuthType == AuthType_PASSWORD {
+		s.udpFilter = new(UDPFilter) // We only use this when auth is enabled
+	}
 	return s, nil
 }
 
@@ -135,6 +139,9 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
 	}
 
 	if request.Command == protocol.RequestCommandUDP {
+		if s.udpFilter != nil {
+			s.udpFilter.Add(conn.RemoteAddr())
+		}
 		return s.handleUDP(conn)
 	}
 
@@ -193,6 +200,10 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
 }
 
 func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error {
+	if s.udpFilter != nil && !s.udpFilter.Check(conn.RemoteAddr()) {
+		newError("Unauthorized UDP access from ", conn.RemoteAddr().String()).AtDebug().WriteToLog(session.ExportIDToError(ctx))
+		return nil
+	}
 	udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
 		payload := packet.Payload
 		newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx))

+ 31 - 0
proxy/socks/udpfilter.go

@@ -0,0 +1,31 @@
+package socks
+
+import (
+	"net"
+	"sync"
+)
+
+/*
+In the sock implementation of * ray, UDP authentication is flawed and can be bypassed.
+Tracking a UDP connection may be a bit troublesome.
+Here is a simple solution.
+We creat a filter, add remote IP to the pool when it try to establish a UDP connection with auth.
+And drop UDP packets from unauthorized IP.
+After discussion, we believe it is not necessary to add a timeout mechanism to this filter.
+*/
+
+type UDPFilter struct {
+	ips sync.Map
+}
+
+func (f *UDPFilter) Add(addr net.Addr) bool {
+	ip, _, _ := net.SplitHostPort(addr.String())
+	f.ips.Store(ip, true)
+	return true
+}
+
+func (f *UDPFilter) Check(addr net.Addr) bool {
+	ip, _, _ := net.SplitHostPort(addr.String())
+	_, ok := f.ips.Load(ip)
+	return ok
+}