Update hosts records as a regular interval
1. /etc/hosts will be update cycle instead of update once at the progress run 2. Avoid access redis in each requests
This commit is contained in:
parent
a5a8615b32
commit
81450a3983
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -86,7 +87,8 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
|||||||
|
|
||||||
// Query hosts
|
// Query hosts
|
||||||
if settings.Hosts.Enable && IPQuery > 0 {
|
if settings.Hosts.Enable && IPQuery > 0 {
|
||||||
if ip, ok := h.hosts.Get(Q.qname, IPQuery); ok {
|
if sip, ok := h.hosts.Get(Q.qname); ok {
|
||||||
|
var ip net.IP
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetReply(req)
|
m.SetReply(req)
|
||||||
|
|
||||||
@ -98,6 +100,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
|||||||
Class: dns.ClassINET,
|
Class: dns.ClassINET,
|
||||||
Ttl: settings.Hosts.TTL,
|
Ttl: settings.Hosts.TTL,
|
||||||
}
|
}
|
||||||
|
ip = net.ParseIP(sip).To4()
|
||||||
a := &dns.A{rr_header, ip}
|
a := &dns.A{rr_header, ip}
|
||||||
m.Answer = append(m.Answer, a)
|
m.Answer = append(m.Answer, a)
|
||||||
case _IP6Query:
|
case _IP6Query:
|
||||||
@ -107,6 +110,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
|||||||
Class: dns.ClassINET,
|
Class: dns.ClassINET,
|
||||||
Ttl: settings.Hosts.TTL,
|
Ttl: settings.Hosts.TTL,
|
||||||
}
|
}
|
||||||
|
ip = net.ParseIP(sip).To16()
|
||||||
aaaa := &dns.AAAA{rr_header, ip}
|
aaaa := &dns.AAAA{rr_header, ip}
|
||||||
m.Answer = append(m.Answer, aaaa)
|
m.Answer = append(m.Answer, aaaa)
|
||||||
}
|
}
|
||||||
|
94
hosts.go
94
hosts.go
@ -2,116 +2,98 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hoisie/redis"
|
"github.com/hoisie/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Hosts struct {
|
type Hosts struct {
|
||||||
FileHosts *FileHosts
|
fileHosts *FileHosts
|
||||||
RedisHosts *RedisHosts
|
redisHosts *RedisHosts
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHosts(hs HostsSettings, rs RedisSettings) Hosts {
|
func NewHosts(hs HostsSettings, rs RedisSettings) Hosts {
|
||||||
fileHosts := &FileHosts{hs.HostsFile}
|
fileHosts := &FileHosts{hs.HostsFile, make(map[string]string)}
|
||||||
|
|
||||||
var redisHosts *RedisHosts
|
var redisHosts *RedisHosts
|
||||||
if hs.RedisEnable {
|
if hs.RedisEnable {
|
||||||
rc := &redis.Client{Addr: rs.Addr(), Db: rs.DB, Password: rs.Password}
|
rc := &redis.Client{Addr: rs.Addr(), Db: rs.DB, Password: rs.Password}
|
||||||
redisHosts = &RedisHosts{rc, hs.RedisKey}
|
redisHosts = &RedisHosts{rc, hs.RedisKey, make(map[string]string)}
|
||||||
} else {
|
|
||||||
redisHosts = new(RedisHosts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hosts := Hosts{fileHosts, redisHosts}
|
hosts := Hosts{fileHosts, redisHosts}
|
||||||
|
hosts.refresh()
|
||||||
return hosts
|
return hosts
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
1. Resolve hosts file only one times
|
1. Match local /etc/hosts file first, remote redis records second
|
||||||
2. Request redis on every query called, not found performance lose serious yet.
|
2. Fetch hosts records from /etc/hosts file and redis per minute
|
||||||
3. Match local /etc/hosts file first, remote redis records second
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (h *Hosts) Get(domain string, family int) (ip net.IP, ok bool) {
|
func (h *Hosts) Get(domain string) (ip string, ok bool) {
|
||||||
var sip string
|
|
||||||
|
|
||||||
if sip, ok = h.FileHosts.Get(domain); !ok {
|
if ip, ok = h.fileHosts.Get(domain); ok {
|
||||||
if sip, ok = h.RedisHosts.Get(domain); !ok {
|
return
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch family {
|
if h.redisHosts != nil {
|
||||||
case _IP4Query:
|
ip, ok = h.redisHosts.Get(domain)
|
||||||
ip = net.ParseIP(sip).To4()
|
return
|
||||||
return ip, (ip != nil)
|
|
||||||
case _IP6Query:
|
|
||||||
ip = net.ParseIP(sip).To16()
|
|
||||||
return ip, (ip != nil)
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hosts) GetAll() map[string]string {
|
return ip, false
|
||||||
|
}
|
||||||
|
|
||||||
m := make(map[string]string)
|
func (h *Hosts) refresh() {
|
||||||
for domain, ip := range h.RedisHosts.GetAll() {
|
ticker := time.NewTicker(time.Minute)
|
||||||
m[domain] = ip
|
go func() {
|
||||||
|
for {
|
||||||
|
h.fileHosts.Refresh()
|
||||||
|
if h.redisHosts != nil {
|
||||||
|
h.redisHosts.Refresh()
|
||||||
}
|
}
|
||||||
for domain, ip := range h.FileHosts.GetAll() {
|
<-ticker.C
|
||||||
m[domain] = ip
|
|
||||||
}
|
}
|
||||||
return m
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
type RedisHosts struct {
|
type RedisHosts struct {
|
||||||
redis *redis.Client
|
redis *redis.Client
|
||||||
key string
|
key string
|
||||||
}
|
hosts map[string]string
|
||||||
|
|
||||||
func (r *RedisHosts) GetAll() map[string]string {
|
|
||||||
if r.redis == nil {
|
|
||||||
return map[string]string{}
|
|
||||||
}
|
|
||||||
var hosts = make(map[string]string)
|
|
||||||
r.redis.Hgetall(r.key, hosts)
|
|
||||||
return hosts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RedisHosts) Get(domain string) (ip string, ok bool) {
|
func (r *RedisHosts) Get(domain string) (ip string, ok bool) {
|
||||||
if r.redis == nil {
|
ip, ok = r.hosts[domain]
|
||||||
return "", false
|
return
|
||||||
}
|
|
||||||
b, err := r.redis.Hget(r.key, domain)
|
|
||||||
return string(b), err == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RedisHosts) Set(domain, ip string) (bool, error) {
|
func (r *RedisHosts) Set(domain, ip string) (bool, error) {
|
||||||
if r.redis == nil {
|
|
||||||
return false, errors.New("Redis not enabled")
|
|
||||||
}
|
|
||||||
return r.redis.Hset(r.key, domain, []byte(ip))
|
return r.redis.Hset(r.key, domain, []byte(ip))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RedisHosts) Refresh() {
|
||||||
|
r.redis.Hgetall(r.key, r.hosts)
|
||||||
|
Debug("update hosts records from redis")
|
||||||
|
}
|
||||||
|
|
||||||
type FileHosts struct {
|
type FileHosts struct {
|
||||||
file string
|
file string
|
||||||
|
hosts map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FileHosts) Get(domain string) (ip string, ok bool) {
|
func (f *FileHosts) Get(domain string) (ip string, ok bool) {
|
||||||
hosts := f.GetAll()
|
ip, ok = f.hosts[domain]
|
||||||
ip, ok = hosts[domain]
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FileHosts) GetAll() map[string]string {
|
func (f *FileHosts) Refresh() {
|
||||||
var hosts = make(map[string]string)
|
|
||||||
|
|
||||||
buf, err := os.Open(f.file)
|
buf, err := os.Open(f.file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Can't open " + f.file)
|
panic("Can't open " + f.file)
|
||||||
@ -142,9 +124,9 @@ func (f *FileHosts) GetAll() map[string]string {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
hosts[domain] = ip
|
f.hosts[domain] = ip
|
||||||
}
|
}
|
||||||
return hosts
|
Debug("update hosts records from %s", f.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FileHosts) isDomain(domain string) bool {
|
func (f *FileHosts) isDomain(domain string) bool {
|
||||||
|
Loading…
Reference in New Issue
Block a user