mirror of https://github.com/ginuerzh/gost
8 changed files with 24 additions and 304 deletions
@ -1,160 +0,0 @@ |
|||
package gost |
|||
|
|||
import ( |
|||
"bufio" |
|||
"io" |
|||
"net" |
|||
"sync" |
|||
"time" |
|||
|
|||
"github.com/go-gost/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 |
|||
} |
|||
} |
|||
@ -1,130 +0,0 @@ |
|||
package gost |
|||
|
|||
import ( |
|||
"bytes" |
|||
"io" |
|||
"net" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
var hostsLookupTests = []struct { |
|||
hosts []Host |
|||
host string |
|||
ip net.IP |
|||
}{ |
|||
{nil, "", nil}, |
|||
{nil, "example.com", nil}, |
|||
{[]Host{}, "", nil}, |
|||
{[]Host{}, "example.com", nil}, |
|||
{[]Host{NewHost(nil, "")}, "", nil}, |
|||
{[]Host{NewHost(nil, "example.com")}, "example.com", nil}, |
|||
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "")}, "", nil}, |
|||
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com")}, "example.com", net.IPv4(192, 168, 1, 1)}, |
|||
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com")}, "example", nil}, |
|||
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com", "example", "examples")}, "example", net.IPv4(192, 168, 1, 1)}, |
|||
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com", "example", "examples")}, "examples", net.IPv4(192, 168, 1, 1)}, |
|||
} |
|||
|
|||
func TestHostsLookup(t *testing.T) { |
|||
for i, tc := range hostsLookupTests { |
|||
hosts := NewHosts() |
|||
hosts.AddHost(tc.hosts...) |
|||
ip := hosts.Lookup(tc.host) |
|||
if !ip.Equal(tc.ip) { |
|||
t.Errorf("#%d test failed: lookup should be %s, got %s", i, tc.ip, ip) |
|||
} |
|||
} |
|||
} |
|||
|
|||
var HostsReloadTests = []struct { |
|||
r io.Reader |
|||
period time.Duration |
|||
host string |
|||
ip net.IP |
|||
stopped bool |
|||
}{ |
|||
{ |
|||
r: nil, |
|||
period: 0, |
|||
host: "", |
|||
ip: nil, |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString(""), |
|||
period: 0, |
|||
host: "example.com", |
|||
ip: nil, |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString("reload 10s"), |
|||
period: 10 * time.Second, |
|||
host: "example.com", |
|||
ip: nil, |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString("#reload 10s\ninvalid.ip.addr example.com"), |
|||
period: 0, |
|||
ip: nil, |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString("reload 10s\n192.168.1.1"), |
|||
period: 10 * time.Second, |
|||
host: "", |
|||
ip: nil, |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString("#reload 10s\n192.168.1.1 example.com"), |
|||
period: 0, |
|||
host: "example.com", |
|||
ip: net.IPv4(192, 168, 1, 1), |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString("#reload 10s\n#192.168.1.1 example.com"), |
|||
period: 0, |
|||
host: "example.com", |
|||
ip: nil, |
|||
stopped: true, |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString("#reload 10s\n192.168.1.1 example.com example examples"), |
|||
period: 0, |
|||
host: "example", |
|||
ip: net.IPv4(192, 168, 1, 1), |
|||
stopped: true, |
|||
}, |
|||
{ |
|||
r: bytes.NewBufferString("#reload 10s\n192.168.1.1 example.com example examples"), |
|||
period: 0, |
|||
host: "examples", |
|||
ip: net.IPv4(192, 168, 1, 1), |
|||
stopped: true, |
|||
}, |
|||
} |
|||
|
|||
func TestHostsReload(t *testing.T) { |
|||
for i, tc := range HostsReloadTests { |
|||
hosts := NewHosts() |
|||
if err := hosts.Reload(tc.r); err != nil { |
|||
t.Error(err) |
|||
} |
|||
if hosts.Period() != tc.period { |
|||
t.Errorf("#%d test failed: period value should be %v, got %v", |
|||
i, tc.period, hosts.Period()) |
|||
} |
|||
ip := hosts.Lookup(tc.host) |
|||
if !ip.Equal(tc.ip) { |
|||
t.Errorf("#%d test failed: lookup should be %s, got %s", i, tc.ip, ip) |
|||
} |
|||
if tc.stopped { |
|||
hosts.Stop() |
|||
if hosts.Period() >= 0 { |
|||
t.Errorf("period of the stopped reloader should be minus value") |
|||
} |
|||
} |
|||
if hosts.Stopped() != tc.stopped { |
|||
t.Errorf("#%d test failed: stopped value should be %v, got %v", |
|||
i, tc.stopped, hosts.Stopped()) |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue