Documentation ¶
Overview ¶
Package hostsfile provides utilities for working with system hosts files. The syntax of the hosts files described in man page hosts(5), with hostname's syntax from RFC-952, including its updates from RFC-1123 and further ones.
Index ¶
Examples ¶
Constants ¶
const ErrEmptyLine errors.Error = "line is empty"
ErrEmptyLine is returned when the hosts file line is empty or contains only comments and spaces.
const ErrNoHosts errors.Error = "no hostnames"
ErrNoHosts is returned when the record doesn't contain any delimiters, but the IP address is valid.
Variables ¶
This section is empty.
Functions ¶
func DefaultHostsPaths ¶
DefaultHostsPaths returns the slice of default paths to system hosts files. Those are relative to the corresponding operating system's root directory and always use slashes to separate path elements, since those are assumed to be used with fs.FS. It may return an error only on Windows.
func Parse ¶
Parse reads src and parses it as a hosts file line by line using buf for buffered scanning. If src is a NamedReader, the name of the data source will be set to the Source field of each record.
dst must not be nil, use DiscardSet if only the unmarshaling errors needed. By default it returns all unmarshaling errors within err, but if dst is also a HandleSet, it will be used to handle invalid records and unmarshaling errors wrapped with LineError, see Record.UnmarshalText for returned errors.
Types ¶
type DiscardSet ¶
type DiscardSet struct{}
DiscardSet is a Set that discards all records.
func (DiscardSet) Add ¶
func (DiscardSet) Add(_ *Record)
Add implements the Set interface for DiscardSet.
type FuncSet ¶
type FuncSet func(rec *Record)
FuncSet is a functional Set implementation.
Example ¶
package main import ( "fmt" "net/netip" "strings" "github.com/AdguardTeam/golibs/hostsfile" ) func main() { const content = "# comment\n" + "1.2.3.4 host1 host2\n" + "4.3.2.1 host3\n" + "1.2.3.4 host4 host5 # repeating address\n" + "2.3.4.5 host3 # repeating hostname" addrs := map[string][]netip.Addr{} names := map[netip.Addr][]string{} set := hostsfile.FuncSet(func(r *hostsfile.Record) { names[r.Addr] = append(names[r.Addr], r.Names...) for _, name := range r.Names { addrs[name] = append(addrs[name], r.Addr) } }) // Parse the hosts file. err := hostsfile.Parse(set, strings.NewReader(content), nil) fmt.Printf("error: %s\n", err) fmt.Printf("records for 1.2.3.4: %q\n", names[netip.MustParseAddr("1.2.3.4")]) fmt.Printf("records for host3: %s\n", addrs["host3"]) }
Output: error: parsing: line 1: line is empty records for 1.2.3.4: ["host1" "host2" "host4" "host5"] records for host3: [4.3.2.1 2.3.4.5]
type HandleSet ¶
type HandleSet interface { Set // HandleInvalid unmarshals invalid records according to the err returned by // [Record.UnmarshalText]. data is the original line from the hosts file, // including spaces, srcName is the name of the data source, if provided. HandleInvalid(srcName string, data []byte, err error) }
HandleSet is a Set that handles invalid records.
Example ¶
package main import ( "bytes" "fmt" "strings" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/hostsfile" "github.com/AdguardTeam/golibs/netutil" ) // invalidSet is a [HandleSet] implementation that collects invalid records. type invalidSet []hostsfile.Record // Add implements the [Set] interface for invalidSet. func (s *invalidSet) Add(r *hostsfile.Record) { *s = append(*s, *r) } // AddInvalid implements the [HandleSet] interface for invalidSet. func (s *invalidSet) HandleInvalid(srcName string, data []byte, err error) { addrErr := &netutil.AddrError{} if !errors.As(err, &addrErr) { return } rec := &hostsfile.Record{Source: srcName} _ = rec.UnmarshalText(data) if commIdx := bytes.IndexByte(data, '#'); commIdx >= 0 { data = bytes.TrimRight(data[:commIdx], " \t") } invIdx := bytes.Index(data, []byte(addrErr.Addr)) for _, name := range bytes.Fields(data[invIdx:]) { rec.Names = append(rec.Names, string(name)) } s.Add(rec) } func main() { const content = "\n" + "# comment\n" + "4.3.2.1 invalid.-host valid.host # comment\n" + "1.2.3.4 another.valid.host\n" set := invalidSet{} err := hostsfile.Parse(&set, strings.NewReader(content), nil) fmt.Printf("error: %v\n", err) for _, r := range set { fmt.Printf("%q\n", r.Names) } }
Output: error: <nil> ["invalid.-host" "valid.host"] ["another.valid.host"]
type LineError ¶
type LineError struct { // Line is the line number in the hosts file source. Line int // contains filtered or unexported fields }
LineError is an error about a specific line in the hosts file.
func (*LineError) Unwrap ¶
Unwrap implements the errors.Wrapper interface for *LineErr.
type NamedReader ¶
type NamedReader interface { io.Reader // Name returns the name of the data source. Name() (name string) }
NamedReader is an optional interface that may be implemented by an io.Reader to provide the name of the data source.
type Record ¶
type Record struct { // Addr is the IP address of the record. Addr netip.Addr // Source is the name of the source hosts file. Source string // Names are the hostnames for the Addr of the record. Names []string }
Record represents a single hosts file record.
func (Record) MarshalText ¶
MarshalText implements the encoding.TextMarshaler interface for Record.
func (*Record) UnmarshalText ¶
UnmarshalText implements the encoding.TextUnmarshaler interface for *Record. It only returns the following errors:
- ErrEmptyLine if the line is empty or contains only spaces and comments;
- ErrNoHosts if the record doesn't contain any space delimiters, but the IP address may appear valid;
- error from netip.ParseAddr on invalid IP address;
- netutil.AddrError if one of the hostnames is invalid, but the Record may already contain hostnames.
Note that this function doesn't set the Source field of rec, see Parse and HandleSet for details.