hostsfile

package
v0.0.0-...-9d39026 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 8, 2024 License: Unlicense Imports: 12 Imported by: 0

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

View Source
const ErrEmptyLine errors.Error = "line is empty"

ErrEmptyLine is returned when the hosts file line is empty or contains only comments and spaces.

View Source
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

func DefaultHostsPaths() (p []string, err error)

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

func Parse(dst Set, src io.Reader, buf []byte) (err error)

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]

func (FuncSet) Add

func (f FuncSet) Add(rec *Record)

Add implements the Set interface for FuncSet.

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) Error

func (e *LineError) Error() (msg string)

Error implements the [error] interface for *LineErr.

func (*LineError) Unwrap

func (e *LineError) Unwrap() (unwrapped error)

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

func (rec Record) MarshalText() (data []byte, err error)

MarshalText implements the encoding.TextMarshaler interface for Record.

func (*Record) UnmarshalText

func (rec *Record) UnmarshalText(data []byte) (err error)

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.

type Set

type Set interface {
	// Add adds rec to the set.  rec should be valid.
	Add(rec *Record)
}

Set adds successfully unmarshaled records.

type Storage

type Storage interface {
	// ByAddr returns the hostnames for the given address.
	ByAddr(addr netip.Addr) (names []string)

	// ByName returns the addresses for the given hostname.
	ByName(name string) (addrs []netip.Addr)
}

Storage indexes the hosts file records.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL