61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
// Package sshurl parses ssh:// URLs into components for use with the ssh binary.
|
|
package sshurl
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
)
|
|
|
|
// Parsed holds the components of an ssh:// URL.
|
|
type Parsed struct {
|
|
Host string // hostname or user@hostname
|
|
Port string // port number, or "" for default
|
|
Path string // absolute remote path (starts with /)
|
|
}
|
|
|
|
// SSHArgs returns the ssh command arguments for connecting to this host.
|
|
// If a port is specified, -p is included.
|
|
func (p Parsed) SSHArgs() []string {
|
|
args := []string{"-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", "-o", "ConnectTimeout=5"}
|
|
if p.Port != "" {
|
|
args = append(args, "-p", p.Port)
|
|
}
|
|
args = append(args, p.Host)
|
|
return args
|
|
}
|
|
|
|
// Parse parses an ssh:// URL into host, port, and path components.
|
|
// Accepts formats like ssh://host/path, ssh://user@host/path, ssh://host:2222/path.
|
|
func Parse(raw string) (Parsed, error) {
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
return Parsed{}, fmt.Errorf("invalid ssh URL %q: %w", raw, err)
|
|
}
|
|
if u.Scheme != "ssh" {
|
|
return Parsed{}, fmt.Errorf("expected ssh:// scheme, got %q", u.Scheme)
|
|
}
|
|
if u.Path == "" {
|
|
return Parsed{}, fmt.Errorf("ssh URL missing path: %s", raw)
|
|
}
|
|
|
|
host := u.Hostname()
|
|
if u.User != nil {
|
|
host = u.User.Username() + "@" + host
|
|
}
|
|
|
|
port := u.Port()
|
|
// net/url splits host:port correctly; verify with SplitHostPort for edge cases.
|
|
if port == "" && u.Host != host {
|
|
if _, p, err := net.SplitHostPort(u.Host); err == nil {
|
|
port = p
|
|
}
|
|
}
|
|
|
|
return Parsed{
|
|
Host: host,
|
|
Port: port,
|
|
Path: u.Path,
|
|
}, nil
|
|
}
|