Gitfed
bastien-mrq/sssh / internal / sshconf / sshconf.go
// Package sshconf lit ~/.ssh/config (jamais modifié) pour proposer
// d'importer les hosts existants dans sssh.
package sshconf

import (
	"os"
	"path/filepath"
	"strconv"
	"strings"

	ssh_config "github.com/kevinburke/ssh_config"

	"git.neuromancer.ovh/bastien-mrq/sssh/internal/host"
)

// Hosts rend les alias concrets (sans jokers) déclarés dans ~/.ssh/config.
// Toute erreur de lecture ou de parsing donne une liste vide : ce fichier
// n'appartient pas à sssh, on ne bloque jamais dessus.
func Hosts() []host.Host {
	home, err := os.UserHomeDir()
	if err != nil {
		return nil
	}
	f, err := os.Open(filepath.Join(home, ".ssh", "config"))
	if err != nil {
		return nil
	}
	defer f.Close()

	cfg, err := ssh_config.Decode(f)
	if err != nil {
		return nil
	}

	var hosts []host.Host
	for _, block := range cfg.Hosts {
		kv := map[string]string{}
		for _, node := range block.Nodes {
			if n, ok := node.(*ssh_config.KV); ok {
				kv[strings.ToLower(n.Key)] = n.Value
			}
		}
		for _, p := range block.Patterns {
			name := p.String()
			if strings.ContainsAny(name, "*?!") {
				continue
			}
			h := host.Host{Name: name}
			h.Host = kv["hostname"]
			if h.Host == "" {
				h.Host = name
			}
			h.User = kv["user"]
			if v := kv["port"]; v != "" {
				h.Port, _ = strconv.Atoi(v)
			}
			h.Identity = kv["identityfile"]
			h.Jump = kv["proxyjump"]
			hosts = append(hosts, h)
		}
	}
	return hosts
}