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 ¶
- Constants
- func DefaultHostsPaths() (p []string, err error)
- func Parse(dst Set, src io.Reader, buf []byte) (err error)
- type DefaultStorage
- func (s *DefaultStorage) Add(rec *Record)
- func (s *DefaultStorage) ByAddr(addr netip.Addr) (hosts []string)
- func (s *DefaultStorage) ByName(host string) (addrs []netip.Addr)
- func (s *DefaultStorage) Equal(other *DefaultStorage) (ok bool)
- func (s *DefaultStorage) HandleInvalid(srcName string, _ []byte, err error)
- func (s *DefaultStorage) RangeAddrs(f func(host string, addrs []netip.Addr) (cont bool))
- func (s *DefaultStorage) RangeNames(f func(addr netip.Addr, names []string) (cont bool))
- type DiscardSet
- type FuncSet
- type HandleSet
- type LineError
- type NamedReader
- type Record
- type Set
- type Storage
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 DefaultStorage ¶
type DefaultStorage struct {
// contains filtered or unexported fields
}
DefaultStorage is a Storage that removes duplicates. It also implements the HandleSet interface and therefore can be used within Parse.
It must be initialized with NewDefaultStorage.
func NewDefaultStorage ¶
func NewDefaultStorage(readers ...io.Reader) (s *DefaultStorage, err error)
NewDefaultStorage parses data if hosts files format from readers and returns a new properly initialized DefaultStorage. readers are optional, an empty storage is completely usable.
func (*DefaultStorage) Add ¶
func (s *DefaultStorage) Add(rec *Record)
Add implements the Set interface for *DefaultStorage. It skips records without hostnames, ignores duplicates and squashes the rest.
func (*DefaultStorage) ByAddr ¶
func (s *DefaultStorage) ByAddr(addr netip.Addr) (hosts []string)
ByAddr implements the Storage interface for *DefaultStorage. It returns each host for addr in original case, in original adding order without duplicates. It returns nil if h doesn't contain the addr.
func (*DefaultStorage) ByName ¶
func (s *DefaultStorage) ByName(host string) (addrs []netip.Addr)
ByName implements the Storage interface for *DefaultStorage. It returns each address for host in original adding order without duplicates. It returns nil if h doesn't contain the host.
func (*DefaultStorage) Equal ¶
func (s *DefaultStorage) Equal(other *DefaultStorage) (ok bool)
Equal returns true if s and other contain the same addresses mapped to the same hostnames.
func (*DefaultStorage) HandleInvalid ¶
func (s *DefaultStorage) HandleInvalid(srcName string, _ []byte, err error)
HandleInvalid implements the HandleSet interface for *DefaultStorage. It essentially ignores empty lines and logs all other errors at debug level.
func (*DefaultStorage) RangeAddrs ¶
func (s *DefaultStorage) RangeAddrs(f func(host string, addrs []netip.Addr) (cont bool))
RangeAddrs ranges through all hostnames in s and calls f with all the corresponding addresses for each one. The order of range is undefined. addrs must not be modified.
func (*DefaultStorage) RangeNames ¶
func (s *DefaultStorage) RangeNames(f func(addr netip.Addr, names []string) (cont bool))
RangeNames ranges through all addresses in s and calls f with all the corresponding names for each one. The order of range is undefined. names must not be modified.
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/Potterli20/golibs-fork/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/Potterli20/golibs-fork/errors" "github.com/Potterli20/golibs-fork/hostsfile" "github.com/Potterli20/golibs-fork/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.