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