-
Notifications
You must be signed in to change notification settings - Fork 51
/
fqdn.go
123 lines (108 loc) · 3.44 KB
/
fqdn.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package fqdn
import (
"net"
"os"
"strings"
"sync"
)
// defining os and net package functions in their own variables
// so that we can mock them for unit tests
var (
osHostname = os.Hostname
netLookupIP = net.LookupIP
netLookupAddr = net.LookupAddr
)
const unknownHostname = "unknown"
// InitializeAlternativeHostname can be used to set an alternative hostname that is being used by `FindFQDN`.
// The enforcer can use this during startup to provide an alternative value.
func InitializeAlternativeHostname(hostname string) {
if hostname != "" {
alternativeHostnameOnce.Do(func() {
alternativeHostnameLock.Lock()
alternativeHostname = hostname
alternativeHostnameLock.Unlock()
})
}
}
func getAlternativeHostname() string {
alternativeHostnameLock.RLock()
defer alternativeHostnameLock.RUnlock()
return alternativeHostname
}
var (
alternativeHostnameOnce = &sync.Once{}
alternativeHostnameLock sync.RWMutex
alternativeHostname string
)
// Find returns fqdn. It uses the following algorithm:
// First of all, it will return the globally set alternative hostname if it has been initialized with previously with `IntializeAlternativeHostname`
// If this is not set, it will try to determine the hostname, resolve the hostname to an IP,
// and based on the hostname it will perform a reverse DNS lookup for the IP.
// The first entry of the reverse DNS lookup will be returned.
// If there are any errors during this process, this function will return "unknown".
// It will never return an empty string.
func Find() string {
// return with the global alternative hostname if this is what we really want to do
alternativeHostname := getAlternativeHostname()
if alternativeHostname != "" {
return alternativeHostname
}
// for some cloud providers (like AWS at some point) we prefer different FQDNs
// return with th
hostnameRaw, err := osHostname()
if err != nil {
return unknownHostname
}
// net.LookupIP will actually error if hostname is empty
// so if there is no hostname set in the kernel, also return unknown
// as in all other error cases we want to return a valid string
// make sure that it is set to either os.Hostname or "unknown", but is never empty
hostname := hostnameRaw
if hostnameRaw == "" {
hostname = unknownHostname
}
addrs, err := netLookupIP(hostnameRaw)
if err != nil {
return hostname
}
for _, addr := range addrs {
if ipv4 := addr.To4(); ipv4 != nil {
ip, err := ipv4.MarshalText()
if err != nil {
// impossible case and only possible if there is a bug in golang:
// this will only error if this is not a valid IP address
// To4() already proves that
continue
}
hosts, err := netLookupAddr(string(ip))
if err != nil || len(hosts) == 0 {
continue
}
fqdn := hosts[0]
ret := strings.TrimSuffix(fqdn, ".") // return fqdn without trailing dot
if ret != "" {
return ret
}
}
if ipv6 := addr.To16(); ipv6 != nil {
ip, err := ipv6.MarshalText()
if err != nil {
// impossible case and only possible if there is a bug in golang:
// this will only error if this is not a valid IP address
// To16() already proves that
continue
}
hosts, err := netLookupAddr(string(ip))
if err != nil || len(hosts) == 0 {
continue
}
fqdn := hosts[0]
ret := strings.TrimSuffix(fqdn, ".") // return fqdn without trailing dot
if ret != "" {
return ret
}
}
}
// fall back to os.Hostname or unknown if none of that worked
return hostname
}