func (hfile *HostsFile) Names() (names []string) {
	names = make([]string, 0, len(hfile.Records))
	for _, rr := range hfile.Records {
		names = append(names, rr.Name)
	}
	return names
}
Names return all hosts domain names.
package dns
import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
	libio "github.com/shuLhan/share/lib/io"
)
const (
	HostsFilePOSIX   = "/etc/hosts"
	HostsFileWindows = "C:\\Windows\\System32\\Drivers\\etc\\hosts"
	defaultTTL       = 604800  
)
type HostsFile struct {
	Path    string `json:"-"`
	Name    string
	Records []*ResourceRecord `json:"-"`
	out     *os.File
}
func NewHostsFile(path string, records []*ResourceRecord) (
	hfile *HostsFile, err error,
) {
	hfile = &HostsFile{
		Path:    path,
		Name:    filepath.Base(path),
		Records: records,
	}
	err = hfile.Save()
	return hfile, err
}
func GetSystemHosts() string {
	if runtime.GOOS == "windows" {
		return HostsFileWindows
	}
	return HostsFilePOSIX
}
func LoadHostsDir(dir string) (hostsFiles map[string]*HostsFile, err error) {
	if len(dir) == 0 {
		return nil, nil
	}
	d, err := os.Open(dir)
	if err != nil {
		return nil, fmt.Errorf("LoadHostsDir %q: %w", dir, err)
	}
	fis, err := d.Readdir(0)
	if err != nil {
		log.Println("dns: LoadHostsDir: ", err)
		errClose := d.Close()
		if errClose != nil {
			log.Println("dns: LoadHostsDir: ", errClose)
		}
		return nil, fmt.Errorf("LoadHostsDir %q: %w", dir, err)
	}
	hostsFiles = make(map[string]*HostsFile)
	for x := 0; x < len(fis); x++ {
		if fis[x].IsDir() {
			continue
		}
		name := fis[x].Name()
		if name[0] == '.' {
			continue
		}
		hostsFilePath := filepath.Join(dir, name)
		hfile, err := ParseHostsFile(hostsFilePath)
		if err != nil {
			return hostsFiles, fmt.Errorf("LoadHostsDir %q: %w", dir, err)
		}
		hostsFiles[name] = hfile
	}
	err = d.Close()
	if err != nil {
		return hostsFiles, fmt.Errorf("LoadHostsDir %q: %w", dir, err)
	}
	return hostsFiles, nil
}
func ParseHostsFile(path string) (hfile *HostsFile, err error) {
	if len(path) == 0 {
		path = GetSystemHosts()
	}
	reader, err := libio.NewReader(path)
	if err != nil {
		return nil, fmt.Errorf("ParseHostsFile %q: %w", path, err)
	}
	hfile = &HostsFile{
		Path:    path,
		Name:    filepath.Base(path),
		Records: parse(reader),
	}
	return hfile, nil
}
func parse(reader *libio.Reader) (listRR []*ResourceRecord) {
	var (
		seps  = []byte{'\t', '\v', ' '}
		terms = []byte{'\n', '\f', '#'}
	)
	for {
		c := reader.SkipSpaces()
		if c == 0 {
			break
		}
		if c == '#' {
			reader.SkipLine()
			continue
		}
		addr, isTerm, c := reader.ReadUntil(seps, terms)
		if isTerm {
			if c == 0 {
				break
			}
			if c == '#' {
				reader.SkipLine()
			}
			continue
		}
		for {
			c := reader.SkipSpaces()
			if c == 0 {
				break
			}
			if c == '#' {
				reader.SkipLine()
				break
			}
			hname, isTerm, c := reader.ReadUntil(seps, terms)
			if len(hname) > 0 {
				qtype := GetQueryTypeFromAddress(addr)
				if qtype == 0 {
					continue
				}
				rr := &ResourceRecord{
					Name:  string(bytes.ToLower(hname)),
					Type:  qtype,
					Class: QueryClassIN,
					TTL:   defaultTTL,
					Value: string(addr),
				}
				listRR = append(listRR, rr)
			}
			if isTerm {
				if c == 0 {
					break
				}
				if c == '#' {
					reader.SkipLine()
				}
				break
			}
		}
	}
	return listRR
}
func (hfile *HostsFile) AppendAndSaveRecord(rr *ResourceRecord) (err error) {
	f, err := os.OpenFile(
		hfile.Path,
		os.O_RDWR|os.O_CREATE|os.O_APPEND,
		0600,
	)
	if err != nil {
		return err
	}
	ipAddress, ok := rr.Value.(string)
	if ok {
		_, err = fmt.Fprintf(f, "%s %s\n", ipAddress, rr.Name)
	}
	errClose := f.Close()
	if errClose != nil {
		if err == nil {
			err = errClose
		}
	}
	if err == nil {
		hfile.Records = append(hfile.Records, rr)
	}
	return err
}
func (hfile *HostsFile) Delete() (err error) {
	return os.RemoveAll(hfile.Path)
}