Fix port detection and manual forwarding issues
Major improvements to error handling and debugging: - Fix program quitting on manual port forwarding errors - Add comprehensive debug logging for SSH connections - Improve error handling to show messages instead of quitting - Add StateStartingForward for better user feedback - Enhanced SSH client creation with default key loading - Add --test-connect mode for debugging specific hosts - Better timeout handling and connection diagnostics The application now gracefully handles connection failures and provides helpful error messages instead of crashing. Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -154,24 +155,32 @@ func (pf *PortForwarder) handleConnection(localConn net.Conn) {
|
||||
// StartPortForwarding starts port forwarding for a specific port
|
||||
func StartPortForwarding(host SSHHost, remotePort int) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Starting port forwarding for %s:%d\n", host.Name, remotePort)
|
||||
|
||||
// Find an available local port
|
||||
localPort, err := findAvailablePort()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Failed to find available port: %v\n", err)
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to find available local port: %w", err)}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Debug: Found available local port: %d\n", localPort)
|
||||
|
||||
// Create SSH client
|
||||
client, err := createSSHClient(host)
|
||||
if err != nil {
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to create SSH client: %w", err)}
|
||||
fmt.Fprintf(os.Stderr, "Debug: Failed to create SSH client: %v\n", err)
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to connect to %s: %w", host.Name, err)}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Debug: SSH client created successfully\n")
|
||||
|
||||
// Create and start port forwarder
|
||||
forwarder := NewPortForwarder(client, localPort, remotePort)
|
||||
if err := forwarder.Start(); err != nil {
|
||||
client.Close()
|
||||
fmt.Fprintf(os.Stderr, "Debug: Failed to start port forwarder: %v\n", err)
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to start port forwarding: %w", err)}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Debug: Port forwarder started successfully\n")
|
||||
|
||||
return ForwardingStartedMsg{
|
||||
LocalPort: localPort,
|
||||
@@ -183,33 +192,43 @@ func StartPortForwarding(host SSHHost, remotePort int) tea.Cmd {
|
||||
// StartManualPortForwarding starts port forwarding for a manually entered port
|
||||
func StartManualPortForwarding(host SSHHost, portStr string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Manual port forwarding requested for %s:%s\n", host.Name, portStr)
|
||||
|
||||
remotePort, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Invalid port number: %s\n", portStr)
|
||||
return ErrorMsg{Error: fmt.Errorf("invalid port number: %s", portStr)}
|
||||
}
|
||||
|
||||
if remotePort <= 0 || remotePort > 65535 {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Port number out of range: %d\n", remotePort)
|
||||
return ErrorMsg{Error: fmt.Errorf("port number must be between 1 and 65535")}
|
||||
}
|
||||
|
||||
// Find an available local port
|
||||
localPort, err := findAvailablePort()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Failed to find available port: %v\n", err)
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to find available local port: %w", err)}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Debug: Found available local port: %d\n", localPort)
|
||||
|
||||
// Create SSH client
|
||||
client, err := createSSHClient(host)
|
||||
if err != nil {
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to create SSH client: %w", err)}
|
||||
fmt.Fprintf(os.Stderr, "Debug: Failed to create SSH client: %v\n", err)
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to connect to %s: %w", host.Name, err)}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Debug: SSH client created successfully\n")
|
||||
|
||||
// Create and start port forwarder
|
||||
forwarder := NewPortForwarder(client, localPort, remotePort)
|
||||
if err := forwarder.Start(); err != nil {
|
||||
client.Close()
|
||||
fmt.Fprintf(os.Stderr, "Debug: Failed to start port forwarder: %v\n", err)
|
||||
return ErrorMsg{Error: fmt.Errorf("failed to start port forwarding: %w", err)}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Debug: Port forwarder started successfully\n")
|
||||
|
||||
return ForwardingStartedMsg{
|
||||
LocalPort: localPort,
|
||||
@@ -224,29 +243,58 @@ func createSSHClient(host SSHHost) (*ssh.Client, error) {
|
||||
User: host.User,
|
||||
Auth: []ssh.AuthMethod{},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // In production, use proper host key verification
|
||||
Timeout: 5 * time.Second, // Shorter timeout
|
||||
Timeout: 10 * time.Second, // Longer timeout for better reliability
|
||||
}
|
||||
|
||||
// Add key-based authentication if identity file is specified
|
||||
if host.Identity != "" {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Trying identity file: %s\n", host.Identity)
|
||||
key, err := loadPrivateKey(host.Identity)
|
||||
if err == nil {
|
||||
config.Auth = append(config.Auth, ssh.PublicKeys(key))
|
||||
fmt.Fprintf(os.Stderr, "Debug: Added key-based auth\n")
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Debug: Failed to load identity file: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add SSH agent authentication
|
||||
if agentAuth, err := sshAgentAuth(); err == nil {
|
||||
config.Auth = append(config.Auth, agentAuth)
|
||||
fmt.Fprintf(os.Stderr, "Debug: Added SSH agent auth\n")
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Debug: SSH agent not available: %v\n", err)
|
||||
}
|
||||
|
||||
// Try to load default SSH keys if no specific identity is set
|
||||
if host.Identity == "" {
|
||||
defaultKeys := []string{"id_rsa", "id_ecdsa", "id_ed25519"}
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
for _, keyName := range defaultKeys {
|
||||
keyPath := filepath.Join(homeDir, ".ssh", keyName)
|
||||
if key, err := loadPrivateKey(keyPath); err == nil {
|
||||
config.Auth = append(config.Auth, ssh.PublicKeys(key))
|
||||
fmt.Fprintf(os.Stderr, "Debug: Added default key: %s\n", keyName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no auth methods available, provide helpful error
|
||||
if len(config.Auth) == 0 {
|
||||
return nil, fmt.Errorf("no SSH authentication methods available - please set up SSH keys or SSH agent")
|
||||
}
|
||||
|
||||
// Connect to the remote host
|
||||
addr := net.JoinHostPort(host.Hostname, host.Port)
|
||||
fmt.Fprintf(os.Stderr, "Debug: Connecting to %s\n", addr)
|
||||
client, err := ssh.Dial("tcp", addr, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to %s: %w", host.Name, err)
|
||||
return nil, fmt.Errorf("failed to connect to %s (%s): %w", host.Name, addr, err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Debug: Successfully connected to %s\n", host.Name)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user