//go:build windows package sshprovider import ( "net" "regexp" "strings" "github.com/Microsoft/go-winio" "github.com/pkg/errors" "golang.org/x/sys/windows" ) // Returns the Windows OpenSSH agent named pipe path, but // only if the agent is running. Returns an error otherwise. func getFallbackAgentPath() (string, error) { // Windows OpenSSH agent uses a named pipe rather // than a UNIX socket. These pipes do not play nice // with os.Stat (which tries to open its target), so // use a FindFirstFile syscall to check for existence. var fd windows.Win32finddata path := `\\.\pipe\openssh-ssh-agent` pathPtr, _ := windows.UTF16PtrFromString(path) handle, err := windows.FindFirstFile(pathPtr, &fd) if err != nil { msg := "Windows OpenSSH agent not available at %s." + " Enable the SSH agent service or set SSH_AUTH_SOCK." return "", errors.Errorf(msg, path) } _ = windows.CloseHandle(handle) return path, nil } // Returns true if the path references a named pipe. func isWindowsPipePath(path string) bool { // If path matches \\*\pipe\* then it references a named pipe // and requires winio.DialPipe() rather than DialTimeout("unix"). // Slashes and backslashes may be used interchangeably in the path. // Path separators may consist of multiple consecutive (back)slashes. pipePattern := strings.ReplaceAll("^[/]{2}[^/]+[/]+pipe[/]+", "/", `\\/`) ok, _ := regexp.MatchString(pipePattern, path) return ok } func getWindowsPipeDialer(path string) *socketDialer { if isWindowsPipePath(path) { return &socketDialer{path: path, dialer: windowsPipeDialer} } return nil } func windowsPipeDialer(path string) (net.Conn, error) { return winio.DialPipe(path, nil) }