package hosts import ( "bufio" "bytes" "fmt" "io" "net" "os" "runtime" "strings" "sync" ) var lineBreaker string func init() { if runtime.GOOS == "windows" { lineBreaker = "\r\n" } else { lineBreaker = "\n" } } func SystemHostpath() string { if runtime.GOOS == "windows" { return `C:\Windows\System32\drivers\etc\hosts` } return `/etc/hosts` } var defaultParser = NewHosts() func ParseHosts(hostPath string) error { return defaultParser.Parse(hostPath) } func Parse() error { return defaultParser.Parse(SystemHostpath()) } func List() []*HostNode { return defaultParser.List() } func ListHosts() map[string][]string { return defaultParser.ListHosts() } func ListIPs() map[string][]string { return defaultParser.ListIPs() } func ListByHost(host string) []*HostNode { return defaultParser.ListByHost(host) } func ListIPsByHost(host string) []string { return defaultParser.ListIPsByHost(host) } func ListFirstIPByHost(host string) string { return defaultParser.ListFirstIPByHost(host) } func ListByIP(ip string) []*HostNode { return defaultParser.ListByIP(ip) } func ListHostsByIP(ip string) []string { return defaultParser.ListHostsByIP(ip) } func ListFirstHostByIP(ip string) string { return defaultParser.ListFirstHostByIP(ip) } func AddHosts(ip string, hosts ...string) error { return defaultParser.AddHosts(ip, hosts...) } func AddHostsComment(comment string, ip string, hosts ...string) error { return defaultParser.AddHostsComment(comment, ip, hosts...) } func RemoveIPHosts(ip string, hosts ...string) error { return defaultParser.RemoveIPHosts(ip, hosts...) } func RemoveIPs(ips ...string) error { return defaultParser.RemoveIPs(ips...) } func RemoveHosts(hosts ...string) error { return defaultParser.RemoveHosts(hosts...) } func SetIPHosts(ip string, hosts ...string) error { return defaultParser.SetIPHosts(ip, hosts...) } func SetHostIPs(host string, ips ...string) error { return defaultParser.SetHostIPs(host, ips...) } func InsertNodes(node *HostNode, before bool, comment string, ip string, hosts ...string) error { return defaultParser.InsertNodeByData(node, before, comment, ip, hosts...) } func SaveAs(path string) error { return defaultParser.SaveAs(path) } func Save() error { return defaultParser.Save() } func Build() ([]byte, error) { return defaultParser.Build() } func GetFirstNode() (*HostNode, error) { return defaultParser.GetFirstNode() } func GetNode(nodeID uint64) (*HostNode, error) { return defaultParser.GetNode(nodeID) } func GetNextNode(nodeID uint64) (*HostNode, error) { return defaultParser.GetNextNode(nodeID) } func GetLastNode(nodeID uint64) (*HostNode, error) { return defaultParser.GetLastNode(nodeID) } func GetLatestNode() (*HostNode, error) { return defaultParser.GetLatestNode() } func DeleteNode(node *HostNode) error { return defaultParser.DeleteNode(node) } func DeleteNodeByUID(uid uint64) error { return defaultParser.DeleteNodeByUID(uid) } func AddNode(node *HostNode) error { return defaultParser.AddNode(node) } func InsertNode(node *HostNode) error { return defaultParser.InsertNode(node) } func UpdateNode(node *HostNode) error { return defaultParser.UpdateNode(node) } type HostNode struct { uid uint64 nextuid uint64 lastuid uint64 ip string host []string comment string original string onlyComment bool valid bool } type Host struct { idx uint64 hostPath string firstUid uint64 lastUid uint64 fulldata map[uint64]*HostNode hostData map[string][]*HostNode ipData map[string][]*HostNode sync.RWMutex } func NewHosts() *Host { return &Host{ hostData: make(map[string][]*HostNode), ipData: make(map[string][]*HostNode), fulldata: make(map[uint64]*HostNode), } } func cloneHostNode(node *HostNode) *HostNode { if node == nil { return nil } cloned := *node cloned.host = append([]string(nil), node.host...) return &cloned } func cloneHostNodes(nodes []*HostNode) []*HostNode { if len(nodes) == 0 { return nil } cloned := make([]*HostNode, 0, len(nodes)) for _, node := range nodes { cloned = append(cloned, cloneHostNode(node)) } return cloned } func copyHostNodeState(dst, src *HostNode) { if dst == nil || src == nil { return } dst.uid = src.uid dst.nextuid = src.nextuid dst.lastuid = src.lastuid dst.ip = src.ip dst.host = append(dst.host[:0], src.host...) dst.comment = src.comment dst.original = src.original dst.onlyComment = src.onlyComment dst.valid = src.valid } func normalizeEditableNode(node *HostNode) (*HostNode, error) { if node == nil { return nil, fmt.Errorf("node not exists") } candidate := cloneHostNode(node) if candidate.comment != "" && !strings.HasPrefix(strings.TrimSpace(candidate.comment), "#") { candidate.comment = "#" + candidate.comment } if len(candidate.host) > 0 { hosts, err := normalizeHostTokens(candidate.host...) if err != nil { return nil, err } candidate.host = hosts } if candidate.ip != "" || len(candidate.host) > 0 { ip, err := normalizeIPToken(candidate.ip) if err != nil { return nil, err } candidate.ip = ip if len(candidate.host) == 0 { return nil, fmt.Errorf("empty host") } } else { candidate.ip = strings.TrimSpace(candidate.ip) } candidate.onlyComment = candidate.ip == "" && len(candidate.host) == 0 && candidate.comment != "" if !candidate.onlyComment && candidate.ip == "" && len(candidate.host) == 0 { return nil, fmt.Errorf("empty node") } candidate.valid = candidate.CheckValid() if !candidate.valid { return nil, fmt.Errorf("invalid hosts node") } return candidate, nil } func (h *Host) Parse(hostPath string) error { h.Lock() defer h.Unlock() h.idx = 0 h.firstUid = 0 h.lastUid = 0 h.hostPath = hostPath h.fulldata = make(map[uint64]*HostNode) h.hostData = make(map[string][]*HostNode) h.ipData = make(map[string][]*HostNode) return h.parse() } func (h *Host) parse() error { f, err := os.OpenFile(h.hostPath, os.O_RDONLY, 0666) if err != nil { return fmt.Errorf("open hosts file %s error: %s", h.hostPath, err) } defer f.Close() buf := bufio.NewReader(f) for { line, err := buf.ReadString('\n') if err != nil && err != io.EOF { return fmt.Errorf("read hosts file error: %s", err) } raw := strings.TrimRight(line, "\r\n") if err == nil || (err == io.EOF && raw != "") { data, _ := h.parseLine(raw) h.appendNodeLocked(&data) } if err == io.EOF { break } } return nil } func (h *Host) parseLine(raw string) (HostNode, error) { var res = HostNode{ original: raw, } data := strings.TrimSpace(raw) if len(data) == 0 { return res, fmt.Errorf("empty line") } if data[0] == '#' { res.comment = data res.onlyComment = true res.valid = true return res, nil } var dataArr []string cache := "" for k, v := range data { if v == '#' { if len(cache) > 0 { dataArr = append(dataArr, cache) cache = "" } dataArr = append(dataArr, data[k:]) break } if v == ' ' || v == '\t' { if len(cache) > 0 { dataArr = append(dataArr, cache) cache = "" } continue } cache += string(v) } if len(cache) > 0 { dataArr = append(dataArr, cache) } if len(dataArr) < 2 { return res, fmt.Errorf("invalid line") } if strings.HasPrefix(dataArr[1], "#") { return res, fmt.Errorf("invalid line") } if net.ParseIP(dataArr[0]) == nil { return res, fmt.Errorf("invalid ip address") } for k, v := range dataArr { switch k { case 0: res.ip = v default: if strings.HasPrefix(v, "#") { res.comment = v } else { res.host = append(res.host, v) } } } res.valid = true return res, nil } func (h *Host) List() []*HostNode { h.RLock() defer h.RUnlock() var res []*HostNode nextUid := h.firstUid for { if nextUid == 0 { break } res = append(res, cloneHostNode(h.fulldata[nextUid])) nextUid = h.fulldata[nextUid].nextuid } return res } func (h *Host) ListHosts() map[string][]string { h.RLock() defer h.RUnlock() if h.hostData == nil { return nil } var res = make(map[string][]string) for k, v := range h.hostData { for _, vv := range v { res[k] = append(res[k], vv.ip) } } return res } func (h *Host) ListIPs() map[string][]string { h.RLock() defer h.RUnlock() if h.ipData == nil { return nil } var res = make(map[string][]string) for k, v := range h.ipData { for _, vv := range v { res[k] = append(res[k], vv.host...) } } return res } func (h *Host) ListByHost(host string) []*HostNode { h.RLock() defer h.RUnlock() if h.hostData == nil { return nil } return cloneHostNodes(h.hostData[host]) } func (h *Host) ListIPsByHost(host string) []string { h.RLock() defer h.RUnlock() if h.hostData == nil { return nil } var res []string for _, v := range h.hostData[host] { res = append(res, v.ip) } return res } func (h *Host) ListFirstIPByHost(host string) string { h.RLock() defer h.RUnlock() if h.hostData == nil { return "" } for _, v := range h.hostData[host] { return v.ip } return "" } func (h *Host) ListByIP(ip string) []*HostNode { h.RLock() defer h.RUnlock() if h.ipData == nil { return nil } return cloneHostNodes(h.ipData[ip]) } func (h *Host) ListHostsByIP(ip string) []string { h.RLock() defer h.RUnlock() return h.listHostsByIP(ip) } func (h *Host) listHostsByIP(ip string) []string { if h.ipData == nil { return nil } var res []string for _, v := range h.ipData[ip] { res = append(res, v.host...) } return res } func (h *Host) ListFirstHostByIP(ip string) string { h.RLock() defer h.RUnlock() if h.ipData == nil { return "" } for _, v := range h.ipData[ip] { if len(v.host) > 0 { return v.host[0] } } return "" } func (h *Host) AddHosts(ip string, hosts ...string) error { return h.addHosts("", ip, hosts...) } func (h *Host) AddHostsComment(comment string, ip string, hosts ...string) error { return h.addHosts(comment, ip, hosts...) } func (h *Host) RemoveIPHosts(ip string, hosts ...string) error { h.Lock() defer h.Unlock() if h.ipData == nil { return fmt.Errorf("hosts data not initialized") } ipInfo := h.ipData[ip] if len(ipInfo) == 0 { return nil } cntfor: for _, v := range ipInfo { for _, host := range hosts { if len(v.host) == 1 && v.host[0] == host { h.removeNodeFromIPDataLocked(ip, v.uid) h.removeNodeFromHostDataLocked(host, v.uid) h.unlinkNodeLocked(v) v = nil continue cntfor } if len(v.host) > 1 { var newHosts []string removed := false for _, vv := range v.host { if vv != host { newHosts = append(newHosts, vv) } else { removed = true } } if !removed { continue } v.host = newHosts h.removeNodeFromHostDataLocked(host, v.uid) } } } return nil } func (h *Host) RemoveIPs(ips ...string) error { h.Lock() defer h.Unlock() if h.ipData == nil { return fmt.Errorf("hosts data not initialized") } for _, ip := range ips { h.removeIPLocked(ip) } return nil } func (h *Host) RemoveHosts(hosts ...string) error { h.Lock() defer h.Unlock() if h.hostData == nil { return fmt.Errorf("hosts data not initialized") } for _, host := range hosts { h.removeHostLocked(host) } return nil } func (h *Host) SetIPHosts(ip string, hosts ...string) error { var err error ip, err = normalizeIPToken(ip) if err != nil { return err } hosts, err = normalizeHostTokens(hosts...) if err != nil { return err } if len(hosts) == 0 { return fmt.Errorf("empty host") } h.Lock() defer h.Unlock() if h.ipData == nil { return fmt.Errorf("hosts data not initialized") } info := h.ipData[ip] if len(info) == 0 { return h.addHostsLocked("", ip, hosts...) } else if len(info) == 1 { node := *info[0] node.host = append([]string(nil), hosts...) node.comment = "" return h.updateNodeLocked(&node) } h.removeIPLocked(ip) return h.addHostsLocked("", ip, hosts...) } func (h *Host) SetHostIPs(host string, ips ...string) error { if len(ips) == 0 { return fmt.Errorf("no ip address") } var err error host, err = normalizeHostToken(host) if err != nil { return err } normalizedIPs := make([]string, 0, len(ips)) for _, ip := range ips { normalizedIP, err := normalizeIPToken(ip) if err != nil { return err } normalizedIPs = append(normalizedIPs, normalizedIP) } h.Lock() defer h.Unlock() if h.hostData == nil { return fmt.Errorf("hosts data not initialized") } h.removeHostLocked(host) for _, ip := range normalizedIPs { err := h.addHostsLocked("", ip, host) if err != nil { return err } } return nil } func (h *Host) addHosts(comment string, ip string, hosts ...string) error { h.Lock() defer h.Unlock() return h.addHostsLocked(comment, ip, hosts...) } func (h *Host) addHostsLocked(comment string, ip string, hosts ...string) error { if h.hostData == nil { return fmt.Errorf("hosts data not initialized") } var err error ip, err = normalizeIPToken(ip) if err != nil { return err } ipInfo := h.listHostsByIP(ip) var needAddHosts []string for _, v := range hosts { host, err := normalizeHostToken(v) if err != nil { return err } if !inArray(ipInfo, host) && !inArray(needAddHosts, host) { needAddHosts = append(needAddHosts, host) } } if len(needAddHosts) == 0 { return nil } if comment != "" && !strings.HasPrefix(strings.TrimSpace(comment), "#") { comment = "#" + comment } hostNode := HostNode{ ip: ip, host: needAddHosts, valid: true, comment: comment, } if !hostNode.CheckValid() { return fmt.Errorf("invalid hosts node") } h.appendNodeLocked(&hostNode) return nil } func (h *Host) removeHostLocked(host string) { hostInfo := h.hostData[host] if len(hostInfo) == 0 { return } delete(h.hostData, host) for _, v := range hostInfo { var newHosts []string for _, vv := range v.host { if vv != host { newHosts = append(newHosts, vv) } } v.host = newHosts if len(v.host) == 0 { h.removeNodeFromIPDataLocked(v.ip, v.uid) h.unlinkNodeLocked(v) } } } func (h *Host) removeIPLocked(ip string) { ipInfo := h.ipData[ip] if len(ipInfo) == 0 { return } delete(h.ipData, ip) for _, v := range ipInfo { for _, host := range v.host { h.removeNodeFromHostDataLocked(host, v.uid) } h.unlinkNodeLocked(v) } } func (h *Host) InsertNodeByData(node *HostNode, before bool, comment string, ip string, hosts ...string) error { h.Lock() defer h.Unlock() if h.hostData == nil { return fmt.Errorf("hosts data not initialized") } if node == nil { return fmt.Errorf("node not exists") } anchor, ok := h.fulldata[node.uid] if !ok { return fmt.Errorf("node not exists") } if comment != "" && !strings.HasPrefix(strings.TrimSpace(comment), "#") { comment = "#" + comment } ip = strings.TrimSpace(ip) var err error hosts, err = normalizeHostTokens(hosts...) if err != nil { return err } if ip != "" || len(hosts) > 0 { ip, err = normalizeIPToken(ip) if err != nil { return err } if len(hosts) == 0 { return fmt.Errorf("empty host") } } if ip == "" && len(hosts) == 0 && comment == "" { return fmt.Errorf("empty node") } hostNode := HostNode{ ip: ip, host: hosts, valid: true, comment: comment, } if ip == "" && len(hosts) == 0 && comment != "" { hostNode.onlyComment = true } return h.insertNodeRelativeLocked(anchor, before, &hostNode) } func normalizeIPToken(ip string) (string, error) { ip = strings.TrimSpace(ip) if net.ParseIP(ip) == nil { return "", fmt.Errorf("invalid ip address") } return ip, nil } func normalizeHostToken(host string) (string, error) { host = strings.TrimSpace(host) if host == "" { return "", fmt.Errorf("empty host") } if strings.ContainsAny(host, " \t\r\n") || strings.HasPrefix(host, "#") { return "", fmt.Errorf("invalid host") } return host, nil } func normalizeHostTokens(hosts ...string) ([]string, error) { out := make([]string, 0, len(hosts)) seen := make(map[string]struct{}, len(hosts)) for _, host := range hosts { normalized, err := normalizeHostToken(host) if err != nil { return nil, err } if _, ok := seen[normalized]; ok { continue } seen[normalized] = struct{}{} out = append(out, normalized) } return out, nil } func (h *Host) DeleteNode(node *HostNode) error { h.Lock() defer h.Unlock() if h.hostData == nil { return fmt.Errorf("hosts data not initialized") } if node == nil { return fmt.Errorf("node not exists") } current, ok := h.fulldata[node.uid] if !ok { return fmt.Errorf("node not exists") } for _, v := range current.host { h.removeNodeFromHostDataLocked(v, current.uid) } if current.ip != "" { h.removeNodeFromIPDataLocked(current.ip, current.uid) } h.unlinkNodeLocked(current) return nil } func (h *Host) DeleteNodeByUID(uid uint64) error { h.Lock() defer h.Unlock() node, ok := h.fulldata[uid] if !ok { return fmt.Errorf("node not exists") } for _, v := range node.host { h.removeNodeFromHostDataLocked(v, node.uid) } if node.ip != "" { h.removeNodeFromIPDataLocked(node.ip, node.uid) } h.unlinkNodeLocked(node) return nil } func (h *Host) AddNode(node *HostNode) error { h.Lock() defer h.Unlock() if h.hostData == nil { return fmt.Errorf("hosts data not initialized") } prepared, err := normalizeEditableNode(node) if err != nil { return err } h.appendNodeLocked(prepared) copyHostNodeState(node, prepared) return nil } func (h *Host) InsertNode(node *HostNode) error { if node.lastuid == 0 && node.nextuid == 0 { return h.AddNode(node) } h.Lock() defer h.Unlock() if h.hostData == nil { return fmt.Errorf("hosts data not initialized") } prepared, err := normalizeEditableNode(node) if err != nil { return err } prepared.uid = h.idx + 1 lastNode := h.fulldata[prepared.lastuid] nextNode := h.fulldata[prepared.nextuid] if lastNode == nil || nextNode == nil { return fmt.Errorf("node link not exists") } if lastNode.nextuid != prepared.nextuid || nextNode.lastuid != prepared.lastuid { return fmt.Errorf("node lastuid nextuid not match") } h.idx++ lastNode.nextuid = prepared.uid nextNode.lastuid = prepared.uid h.storeNodeLocked(prepared) copyHostNodeState(node, prepared) return nil } func (h *Host) appendNodeLocked(node *HostNode) { if node == nil { return } h.idx++ node.uid = h.idx node.lastuid = h.lastUid node.nextuid = 0 if h.lastUid != 0 { if last := h.fulldata[h.lastUid]; last != nil { last.nextuid = node.uid } } else { h.firstUid = node.uid } if h.firstUid == 0 { h.firstUid = node.uid } h.lastUid = node.uid h.storeNodeLocked(node) } func (h *Host) insertNodeRelativeLocked(anchor *HostNode, before bool, node *HostNode) error { if anchor == nil || node == nil { return fmt.Errorf("node not exists") } h.idx++ node.uid = h.idx if before { node.nextuid = anchor.uid node.lastuid = anchor.lastuid if anchor.lastuid != 0 { prev := h.fulldata[anchor.lastuid] if prev == nil { return fmt.Errorf("node lastuid not exists") } prev.nextuid = node.uid } else { h.firstUid = node.uid } anchor.lastuid = node.uid } else { node.lastuid = anchor.uid node.nextuid = anchor.nextuid if anchor.nextuid != 0 { next := h.fulldata[anchor.nextuid] if next == nil { return fmt.Errorf("node nextuid not exists") } next.lastuid = node.uid } else { h.lastUid = node.uid } anchor.nextuid = node.uid } if h.firstUid == 0 { h.firstUid = node.uid } if node.nextuid == 0 { h.lastUid = node.uid } node.valid = node.CheckValid() h.storeNodeLocked(node) return nil } func (h *Host) storeNodeLocked(node *HostNode) { if node == nil { return } stored := cloneHostNode(node) h.fulldata[stored.uid] = stored if !stored.valid { return } if stored.ip != "" { h.ipData[stored.ip] = append(h.ipData[stored.ip], stored) } for _, v := range stored.host { h.hostData[v] = append(h.hostData[v], stored) } } func (h *Host) removeNodeFromIPDataLocked(ip string, uid uint64) { var newIPData []*HostNode for _, node := range h.ipData[ip] { if node.uid != uid { newIPData = append(newIPData, node) } } if len(newIPData) == 0 { delete(h.ipData, ip) return } h.ipData[ip] = newIPData } func (h *Host) removeNodeFromHostDataLocked(host string, uid uint64) { var newHostData []*HostNode for _, node := range h.hostData[host] { if node.uid != uid { newHostData = append(newHostData, node) } } if len(newHostData) == 0 { delete(h.hostData, host) return } h.hostData[host] = newHostData } func (h *Host) unlinkNodeLocked(node *HostNode) { if node == nil { return } if node.lastuid != 0 { if last := h.fulldata[node.lastuid]; last != nil { last.nextuid = node.nextuid } } else { h.firstUid = node.nextuid } if node.nextuid != 0 { if next := h.fulldata[node.nextuid]; next != nil { next.lastuid = node.lastuid } } else { h.lastUid = node.lastuid } delete(h.fulldata, node.uid) } func inArray(arr []string, v string) bool { for _, vv := range arr { if v == vv { return true } } return false } func (h *Host) SaveAs(path string) error { return h.save(path) } func (h *Host) Save() error { return h.save(h.hostPath) } func (h *Host) save(path string) error { h.Lock() defer h.Unlock() if h.fulldata == nil { return fmt.Errorf("no data") } f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) if err != nil { return fmt.Errorf("open hosts file %s error: %s", h.hostPath, err) } defer f.Close() if h.firstUid == 0 { return nil } node := h.fulldata[h.firstUid] if node == nil { return fmt.Errorf("no data") } for { if node.onlyComment { if node.original != "" && strings.TrimSpace(node.original) == node.comment { if _, err := f.WriteString(node.original + lineBreaker); err != nil { return fmt.Errorf("write hosts file error: %s", err) } } else if _, err := f.WriteString(node.comment + lineBreaker); err != nil { return fmt.Errorf("write hosts file error: %s", err) } if node.nextuid == 0 { break } node = h.fulldata[node.nextuid] continue } if !node.valid { if _, err := f.WriteString(node.original + lineBreaker); err != nil { return fmt.Errorf("write hosts file error: %s", err) } if node.nextuid == 0 { break } node = h.fulldata[node.nextuid] continue } if _, err := f.WriteString(node.ip + " "); err != nil { return fmt.Errorf("write hosts file error: %s", err) } if _, err := f.WriteString(strings.Join(node.host, " ")); err != nil { return fmt.Errorf("write hosts file error: %s", err) } if len(node.comment) > 0 { if _, err := f.WriteString(" " + node.comment); err != nil { return fmt.Errorf("write hosts file error: %s", err) } } if _, err := f.WriteString(lineBreaker); err != nil { return fmt.Errorf("write hosts file error: %s", err) } if node.nextuid == 0 { break } node = h.fulldata[node.nextuid] } return nil } func (h *Host) Build() ([]byte, error) { h.Lock() defer h.Unlock() if h.fulldata == nil { return nil, fmt.Errorf("no data") } if h.firstUid == 0 { return []byte{}, nil } var f bytes.Buffer node := h.fulldata[h.firstUid] if node == nil { return nil, fmt.Errorf("no data") } for { if node.onlyComment { if node.original != "" && strings.TrimSpace(node.original) == node.comment { if _, err := f.WriteString(node.original + lineBreaker); err != nil { return nil, fmt.Errorf("write hosts file error: %s", err) } } else if _, err := f.WriteString(node.comment + lineBreaker); err != nil { return nil, fmt.Errorf("write hosts file error: %s", err) } if node.nextuid == 0 { break } node = h.fulldata[node.nextuid] continue } if !node.valid { if _, err := f.WriteString(node.original + lineBreaker); err != nil { return nil, fmt.Errorf("write hosts file error: %s", err) } if node.nextuid == 0 { break } node = h.fulldata[node.nextuid] continue } if _, err := f.WriteString(node.ip + " "); err != nil { return nil, fmt.Errorf("write hosts file error: %s", err) } if _, err := f.WriteString(strings.Join(node.host, " ")); err != nil { return nil, fmt.Errorf("write hosts file error: %s", err) } if len(node.comment) > 0 { if _, err := f.WriteString(" " + node.comment); err != nil { return nil, fmt.Errorf("write hosts file error: %s", err) } } if _, err := f.WriteString(lineBreaker); err != nil { return nil, fmt.Errorf("write hosts file error: %s", err) } if node.nextuid == 0 { break } node = h.fulldata[node.nextuid] } return f.Bytes(), nil } func (h *Host) GetFirstNode() (*HostNode, error) { h.RLock() defer h.RUnlock() if h.firstUid == 0 { return nil, fmt.Errorf("no data") } if h.fulldata == nil { return nil, fmt.Errorf("no data") } return cloneHostNode(h.fulldata[h.firstUid]), nil } func (h *Host) GetNode(nodeID uint64) (*HostNode, error) { h.RLock() defer h.RUnlock() if h.fulldata == nil { return nil, fmt.Errorf("no data") } if node, ok := h.fulldata[nodeID]; ok { return cloneHostNode(node), nil } return nil, fmt.Errorf("node not exists") } func (h *Host) GetNextNode(nodeID uint64) (*HostNode, error) { h.RLock() defer h.RUnlock() if h.fulldata == nil { return nil, fmt.Errorf("no data") } if node, ok := h.fulldata[nodeID]; ok { if node.nextuid == 0 { return nil, fmt.Errorf("no next node") } return cloneHostNode(h.fulldata[node.nextuid]), nil } return nil, fmt.Errorf("node not exists") } func (h *Host) GetLastNode(nodeID uint64) (*HostNode, error) { h.RLock() defer h.RUnlock() if h.fulldata == nil { return nil, fmt.Errorf("no data") } if node, ok := h.fulldata[nodeID]; ok { if node.lastuid == 0 { return nil, fmt.Errorf("no last node") } return cloneHostNode(h.fulldata[node.lastuid]), nil } return nil, fmt.Errorf("node not exists") } func (h *Host) GetLatestNode() (*HostNode, error) { h.RLock() defer h.RUnlock() if h.lastUid == 0 { return nil, fmt.Errorf("no data") } if h.fulldata == nil { return nil, fmt.Errorf("no data") } return cloneHostNode(h.fulldata[h.lastUid]), nil } func (h *Host) UpdateNode(node *HostNode) error { h.Lock() defer h.Unlock() return h.updateNodeLocked(node) } func (h *Host) updateNodeLocked(node *HostNode) error { if node == nil { return fmt.Errorf("node not exists") } if h.fulldata == nil { return fmt.Errorf("no data") } current, ok := h.fulldata[node.uid] if !ok { return fmt.Errorf("node not exists") } candidate, err := normalizeEditableNode(node) if err != nil { return err } candidate.uid = current.uid candidate.lastuid = current.lastuid candidate.nextuid = current.nextuid if candidate.original == "" { candidate.original = current.original } for _, host := range current.host { h.removeNodeFromHostDataLocked(host, current.uid) } if current.ip != "" { h.removeNodeFromIPDataLocked(current.ip, current.uid) } h.storeNodeLocked(candidate) copyHostNodeState(node, candidate) return nil } func (n *HostNode) String() string { if n.onlyComment { return n.comment } return fmt.Sprintf("%s %s %s", n.ip, strings.Join(n.host, " "), n.comment) } func (n *HostNode) Bytes() []byte { return []byte(n.String()) } func (n *HostNode) Hosts() []string { return n.host } func (n *HostNode) IP() string { return n.ip } func (n *HostNode) Comment() string { return n.comment } func (n *HostNode) UID() uint64 { return n.uid } func (n *HostNode) NextUID() uint64 { return n.nextuid } func (n *HostNode) LastUID() uint64 { return n.lastuid } func (n *HostNode) Valid() bool { return n.valid } func (n *HostNode) OnlyComment() bool { return n.onlyComment } func (n *HostNode) Original() string { return n.original } func (n *HostNode) SetHosts(hosts ...string) { n.host = hosts } func (n *HostNode) SetIP(ip string) { n.ip = ip } func (n *HostNode) SetComment(comment string) { if comment == "" { return } if !strings.HasPrefix(strings.TrimSpace(comment), "#") { n.comment = "#" + comment return } n.comment = comment } func (n *HostNode) SetValid(valid bool) { n.valid = valid } func (n *HostNode) SetOnlyComment(onlyComment bool) { n.onlyComment = onlyComment } func (n *HostNode) SetOriginal(original string) { n.original = original } func (n *HostNode) AddHosts(hosts ...string) { n.host = append(n.host, hosts...) } func (n *HostNode) SetLastUID(uid uint64) error { if n.uid != 0 { return fmt.Errorf("uid already set,you cannot change the existing node") } n.lastuid = uid return nil } func (n *HostNode) SetNextUID(uid uint64) error { if n.uid != 0 { return fmt.Errorf("uid already set,you cannot change the existing node") } n.nextuid = uid return nil } func (n *HostNode) CheckValid() bool { if n.ip == "" && len(n.host) == 0 && n.comment == "" { return true } if n.ip == "" && len(n.host) == 0 && n.comment != "" { return true } if n.ip == "" && len(n.host) > 0 { return false } if net.ParseIP(n.ip) == nil { return false } return true }