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:
Ona
2025-09-26 00:24:04 +00:00
parent 70307c7cba
commit bde1529248
5 changed files with 168 additions and 8 deletions

View File

@@ -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
}