mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-24 21:30:43 +00:00
- Add Bearer Token authentication, supporting tunnel access control via the --auth-bearer parameter - Refactor large modules into smaller, more focused components to improve code maintainability - Update dependency versions, including golang.org/x/crypto, golang.org/x/net, etc. - Add SilenceUsage and SilenceErrors configuration for all CLI commands - Modify connector configuration structure to support the new authentication method - Update recent change log in README with new feature descriptions BREAKING CHANGE: Authentication via Bearer Token is now supported, requiring the new --auth-bearer parameter
187 lines
5.4 KiB
Go
187 lines
5.4 KiB
Go
package tcp
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
json "github.com/goccy/go-json"
|
|
"go.uber.org/zap"
|
|
|
|
"drip/internal/server/tunnel"
|
|
"drip/internal/shared/protocol"
|
|
"drip/internal/shared/utils"
|
|
)
|
|
|
|
// RegistrationHandler handles tunnel registration logic.
|
|
type RegistrationHandler struct {
|
|
manager *tunnel.Manager
|
|
portAlloc *PortAllocator
|
|
groupManager *ConnectionGroupManager
|
|
domain string
|
|
tunnelDomain string
|
|
publicPort int
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewRegistrationHandler creates a new registration handler.
|
|
func NewRegistrationHandler(
|
|
manager *tunnel.Manager,
|
|
portAlloc *PortAllocator,
|
|
groupManager *ConnectionGroupManager,
|
|
domain, tunnelDomain string,
|
|
publicPort int,
|
|
logger *zap.Logger,
|
|
) *RegistrationHandler {
|
|
return &RegistrationHandler{
|
|
manager: manager,
|
|
portAlloc: portAlloc,
|
|
groupManager: groupManager,
|
|
domain: domain,
|
|
tunnelDomain: tunnelDomain,
|
|
publicPort: publicPort,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// RegistrationRequest contains all information needed for registration.
|
|
type RegistrationRequest struct {
|
|
TunnelType protocol.TunnelType
|
|
CustomSubdomain string
|
|
Token string
|
|
ConnectionType string
|
|
PoolCapabilities *protocol.PoolCapabilities
|
|
IPAccess *protocol.IPAccessControl
|
|
ProxyAuth *protocol.ProxyAuth
|
|
LocalPort int
|
|
}
|
|
|
|
// RegistrationResult contains the result of a registration attempt.
|
|
type RegistrationResult struct {
|
|
Subdomain string
|
|
Port int
|
|
TunnelURL string
|
|
TunnelID string
|
|
SupportsDataConn bool
|
|
RecommendedConns int
|
|
TunnelConn *tunnel.Connection
|
|
}
|
|
|
|
// Register handles the tunnel registration process.
|
|
func (rh *RegistrationHandler) Register(req *RegistrationRequest) (*RegistrationResult, error) {
|
|
// Allocate port for TCP tunnels
|
|
port := 0
|
|
if req.TunnelType == protocol.TunnelTypeTCP {
|
|
if rh.portAlloc == nil {
|
|
return nil, fmt.Errorf("port allocator not configured")
|
|
}
|
|
|
|
if requestedPort, ok := parseTCPSubdomainPort(req.CustomSubdomain); ok {
|
|
allocatedPort, err := rh.portAlloc.AllocateSpecific(requestedPort)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to allocate requested port %d: %w", requestedPort, err)
|
|
}
|
|
port = allocatedPort
|
|
} else {
|
|
allocatedPort, err := rh.portAlloc.Allocate()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to allocate port: %w", err)
|
|
}
|
|
port = allocatedPort
|
|
|
|
if req.CustomSubdomain == "" {
|
|
req.CustomSubdomain = fmt.Sprintf("tcp-%d", port)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register with tunnel manager
|
|
subdomain, err := rh.manager.Register(nil, req.CustomSubdomain)
|
|
if err != nil {
|
|
if port > 0 && rh.portAlloc != nil {
|
|
rh.portAlloc.Release(port)
|
|
}
|
|
return nil, fmt.Errorf("tunnel registration failed: %w", err)
|
|
}
|
|
|
|
// Get tunnel connection
|
|
tunnelConn, ok := rh.manager.Get(subdomain)
|
|
if !ok {
|
|
return nil, fmt.Errorf("failed to get registered tunnel")
|
|
}
|
|
|
|
// Configure tunnel
|
|
tunnelConn.SetTunnelType(req.TunnelType)
|
|
|
|
if req.IPAccess != nil && (len(req.IPAccess.AllowIPs) > 0 || len(req.IPAccess.DenyIPs) > 0) {
|
|
tunnelConn.SetIPAccessControl(req.IPAccess.AllowIPs, req.IPAccess.DenyIPs)
|
|
rh.logger.Info("IP access control configured",
|
|
zap.String("subdomain", subdomain),
|
|
zap.Strings("allow_ips", req.IPAccess.AllowIPs),
|
|
zap.Strings("deny_ips", req.IPAccess.DenyIPs),
|
|
)
|
|
}
|
|
|
|
if req.ProxyAuth != nil && req.ProxyAuth.Enabled {
|
|
tunnelConn.SetProxyAuth(req.ProxyAuth)
|
|
rh.logger.Info("Proxy authentication configured",
|
|
zap.String("subdomain", subdomain),
|
|
)
|
|
}
|
|
|
|
// Build tunnel URL
|
|
urlBuilder := utils.NewTunnelURLBuilder(rh.tunnelDomain, rh.publicPort)
|
|
tunnelURL := urlBuilder.BuildURL(subdomain, req.TunnelType, port)
|
|
|
|
// Handle connection groups for multi-connection support
|
|
var tunnelID string
|
|
var supportsDataConn bool
|
|
recommendedConns := 0
|
|
|
|
if req.PoolCapabilities != nil && req.ConnectionType == "primary" && rh.groupManager != nil {
|
|
// This will be handled by the caller since it needs the connection instance
|
|
supportsDataConn = true
|
|
recommendedConns = 4
|
|
}
|
|
|
|
rh.logger.Info("Tunnel registered",
|
|
zap.String("subdomain", subdomain),
|
|
zap.String("tunnel_type", string(req.TunnelType)),
|
|
zap.Int("local_port", req.LocalPort),
|
|
zap.Int("remote_port", port),
|
|
)
|
|
|
|
return &RegistrationResult{
|
|
Subdomain: subdomain,
|
|
Port: port,
|
|
TunnelURL: tunnelURL,
|
|
TunnelID: tunnelID,
|
|
SupportsDataConn: supportsDataConn,
|
|
RecommendedConns: recommendedConns,
|
|
TunnelConn: tunnelConn,
|
|
}, nil
|
|
}
|
|
|
|
// BuildRegistrationResponse creates a protocol registration response.
|
|
func (rh *RegistrationHandler) BuildRegistrationResponse(result *RegistrationResult) (*protocol.RegisterResponse, error) {
|
|
resp := &protocol.RegisterResponse{
|
|
Subdomain: result.Subdomain,
|
|
Port: result.Port,
|
|
URL: result.TunnelURL,
|
|
Message: "Tunnel registered successfully",
|
|
TunnelID: result.TunnelID,
|
|
SupportsDataConn: result.SupportsDataConn,
|
|
RecommendedConns: result.RecommendedConns,
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// SendRegistrationResponse sends the registration response frame.
|
|
func (rh *RegistrationHandler) SendRegistrationResponse(conn interface{ Write([]byte) (int, error) }, resp *protocol.RegisterResponse) error {
|
|
respData, err := json.Marshal(resp)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal registration response: %w", err)
|
|
}
|
|
|
|
ackFrame := protocol.NewFrame(protocol.FrameTypeRegisterAck, respData)
|
|
return protocol.WriteFrame(conn, ackFrame)
|
|
}
|