diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 0109d5b4..18084864 100644 --- a/proxy/socks/server.go +++ b/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)) diff --git a/proxy/socks/udpfilter.go b/proxy/socks/udpfilter.go new file mode 100644 index 00000000..25f8a416 --- /dev/null +++ b/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 +}