//go:build linux || freebsd // +build linux freebsd package auth import ( "bufio" "os" "path/filepath" "strings" ) // NetrcEntry holds credentials for a single machine entry in .netrc. type NetrcEntry struct { Machine string Login string Password string } // LoadNetrc reads ~/.netrc and returns entries keyed by machine name. func LoadNetrc() (map[string]*NetrcEntry, error) { home, err := os.UserHomeDir() if err != nil { return nil, nil } path := filepath.Join(home, ".netrc") file, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, err } defer file.Close() entries := make(map[string]*NetrcEntry) var current *NetrcEntry scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) // Skip comments and empty lines if line == "" || strings.HasPrefix(line, "#") { continue } fields := strings.Fields(line) // "default" is also valid but we treat it as a machine if len(fields) >= 2 && (fields[0] == "machine" || fields[0] == "default") { if current != nil && current.Machine != "" { entries[current.Machine] = current } name := fields[1] if fields[0] == "default" { name = "default" } current = &NetrcEntry{Machine: name} // Rest of line may have login/password parseNetrcLine(fields[2:], current) } else if current != nil { parseNetrcLine(fields, current) } } if current != nil && current.Machine != "" { entries[current.Machine] = current } return entries, scanner.Err() } func parseNetrcLine(fields []string, entry *NetrcEntry) { for i := 0; i < len(fields); i++ { switch fields[i] { case "login": if i+1 < len(fields) { entry.Login = fields[i+1] i++ } case "password": if i+1 < len(fields) { entry.Password = fields[i+1] i++ } } } } // LookupNetrc returns credentials from .netrc for a given hostname. func LookupNetrc(host string) (login, password string, found bool) { entries, err := LoadNetrc() if err != nil || entries == nil { return "", "", false } // Exact match first if e, ok := entries[host]; ok { return e.Login, e.Password, true } // Default entry if e, ok := entries["default"]; ok { return e.Login, e.Password, true } return "", "", false }