things-go/go-socks5

[FEATURE REQUEST] How to Implement Connection Count Statistics ?

gopkg-dev opened this issue · 1 comments

Description:
I have added functionality to track active connections in the handleConnect method of the go-socks5 package. I would like to request a code review and suggestions for improving the implementation.

go-socks5/handle.go

Lines 108 to 150 in ea3e2a0

func (sf *Server) handleConnect(ctx context.Context, writer io.Writer, request *Request) error {
// Attempt to connect
dial := sf.dial
if dial == nil {
dial = func(ctx context.Context, net_, addr string) (net.Conn, error) {
return net.Dial(net_, addr)
}
}
target, err := dial(ctx, "tcp", request.DestAddr.String())
if err != nil {
msg := err.Error()
resp := statute.RepHostUnreachable
if strings.Contains(msg, "refused") {
resp = statute.RepConnectionRefused
} else if strings.Contains(msg, "network is unreachable") {
resp = statute.RepNetworkUnreachable
}
if err := SendReply(writer, resp, nil); err != nil {
return fmt.Errorf("failed to send reply, %v", err)
}
return fmt.Errorf("connect to %v failed, %v", request.RawDestAddr, err)
}
defer target.Close()
// Send success
if err := SendReply(writer, statute.RepSuccess, target.LocalAddr()); err != nil {
return fmt.Errorf("failed to send reply, %v", err)
}
// Start proxying
errCh := make(chan error, 2)
sf.goFunc(func() { errCh <- sf.Proxy(target, request.Reader) })
sf.goFunc(func() { errCh <- sf.Proxy(writer, target) })
// Wait
for i := 0; i < 2; i++ {
e := <-errCh
if e != nil {
// return from this function closes target (and conn).
return e
}
}
return nil
}

type Server struct {
	// ...
	ActiveConnections int32 // Number of active connections
	// ...
}

// GetActiveConnections returns the current count of active connections
func (sf *Server) GetActiveConnections() int32 {
	return atomic.LoadInt32(&sf.ActiveConnections)
}

// handleConnect is used to handle a connect command
func (sf *Server) handleConnect(ctx context.Context, writer io.Writer, request *Request) error {
	// Attempt to connect
	dial := sf.dial
	if dial == nil {
		dial = func(ctx context.Context, net_, addr string) (net.Conn, error) {
			return net.Dial(net_, addr)
		}
	}
	target, err := dial(ctx, "tcp", request.DestAddr.String())
	if err != nil {
		msg := err.Error()
		resp := statute.RepHostUnreachable
		if strings.Contains(msg, "refused") {
			resp = statute.RepConnectionRefused
		} else if strings.Contains(msg, "network is unreachable") {
			resp = statute.RepNetworkUnreachable
		}
		if err := SendReply(writer, resp, nil); err != nil {
			return fmt.Errorf("failed to send reply, %v", err)
		}
		return fmt.Errorf("connect to %v failed, %v", request.RawDestAddr, err)
	}

	// Increment the connection count when establishing a connection
	atomic.AddInt32(&sf.ActiveConnections, 1)
	defer func() {
		target.Close() //nolint: errcheck
		atomic.AddInt32(&sf.ActiveConnections, -1)
	}()

	// Send success
	if err := SendReply(writer, statute.RepSuccess, target.LocalAddr()); err != nil {
		return fmt.Errorf("failed to send reply, %v", err)
	}

	// Start proxying
	errCh := make(chan error, 2)
	sf.goFunc(func() { errCh <- sf.Proxy(target, request.Reader) })
	sf.goFunc(func() { errCh <- sf.Proxy(writer, target) })
	// Wait
	for i := 0; i < 2; i++ {
		e := <-errCh
		if e != nil {
			// return from this function closes target (and conn).
			return e
		}
	}
	return nil
}

@gopkg-dev suggest add prestart and poststop function in handleRequest,. custom for user defined it's owned feature.