// Package ssh builds and runs SSH port-forwarding commands. package ssh import ( "errors" "fmt" "git.scuroneko.dev/ScuroNeko/est/internal/config" ) func buildRemoteToLocalString(sourceIP, remoteIP string, sourcePort, remotePort uint16) string { if sourceIP == "" { sourceIP = "127.0.0.1" } if remoteIP == "" { remoteIP = "127.0.0.1" } return fmt.Sprintf("%s:%d:%s:%d", sourceIP, sourcePort, remoteIP, remotePort) } func buildLocalToRemoteString(sourceIP, remoteIP string, sourcePort, remotePort uint16) string { if sourceIP == "" { sourceIP = "127.0.0.1" } if remoteIP == "" { remoteIP = "127.0.0.1" } return fmt.Sprintf("%s:%d:%s:%d", remoteIP, remotePort, sourceIP, sourcePort) } var ( // ErrEmptyHost is returned when a tunnel has no SSH destination. ErrEmptyHost = errors.New("empty host") // ErrEmptyLocalPort is returned when a tunnel has no local port. ErrEmptyLocalPort = errors.New("empty local port") // ErrEmptyRemotePort is returned when a tunnel has no remote port. ErrEmptyRemotePort = errors.New("empty remote port") ) func validateArgs(entry config.ProxyEntry) error { if entry.Host == "" { return ErrEmptyHost } if entry.LocalPort == 0 { return ErrEmptyLocalPort } if entry.RemotePort == 0 { return ErrEmptyRemotePort } return nil } func prepareArgs(configPath string, entry config.ProxyEntry) ([]string, error) { if err := validateArgs(entry); err != nil { return nil, err } args := []string{"-N", "-T", "-o", "ExitOnForwardFailure=yes"} if configPath != "" { args = append(args, "-F", configPath) } else if configPath = getConfigOrEmpty(); configPath != "" { args = append(args, "-F", configPath) } if entry.IdentityFile != "" { args = append(args, "-i", entry.IdentityFile) } switch entry.Direction { case config.LocalToRemote: args = append(args, "-R", buildLocalToRemoteString(entry.LocalIP, entry.RemoteIP, entry.LocalPort, entry.RemotePort)) case config.RemoteToLocal: args = append(args, "-L", buildRemoteToLocalString(entry.LocalIP, entry.RemoteIP, entry.LocalPort, entry.RemotePort)) default: return nil, fmt.Errorf("invalid direction: %v; should be ltr on rtl", entry.Direction) } if entry.Host != "" { args = append(args, entry.Host) } return args, nil }