diff --git a/common.go b/common.go new file mode 100644 index 0000000..ae4b7ff --- /dev/null +++ b/common.go @@ -0,0 +1,47 @@ +package hostname + +import ( + "errors" + "fmt" + "net" + "strings" +) + +var ErrorEmptyDomainName error = errors.New("domain name is empty") +var ErrorDomainNameMisconfigured error = errors.New("your OS domain name is not configured correctly") + +// returns 'hostname', 'domainname' +func Split(s string) (string, string) { + parts := strings.Fields(s) + if len(parts) == 0 { + return "", "" + } + if len(parts) == 1 { + return parts[0], "" + } + return parts[0], strings.Join(parts[1:], ".") +} + +// checks to make sure there is a domainname +func ValidDomainname(s string) error { + _, domainname := Split(s) + if domainname == "" { + return ErrorEmptyDomainName + } + return nil +} + +func ReverseLookupFQDN(hostname string) (string, error) { + addrs, err := net.LookupIP(hostname) + if err != nil || len(addrs) == 0 { + return hostname, fmt.Errorf("failed to lookup IP for hostname %s: %w", hostname, err) + } + + // Try reverse lookup on the first IP address + names, err := net.LookupAddr(addrs[0].String()) + if err != nil || len(names) == 0 { + return hostname, fmt.Errorf("reverse DNS lookup failed for IP %s: %w", addrs[0].String(), err) + } + + return names[0], nil +} diff --git a/get.go b/get.go index 8028bd1..0395e98 100644 --- a/get.go +++ b/get.go @@ -1,8 +1,8 @@ package hostname -// functions to import and export the protobuf -// data to and from config files - +// returns the hostname +// hostname is always set to the best effort +// error is set if hostname isn't real func Get() (string, error) { hostname, err := osGetHostname() return hostname, err diff --git a/get_darwin.go b/get_darwin.go index 39cbe0a..f2183a4 100644 --- a/get_darwin.go +++ b/get_darwin.go @@ -1,10 +1,9 @@ package hostname -// functions to import and export the protobuf -// data to and from config files - import ( - "os" + "bytes" + "fmt" + "os/exec" ) // scutil --get HostName @@ -13,6 +12,17 @@ import ( // scutil --set HostName my-mac.example.com func osGetHostname() (string, error) { - hostname, err := os.Hostname() - return hostname, err + return scutil([]string{"-get", "HostName"}) +} + +// getDomainName executes the 'domainname' command and returns its output. +func scutil(args []string) (string, error) { + cmd := exec.Command("scutil", args) + var out bytes.Buffer + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("failed to execute 'scutil': %w", err) + } + domain := out.String() + return domain, nil } diff --git a/get_linux.go b/get_linux.go index 9db8611..e7cd6d0 100644 --- a/get_linux.go +++ b/get_linux.go @@ -4,10 +4,39 @@ package hostname // data to and from config files import ( + "bytes" + "fmt" "os" + "os/exec" + "strings" ) func osGetHostname() (string, error) { - hostname, err := os.Hostname() - return hostname, err + host, err := os.Hostname() + if err != nil { + return host, fmt.Errorf("failed to get hostname: %w", err) + } + + domain, err := getDomainName() + if err != nil || domain == "" { + return host, err + } + + return fmt.Sprintf("%s.%s", host, domain), nil +} + +// getDomainName executes the 'domainname' command and returns its output. +func getDomainName() (string, error) { + cmd := exec.Command("domainname") + var out bytes.Buffer + cmd.Stdout = &out + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("failed to execute 'domainname': %w", err) + } + domain := strings.TrimSpace(out.String()) + if domain == "(none)" { + return "", ErrorDomainNameMisconfigured + } + + return domain, nil }