161 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package gost
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"io"
 | 
						|
	"net"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/go-log/log"
 | 
						|
)
 | 
						|
 | 
						|
// Host is a static mapping from hostname to IP.
 | 
						|
type Host struct {
 | 
						|
	IP       net.IP
 | 
						|
	Hostname string
 | 
						|
	Aliases  []string
 | 
						|
}
 | 
						|
 | 
						|
// NewHost creates a Host.
 | 
						|
func NewHost(ip net.IP, hostname string, aliases ...string) Host {
 | 
						|
	return Host{
 | 
						|
		IP:       ip,
 | 
						|
		Hostname: hostname,
 | 
						|
		Aliases:  aliases,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Hosts is a static table lookup for hostnames.
 | 
						|
// For each host a single line should be present with the following information:
 | 
						|
// IP_address canonical_hostname [aliases...]
 | 
						|
// Fields of the entry are separated by any number of blanks and/or tab characters.
 | 
						|
// Text from a "#" character until the end of the line is a comment, and is ignored.
 | 
						|
type Hosts struct {
 | 
						|
	hosts   []Host
 | 
						|
	period  time.Duration
 | 
						|
	stopped chan struct{}
 | 
						|
	mux     sync.RWMutex
 | 
						|
}
 | 
						|
 | 
						|
// NewHosts creates a Hosts with optional list of hosts.
 | 
						|
func NewHosts(hosts ...Host) *Hosts {
 | 
						|
	return &Hosts{
 | 
						|
		hosts:   hosts,
 | 
						|
		stopped: make(chan struct{}),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// AddHost adds host(s) to the host table.
 | 
						|
func (h *Hosts) AddHost(host ...Host) {
 | 
						|
	h.mux.Lock()
 | 
						|
	defer h.mux.Unlock()
 | 
						|
 | 
						|
	h.hosts = append(h.hosts, host...)
 | 
						|
}
 | 
						|
 | 
						|
// Lookup searches the IP address corresponds to the given host from the host table.
 | 
						|
func (h *Hosts) Lookup(host string) (ip net.IP) {
 | 
						|
	if h == nil || host == "" {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	h.mux.RLock()
 | 
						|
	defer h.mux.RUnlock()
 | 
						|
 | 
						|
	for _, h := range h.hosts {
 | 
						|
		if h.Hostname == host {
 | 
						|
			ip = h.IP
 | 
						|
			break
 | 
						|
		}
 | 
						|
		for _, alias := range h.Aliases {
 | 
						|
			if alias == host {
 | 
						|
				ip = h.IP
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if ip != nil && Debug {
 | 
						|
		log.Logf("[hosts] hit: %s %s", host, ip.String())
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// Reload parses config from r, then live reloads the hosts.
 | 
						|
func (h *Hosts) Reload(r io.Reader) error {
 | 
						|
	var period time.Duration
 | 
						|
	var hosts []Host
 | 
						|
 | 
						|
	if r == nil || h.Stopped() {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	scanner := bufio.NewScanner(r)
 | 
						|
	for scanner.Scan() {
 | 
						|
		line := scanner.Text()
 | 
						|
		ss := splitLine(line)
 | 
						|
		if len(ss) < 2 {
 | 
						|
			continue // invalid lines are ignored
 | 
						|
		}
 | 
						|
 | 
						|
		switch ss[0] {
 | 
						|
		case "reload": // reload option
 | 
						|
			period, _ = time.ParseDuration(ss[1])
 | 
						|
		default:
 | 
						|
			ip := net.ParseIP(ss[0])
 | 
						|
			if ip == nil {
 | 
						|
				break // invalid IP addresses are ignored
 | 
						|
			}
 | 
						|
			host := Host{
 | 
						|
				IP:       ip,
 | 
						|
				Hostname: ss[1],
 | 
						|
			}
 | 
						|
			if len(ss) > 2 {
 | 
						|
				host.Aliases = ss[2:]
 | 
						|
			}
 | 
						|
			hosts = append(hosts, host)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := scanner.Err(); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	h.mux.Lock()
 | 
						|
	h.period = period
 | 
						|
	h.hosts = hosts
 | 
						|
	h.mux.Unlock()
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Period returns the reload period
 | 
						|
func (h *Hosts) Period() time.Duration {
 | 
						|
	if h.Stopped() {
 | 
						|
		return -1
 | 
						|
	}
 | 
						|
 | 
						|
	h.mux.RLock()
 | 
						|
	defer h.mux.RUnlock()
 | 
						|
 | 
						|
	return h.period
 | 
						|
}
 | 
						|
 | 
						|
// Stop stops reloading.
 | 
						|
func (h *Hosts) Stop() {
 | 
						|
	select {
 | 
						|
	case <-h.stopped:
 | 
						|
	default:
 | 
						|
		close(h.stopped)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Stopped checks whether the reloader is stopped.
 | 
						|
func (h *Hosts) Stopped() bool {
 | 
						|
	select {
 | 
						|
	case <-h.stopped:
 | 
						|
		return true
 | 
						|
	default:
 | 
						|
		return false
 | 
						|
	}
 | 
						|
}
 |