providers/shared/fqdn.go (45 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. //go:build linux || darwin || aix package shared import ( "context" "fmt" "net" "os" "strings" ) // FQDNWithContext attempts to lookup the host's fully-qualified domain name and returns it. // It does so using the following algorithm: // // 1. It gets the hostname from the OS. If this step fails, it returns an error. // // 2. It tries to perform a CNAME DNS lookup for the hostname. If this succeeds, it // returns the CNAME (after trimming any trailing period) as the FQDN. // // 3. It tries to perform an IP lookup for the hostname. If this succeeds, it tries // to perform a reverse DNS lookup on the returned IPs and returns the first // successful result (after trimming any trailing period) as the FQDN. // // 4. If steps 2 and 3 both fail, an empty string is returned as the FQDN along with // errors from those steps. func FQDNWithContext(ctx context.Context) (string, error) { hostname, err := os.Hostname() if err != nil { return "", fmt.Errorf("could not get hostname to look for FQDN: %w", err) } return fqdn(ctx, hostname) } // FQDN just calls FQDNWithContext with a background context. // Deprecated. func FQDN() (string, error) { return FQDNWithContext(context.Background()) } func fqdn(ctx context.Context, hostname string) (string, error) { var errs error cname, err := net.DefaultResolver.LookupCNAME(ctx, hostname) if err != nil { errs = fmt.Errorf("could not get FQDN, all methods failed: failed looking up CNAME: %w", err) } if cname != "" { cname = strings.TrimSuffix(cname, ".") // Go might lowercase the cname "for convenience". Therefore, if cname // is the same as hostname, return hostname as is. // See https://github.com/golang/go/blob/go1.22.5/src/net/hosts.go#L38 if strings.ToLower(cname) == strings.ToLower(hostname) { return hostname, nil } return cname, nil } ips, err := net.DefaultResolver.LookupIP(ctx, "ip", hostname) if err != nil { errs = fmt.Errorf("%s: failed looking up IP: %w", errs, err) } for _, ip := range ips { names, err := net.DefaultResolver.LookupAddr(ctx, ip.String()) if err != nil || len(names) == 0 { continue } return strings.TrimSuffix(names[0], "."), nil } return "", errs }