71 lines
1.8 KiB
Go
71 lines
1.8 KiB
Go
|
|
//go:build windows
|
||
|
|
|
||
|
|
package starssh
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"net"
|
||
|
|
"os"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/Microsoft/go-winio"
|
||
|
|
"golang.org/x/sys/windows"
|
||
|
|
)
|
||
|
|
|
||
|
|
const defaultWindowsSSHAgentPipe = `\\.\pipe\openssh-ssh-agent`
|
||
|
|
|
||
|
|
func dialSSHAgent(timeout time.Duration) (net.Conn, error) {
|
||
|
|
agentSock := strings.TrimSpace(os.Getenv("SSH_AUTH_SOCK"))
|
||
|
|
if agentSock != "" {
|
||
|
|
return dialWindowsSSHAgentEndpoint(agentSock, timeout)
|
||
|
|
}
|
||
|
|
return dialWindowsNamedPipe(defaultWindowsSSHAgentPipe, timeout, true)
|
||
|
|
}
|
||
|
|
|
||
|
|
func dialWindowsSSHAgentEndpoint(endpoint string, timeout time.Duration) (net.Conn, error) {
|
||
|
|
if pipePath, ok := normalizeWindowsSSHAgentPipe(endpoint); ok {
|
||
|
|
return dialWindowsNamedPipe(pipePath, timeout, false)
|
||
|
|
}
|
||
|
|
if timeout > 0 {
|
||
|
|
return net.DialTimeout("unix", endpoint, timeout)
|
||
|
|
}
|
||
|
|
return net.Dial("unix", endpoint)
|
||
|
|
}
|
||
|
|
|
||
|
|
func dialWindowsNamedPipe(path string, timeout time.Duration, unavailableOnNotFound bool) (net.Conn, error) {
|
||
|
|
ctx := context.Background()
|
||
|
|
cancel := func() {}
|
||
|
|
if timeout > 0 {
|
||
|
|
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||
|
|
}
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
conn, err := winio.DialPipeContext(ctx, path)
|
||
|
|
if err != nil && unavailableOnNotFound && isWindowsPipeUnavailable(err) {
|
||
|
|
return nil, errSSHAgentUnavailable
|
||
|
|
}
|
||
|
|
return conn, err
|
||
|
|
}
|
||
|
|
|
||
|
|
func normalizeWindowsSSHAgentPipe(endpoint string) (string, bool) {
|
||
|
|
trimmed := strings.TrimSpace(endpoint)
|
||
|
|
if trimmed == "" {
|
||
|
|
return "", false
|
||
|
|
}
|
||
|
|
|
||
|
|
normalized := trimmed
|
||
|
|
if strings.HasPrefix(normalized, "//./pipe/") {
|
||
|
|
normalized = `\\.\pipe\` + strings.TrimPrefix(normalized, "//./pipe/")
|
||
|
|
}
|
||
|
|
if strings.HasPrefix(normalized, `\\.\pipe\`) {
|
||
|
|
return normalized, true
|
||
|
|
}
|
||
|
|
return "", false
|
||
|
|
}
|
||
|
|
|
||
|
|
func isWindowsPipeUnavailable(err error) bool {
|
||
|
|
return errors.Is(err, windows.ERROR_FILE_NOT_FOUND) || errors.Is(err, windows.ERROR_PATH_NOT_FOUND)
|
||
|
|
}
|