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
|
|
}
|
|
}
|