-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
170 lines (158 loc) · 4.05 KB
/
utils.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package hosts
import (
"fmt"
"log"
"net"
"os"
"strings"
"time"
)
type hostlist []*hostname
type hostname struct {
domain string
ip net.IP
ipv6 bool
wildcard bool
}
// newHostlist creates a hostlist by parsing a file
func newHostlist(data []byte) *hostlist {
return newHostlistString(string(data))
}
func newHostlistString(data string) *hostlist {
hostlist := hostlist{}
for _, v := range strings.Split(data, "\n") {
for _, hostname := range parseLine(v) {
err := hostlist.add(hostname)
if err != nil {
log.Printf("Bad formatted hostsfile line: %s", err)
}
}
}
return &hostlist
}
func (h *hostname) Equal(hostnamev *hostname) bool {
if h.wildcard != hostnamev.wildcard || h.ipv6 != hostnamev.ipv6 {
return false
}
if !h.ip.Equal(hostnamev.ip) {
return false
}
if h.domain != hostnamev.domain {
return false
}
return true
}
// return first match
func (h *hostlist) FindHost(name string) (addr net.IP) {
var ips []net.IP
ips = h.FindHosts(name)
if len(ips) > 0 {
addr = ips[0]
}
return
}
// return exact matches, if existing -> else, return wildcard
func (h *hostlist) FindHosts(name string) (addrs []net.IP) {
for _, hostname := range *h {
if hostname.wildcard == false && hostname.domain == name {
addrs = append(addrs, hostname.ip)
}
}
if len(addrs) == 0 {
var domainMatch string
for _, hostname := range *h {
if hostname.wildcard == true && len(hostname.domain) < len(name) {
domainMatch = strings.Join([]string{".", hostname.domain}, "")
if name[len(name)-len(domainMatch):] == domainMatch {
var left string
left = name[0 : len(name)-len(domainMatch)]
if !strings.Contains(left, ".") {
addrs = append(addrs, hostname.ip)
}
}
}
}
}
return
}
func (h *hostlist) add(hostnamev *hostname) error {
hostname := newHostname(hostnamev.domain, hostnamev.ip, hostnamev.ipv6, hostnamev.wildcard)
for _, found := range *h {
if found.Equal(hostname) {
return fmt.Errorf("Duplicate hostname entry for %#v", hostname)
}
}
*h = append(*h, hostname)
return nil
}
// newHostname creates a new Hostname struct
func newHostname(domain string, ip net.IP, ipv6 bool, wildcard bool) (host *hostname) {
domain = strings.ToLower(domain)
host = &hostname{domain, ip, ipv6, wildcard}
return
}
// ParseLine parses an individual line in a hostfile, which may contain one (un)commented ip and one or more hostnames. For example
// 127.0.0.1 localhost mysite1 mysite2
func parseLine(line string) hostlist {
var hostnames hostlist
if len(line) == 0 {
return hostnames
}
// Parse leading # for disabled lines
if line[0:1] == "#" {
return hostnames
}
// Parse other #s for actual comments
line = strings.Split(line, "#")[0]
// Replace tabs and multispaces with single spaces throughout
line = strings.Replace(line, "\t", " ", -1)
for strings.Contains(line, " ") {
line = strings.Replace(line, " ", " ", -1)
}
line = strings.TrimSpace(line)
// Break line into words
words := strings.Split(line, " ")
for idx, word := range words {
words[idx] = strings.TrimSpace(word)
}
// Separate the first bit (the ip) from the other bits (the domains)
address := words[0]
domains := words[1:]
if strings.Contains(address, "%") {
return hostnames
}
ip := net.ParseIP(address)
var isIPv6 bool
switch {
case !ip.IsGlobalUnicast() && !ip.IsLoopback():
return hostnames
case ip.Equal(net.ParseIP("fe00::")):
return hostnames
case ip.To4() != nil:
isIPv6 = false
case ip.To16() != nil:
isIPv6 = true
default:
log.Printf("Invalid IP address found in hostsfile: %s", address)
return hostnames
}
var isWildcard bool
for _, v := range domains {
isWildcard = false
if len(v) > 1 && v[0:2] == "*." {
v = v[2:]
isWildcard = true
}
hostname := newHostname(v, ip, isIPv6, isWildcard)
hostnames = append(hostnames, hostname)
}
return hostnames
}
// hostsFileMetadata returns metadata about the hosts file.
func hostsFileMetadata(path string) (time.Time, int64, error) {
fi, err := os.Stat(path)
if err != nil {
return time.Time{}, 0, err
}
return fi.ModTime(), fi.Size(), nil
}