Redo settings a bit, move a lot of init logic to main.go
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
991ae3ecb5
commit
f726a5d5ae
|
@ -7,7 +7,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func NewRedisCache(c settings.RedisSettings, expire int32) *RedisCache {
|
||||
func NewRedisCache(c settings.RedisSettings, expire time.Duration) *RedisCache {
|
||||
rc := redis.NewClient(&redis.Options{Addr: c.Addr(), DB: c.DB, Password: c.Password})
|
||||
|
||||
return &RedisCache{
|
||||
|
@ -20,7 +20,7 @@ type RedisCache struct {
|
|||
Cache
|
||||
|
||||
backend *redis.Client
|
||||
expire int32
|
||||
expire time.Duration
|
||||
}
|
||||
|
||||
func (m *RedisCache) Set(key string, msg *dns.Msg) error {
|
||||
|
@ -38,7 +38,7 @@ func (m *RedisCache) Set(key string, msg *dns.Msg) error {
|
|||
if err != nil {
|
||||
err = SerializerError{err}
|
||||
}
|
||||
return m.backend.Set(key, val, time.Duration(m.expire) * time.Second).Err()
|
||||
return m.backend.Set(key, val, m.expire).Err()
|
||||
}
|
||||
|
||||
func (m *RedisCache) Get(key string) (*dns.Msg, error) {
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
server=/adcdownload.apple.com/114.114.114.114
|
||||
server=/appldnld.apple.com/114.114.114.114
|
||||
server=/cdn-cn1.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn-cn2.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn-cn3.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn-cn4.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn1.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn2.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn3.apple-mapkit.com/114.114.114.114
|
||||
server=/cdn4.apple-mapkit.com/114.114.114.114
|
||||
server=/cds.apple.com/114.114.114.114
|
||||
server=/cl1.apple.com/114.114.114.114
|
||||
server=/cl2.apple.com.edgekey.net.globalredir.akadns.net/114.114.114.114
|
||||
server=/cl2.apple.com.edgekey.net/114.114.114.114
|
||||
server=/cl2.apple.com/114.114.114.114
|
||||
server=/cl3.apple.com/114.114.114.114
|
||||
server=/cl4.apple.com/114.114.114.114
|
||||
server=/cl5.apple.com/114.114.114.114
|
||||
server=/gsp11-cn.ls.apple.com/114.114.114.114
|
||||
server=/gsp12-cn.ls.apple.com/114.114.114.114
|
||||
server=/gsp13-cn.ls.apple.com/114.114.114.114
|
||||
server=/gsp4-cn.ls.apple.com.edgekey.net.globalredir.akadns.net/114.114.114.114
|
||||
server=/gsp4-cn.ls.apple.com.edgekey.net/114.114.114.114
|
||||
server=/gsp4-cn.ls.apple.com/114.114.114.114
|
||||
server=/gsp5-cn.ls.apple.com/114.114.114.114
|
||||
server=/gspe19-cn.ls-apple.com.akadns.net/114.114.114.114
|
||||
server=/gspe19-cn.ls.apple.com/114.114.114.114
|
||||
server=/gspe21.ls.apple.com/114.114.114.114
|
||||
server=/gspe21-ssl.ls.apple.com/114.114.114.114
|
||||
server=/gspe35-ssl.ls.apple.com/114.114.114.114
|
||||
server=/icloud.cdn-apple.com/114.114.114.114
|
||||
server=/images.apple.com/114.114.114.114
|
||||
server=/itunes-apple.com.akadns.net/114.114.114.114
|
||||
server=/itunes.apple.com/114.114.114.114
|
||||
server=/itunesconnect.apple.com/114.114.114.114
|
||||
server=/mesu.apple.com/114.114.114.114
|
||||
server=/mesu-china.apple.com.akadns.net/114.114.114.114
|
||||
server=/phobos-apple.com.akadns.net/114.114.114.114
|
||||
server=/phobos.apple.com/114.114.114.114
|
||||
server=/store.apple.com/114.114.114.114
|
||||
server=/store.storeimages.cdn-apple.com/114.114.114.114
|
||||
server=/support.apple.com/114.114.114.114
|
||||
server=/swcdn.apple.com/114.114.114.114
|
||||
server=/swdist.apple.com/114.114.114.114
|
||||
server=/www.apple.com/114.114.114.114
|
|
@ -1,58 +1,48 @@
|
|||
#Toml config file
|
||||
|
||||
|
||||
Title = "GODNS"
|
||||
Version = "0.2.0"
|
||||
Author = "kenshin, tystuyfzand"
|
||||
|
||||
Debug = false
|
||||
debug = false
|
||||
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
nets = ["tcp:53", "udp:53"]
|
||||
|
||||
[resolv]
|
||||
# Domain-specific nameservers configuration, formatting keep compatible with Dnsmasq
|
||||
# Semicolon separate multiple files.
|
||||
resolv-file = ""
|
||||
file = ""
|
||||
timeout = 5 # 5 seconds
|
||||
# The concurrency interval request upstream recursive server
|
||||
# Match the PR15, https://github.com/kenshinx/godns/pull/15
|
||||
interval = 200 # 200 milliseconds
|
||||
setedns0 = false #Support for larger UDP DNS responses
|
||||
server-list-file = "etc/serverlist.conf"
|
||||
|
||||
[redis]
|
||||
enable = true
|
||||
host = "192.168.1.71"
|
||||
port = 6379
|
||||
db = 0
|
||||
password =""
|
||||
|
||||
[memcache]
|
||||
servers = ["127.0.0.1:11211"]
|
||||
edns0 = false #Support for larger UDP DNS responses
|
||||
# Domain-specific nameservers configuration, formatting keep compatible with Dnsmasq
|
||||
server-list = "etc/serverlist.conf"
|
||||
|
||||
[log]
|
||||
stdout = true
|
||||
file = "./godns.log"
|
||||
level = "DEBUG" #DEBUG | INFO |NOTICE | WARN | ERROR
|
||||
|
||||
|
||||
|
||||
[cache]
|
||||
# backend option [memory|memcache|redis]
|
||||
# redis backend not implemented yet
|
||||
backend = "memory"
|
||||
expire = 600 # 10 minutes
|
||||
maxcount = 0 #If set zero. The Sum of cache itmes will be unlimit.
|
||||
|
||||
[cache.memcached]
|
||||
servers = ["127.0.0.1:11211"]
|
||||
|
||||
# Redis cache backend config
|
||||
[cache.redis]
|
||||
host = "192.168.1.71"
|
||||
port = 6379
|
||||
db = 0
|
||||
password =""
|
||||
|
||||
[hosts]
|
||||
#If set false, will not query hosts file and redis hosts record
|
||||
# File provider, supports inotify hot-reload
|
||||
[hosts.file]
|
||||
enable = true
|
||||
host-file = "/etc/hosts"
|
||||
redis-enable = true
|
||||
redis-key = "godns:hosts"
|
||||
file = "/etc/hosts"
|
||||
ttl = 600
|
||||
refresh-interval = 60 # 5 seconds
|
||||
|
||||
|
||||
# Redis provider
|
||||
[hosts.redis]
|
||||
enable = true
|
||||
key = "godns:hosts"
|
||||
ttl = 600
|
|
@ -1,42 +0,0 @@
|
|||
server=/265.com/114.114.114.114
|
||||
server=/2mdn.net/114.114.114.114
|
||||
server=/app-measurement.com/114.114.114.114
|
||||
server=/beacons.gcp.gvt2.com/114.114.114.114
|
||||
server=/beacons.gvt2.com/114.114.114.114
|
||||
server=/beacons3.gvt2.com/114.114.114.114
|
||||
server=/c.admob.com/114.114.114.114
|
||||
server=/c.android.clients.google.com/114.114.114.114
|
||||
server=/cache.pack.google.com/114.114.114.114
|
||||
server=/clientservices.googleapis.com/114.114.114.114
|
||||
server=/connectivitycheck.gstatic.com/114.114.114.114
|
||||
server=/csi.gstatic.com/114.114.114.114
|
||||
server=/dl.google.com/114.114.114.114
|
||||
server=/doubleclick.net/114.114.114.114
|
||||
server=/e.admob.com/114.114.114.114
|
||||
server=/fonts.googleapis.com/114.114.114.114
|
||||
server=/fonts.gstatic.com/114.114.114.114
|
||||
server=/google-analytics.com/114.114.114.114
|
||||
server=/googleadservices.com/114.114.114.114
|
||||
server=/googleanalytics.com/114.114.114.114
|
||||
server=/googlesyndication.com/114.114.114.114
|
||||
server=/googletagmanager.com/114.114.114.114
|
||||
server=/googletagservices.com/114.114.114.114
|
||||
server=/imasdk.googleapis.com/114.114.114.114
|
||||
server=/kh.google.com/114.114.114.114
|
||||
server=/khm.google.com/114.114.114.114
|
||||
server=/khm.googleapis.com/114.114.114.114
|
||||
server=/khm0.google.com/114.114.114.114
|
||||
server=/khm0.googleapis.com/114.114.114.114
|
||||
server=/khm1.google.com/114.114.114.114
|
||||
server=/khm1.googleapis.com/114.114.114.114
|
||||
server=/khm2.google.com/114.114.114.114
|
||||
server=/khm2.googleapis.com/114.114.114.114
|
||||
server=/khm3.google.com/114.114.114.114
|
||||
server=/khm3.googleapis.com/114.114.114.114
|
||||
server=/khmdb.google.com/114.114.114.114
|
||||
server=/khmdb.googleapis.com/114.114.114.114
|
||||
server=/media.admob.com/114.114.114.114
|
||||
server=/mediavisor.doubleclick.com/114.114.114.114
|
||||
server=/redirector.gvt1.com/114.114.114.114
|
||||
server=/toolbarqueries.google.com/114.114.114.114
|
||||
server=/update.googleapis.com/114.114.114.114
|
59
handler.go
59
handler.go
|
@ -3,16 +3,14 @@ package main
|
|||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"meow.tf/joker/godns/cache"
|
||||
"meow.tf/joker/godns/hosts"
|
||||
"meow.tf/joker/godns/log"
|
||||
"meow.tf/joker/godns/resolver"
|
||||
"meow.tf/joker/godns/settings"
|
||||
"meow.tf/joker/godns/utils"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -21,54 +19,17 @@ const (
|
|||
_IP6Query = 6
|
||||
)
|
||||
|
||||
type GODNSHandler struct {
|
||||
type Handler struct {
|
||||
resolver *resolver.Resolver
|
||||
cache, negCache cache.Cache
|
||||
hosts hosts.Hosts
|
||||
}
|
||||
|
||||
func NewHandler() *GODNSHandler {
|
||||
|
||||
var (
|
||||
cacheConfig settings.CacheSettings
|
||||
r *resolver.Resolver
|
||||
resolverCache, negCache cache.Cache
|
||||
)
|
||||
|
||||
r = resolver.NewResolver(settings.Resolver())
|
||||
|
||||
cacheConfig = settings.Cache()
|
||||
switch cacheConfig.Backend {
|
||||
case "memory":
|
||||
cacheDuration := time.Duration(cacheConfig.Expire) * time.Second
|
||||
|
||||
negCache = cache.NewMemoryCache(cacheDuration/2, cacheConfig.Maxcount)
|
||||
resolverCache = cache.NewMemoryCache(time.Duration(cacheConfig.Expire)*time.Second, cacheConfig.Maxcount)
|
||||
case "memcache":
|
||||
resolverCache = cache.NewMemcachedCache(
|
||||
settings.Memcache().Servers,
|
||||
int32(cacheConfig.Expire))
|
||||
negCache = cache.NewMemcachedCache(
|
||||
settings.Memcache().Servers,
|
||||
int32(cacheConfig.Expire/2))
|
||||
case "redis":
|
||||
resolverCache = cache.NewRedisCache(
|
||||
settings.Redis(),
|
||||
int32(cacheConfig.Expire))
|
||||
negCache = cache.NewRedisCache(
|
||||
settings.Redis(),
|
||||
int32(cacheConfig.Expire/2))
|
||||
default:
|
||||
log.Error("Invalid cache backend %s", cacheConfig.Backend)
|
||||
panic("Invalid cache backend")
|
||||
func NewHandler(r *resolver.Resolver, resolverCache, negCache cache.Cache, h hosts.Hosts) *Handler {
|
||||
return &Handler{r, resolverCache, negCache, h}
|
||||
}
|
||||
|
||||
h := hosts.NewHosts(settings.Hosts(), settings.Redis())
|
||||
|
||||
return &GODNSHandler{r, resolverCache, negCache, h}
|
||||
}
|
||||
|
||||
func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
||||
func (h *Handler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
||||
q := req.Question[0]
|
||||
question := resolver.Question{Name: utils.UnFqdn(q.Name), Type: dns.TypeToString[q.Qtype], Class: dns.ClassToString[q.Qclass]}
|
||||
|
||||
|
@ -84,7 +45,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
|||
|
||||
// Query hosts
|
||||
if h.hosts != nil && IPQuery > 0 {
|
||||
if ips, ok := h.hosts.Get(question.Name, IPQuery); ok {
|
||||
if ips, ttl, ok := h.hosts.Get(question.Name, IPQuery); ok {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(req)
|
||||
|
||||
|
@ -94,7 +55,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
|||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: h.hosts.TTL(),
|
||||
Ttl: uint32(ttl / time.Second),
|
||||
}
|
||||
for _, ip := range ips {
|
||||
m.Answer = append(m.Answer, &dns.A{Hdr: hdr, A: ip})
|
||||
|
@ -104,7 +65,7 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
|||
Name: q.Name,
|
||||
Rrtype: dns.TypeAAAA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: h.hosts.TTL(),
|
||||
Ttl: uint32(ttl / time.Second),
|
||||
}
|
||||
for _, ip := range ips {
|
||||
m.Answer = append(m.Answer, &dns.AAAA{Hdr: hdr, AAAA: ip})
|
||||
|
@ -165,13 +126,13 @@ func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *GODNSHandler) Bind(net string) func(w dns.ResponseWriter, req *dns.Msg) {
|
||||
func (h *Handler) Bind(net string) func(w dns.ResponseWriter, req *dns.Msg) {
|
||||
return func(w dns.ResponseWriter, req *dns.Msg) {
|
||||
h.do(net, w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *GODNSHandler) isIPQuery(q dns.Question) int {
|
||||
func (h *Handler) isIPQuery(q dns.Question) int {
|
||||
if q.Qclass != dns.ClassINET {
|
||||
return notIPQuery
|
||||
}
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
package hosts
|
||||
|
||||
import (
|
||||
"meow.tf/joker/godns/log"
|
||||
"meow.tf/joker/godns/settings"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/hoisie/redis"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -15,70 +11,38 @@ const (
|
|||
_IP6Query = 6
|
||||
)
|
||||
|
||||
var (
|
||||
zeroDuration = time.Duration(0)
|
||||
)
|
||||
|
||||
type Hosts interface {
|
||||
Get(domain string, family int) ([]net.IP, bool)
|
||||
TTL() uint32
|
||||
Get(domain string, family int) ([]net.IP, time.Duration, bool)
|
||||
}
|
||||
|
||||
type ProviderList struct {
|
||||
settings settings.HostsSettings
|
||||
providers []HostProvider
|
||||
refreshInterval time.Duration
|
||||
providers []Provider
|
||||
}
|
||||
|
||||
type HostProvider interface {
|
||||
Get(domain string) ([]string, bool)
|
||||
Refresh()
|
||||
type Provider interface {
|
||||
Get(domain string) ([]string, time.Duration, bool)
|
||||
}
|
||||
|
||||
func NewHosts(hs settings.HostsSettings, rs settings.RedisSettings) Hosts {
|
||||
providers := []HostProvider{
|
||||
NewFileProvider(hs.HostsFile),
|
||||
}
|
||||
|
||||
if hs.RedisEnable {
|
||||
log.Info("Redis is enabled: %s", rs.Addr())
|
||||
|
||||
rc := &redis.Client{Addr: rs.Addr(), Db: rs.DB, Password: rs.Password}
|
||||
|
||||
providers = append(providers, NewRedisProvider(rc, hs.RedisKey))
|
||||
}
|
||||
|
||||
h := &ProviderList{hs, providers, time.Second * time.Duration(hs.RefreshInterval)}
|
||||
|
||||
if h.refreshInterval > 0 {
|
||||
h.refresh()
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *ProviderList) refresh() {
|
||||
ticker := time.NewTicker(h.refreshInterval)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
// Force a refresh every refreshInterval
|
||||
for _, provider := range h.providers {
|
||||
provider.Refresh()
|
||||
}
|
||||
|
||||
<-ticker.C
|
||||
}
|
||||
}()
|
||||
func NewHosts(providers []Provider) Hosts {
|
||||
return &ProviderList{providers}
|
||||
}
|
||||
|
||||
/*
|
||||
Match local /etc/hosts file first, remote redis records second
|
||||
*/
|
||||
func (h *ProviderList) Get(domain string, family int) ([]net.IP, bool) {
|
||||
func (h *ProviderList) Get(domain string, family int) ([]net.IP, time.Duration, bool) {
|
||||
var sips []string
|
||||
var ok bool
|
||||
var ip net.IP
|
||||
var ips []net.IP
|
||||
var ttl time.Duration
|
||||
|
||||
for _, provider := range h.providers {
|
||||
sips, ok = provider.Get(domain)
|
||||
sips, ttl, ok = provider.Get(domain)
|
||||
|
||||
if ok {
|
||||
break
|
||||
|
@ -86,7 +50,7 @@ func (h *ProviderList) Get(domain string, family int) ([]net.IP, bool) {
|
|||
}
|
||||
|
||||
if sips == nil {
|
||||
return nil, false
|
||||
return nil, zeroDuration, false
|
||||
}
|
||||
|
||||
for _, sip := range sips {
|
||||
|
@ -103,9 +67,5 @@ func (h *ProviderList) Get(domain string, family int) ([]net.IP, bool) {
|
|||
}
|
||||
}
|
||||
|
||||
return ips, ips != nil
|
||||
}
|
||||
|
||||
func (h *ProviderList) TTL() uint32 {
|
||||
return h.settings.TTL
|
||||
return ips, ttl, ips != nil
|
||||
}
|
|
@ -10,20 +10,23 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FileHosts struct {
|
||||
HostProvider
|
||||
Provider
|
||||
|
||||
file string
|
||||
hosts map[string]string
|
||||
mu sync.RWMutex
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func NewFileProvider(file string) HostProvider {
|
||||
func NewFileProvider(file string, ttl time.Duration) Provider {
|
||||
fp := &FileHosts{
|
||||
file: file,
|
||||
hosts: make(map[string]string),
|
||||
ttl: ttl,
|
||||
}
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
|
@ -46,7 +49,7 @@ func NewFileProvider(file string) HostProvider {
|
|||
return fp
|
||||
}
|
||||
|
||||
func (f *FileHosts) Get(domain string) ([]string, bool) {
|
||||
func (f *FileHosts) Get(domain string) ([]string, time.Duration, bool) {
|
||||
log.Debug("Checking file provider for %s", domain)
|
||||
|
||||
f.mu.RLock()
|
||||
|
@ -54,24 +57,24 @@ func (f *FileHosts) Get(domain string) ([]string, bool) {
|
|||
domain = strings.ToLower(domain)
|
||||
|
||||
if ip, ok := f.hosts[domain]; ok {
|
||||
return strings.Split(ip, ","), true
|
||||
return strings.Split(ip, ","), f.ttl, true
|
||||
}
|
||||
|
||||
if idx := strings.Index(domain, "."); idx != -1 {
|
||||
wildcard := "*." + domain[strings.Index(domain, ".") + 1:]
|
||||
|
||||
if ip, ok := f.hosts[wildcard]; ok {
|
||||
return strings.Split(ip, ","), true
|
||||
return strings.Split(ip, ","), f.ttl, true
|
||||
}
|
||||
}
|
||||
|
||||
for host, ip := range f.hosts {
|
||||
if glob.Glob(host, domain) {
|
||||
return strings.Split(ip, ","), true
|
||||
return strings.Split(ip, ","), f.ttl, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
return nil, time.Duration(0), false
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -1,83 +1,39 @@
|
|||
package hosts
|
||||
|
||||
import (
|
||||
"github.com/hoisie/redis"
|
||||
"github.com/go-redis/redis/v7"
|
||||
"github.com/ryanuber/go-glob"
|
||||
"meow.tf/joker/godns/log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RedisHosts struct {
|
||||
HostProvider
|
||||
Provider
|
||||
|
||||
redis *redis.Client
|
||||
key string
|
||||
hosts map[string]string
|
||||
mu sync.RWMutex
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
func NewRedisProvider(rc *redis.Client, key string) HostProvider {
|
||||
func NewRedisProvider(rc *redis.Client, key string, ttl time.Duration) Provider {
|
||||
rh := &RedisHosts{
|
||||
redis: rc,
|
||||
key: key,
|
||||
hosts: make(map[string]string),
|
||||
ttl: ttl,
|
||||
}
|
||||
|
||||
// Force an initial refresh
|
||||
rh.Refresh()
|
||||
|
||||
// Use pubsub to listen for key update events
|
||||
go func() {
|
||||
keyspaceEvent := "__keyspace@0__:" + key
|
||||
|
||||
sub := make(chan string, 3)
|
||||
sub <- keyspaceEvent
|
||||
sub <- "godns:update"
|
||||
sub <- "godns:update_record"
|
||||
messages := make(chan redis.Message, 0)
|
||||
go rc.Subscribe(sub, nil, nil, nil, messages)
|
||||
|
||||
for {
|
||||
msg := <-messages
|
||||
|
||||
if msg.Channel == "godns:update" {
|
||||
log.Debug("Refreshing redis records due to update")
|
||||
rh.Refresh()
|
||||
} else if msg.Channel == "godns:update_record" {
|
||||
recordName := string(msg.Message)
|
||||
|
||||
b, err := rc.Hget(key, recordName)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Record %s does not exist, but was updated", recordName)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("Record %s was updated to %s", recordName, string(b))
|
||||
|
||||
rh.mu.Lock()
|
||||
rh.hosts[recordName] = string(b)
|
||||
rh.mu.Unlock()
|
||||
} else if msg.Channel == "godns:remove_record" {
|
||||
log.Debug("Record %s was removed", msg.Message)
|
||||
|
||||
recordName := string(msg.Message)
|
||||
|
||||
rh.mu.Lock()
|
||||
delete(rh.hosts, recordName)
|
||||
rh.mu.Unlock()
|
||||
} else if msg.Channel == keyspaceEvent {
|
||||
log.Debug("Refreshing redis records due to update")
|
||||
rh.Refresh()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return rh
|
||||
}
|
||||
|
||||
func (r *RedisHosts) Get(domain string) ([]string, bool) {
|
||||
func (r *RedisHosts) Get(domain string) ([]string, time.Duration, bool) {
|
||||
log.Debug("Checking redis provider for %s", domain)
|
||||
|
||||
r.mu.RLock()
|
||||
|
@ -86,37 +42,39 @@ func (r *RedisHosts) Get(domain string) ([]string, bool) {
|
|||
domain = strings.ToLower(domain)
|
||||
|
||||
if ip, ok := r.hosts[domain]; ok {
|
||||
return strings.Split(ip, ","), true
|
||||
return strings.Split(ip, ","), r.ttl, true
|
||||
}
|
||||
|
||||
if idx := strings.Index(domain, "."); idx != -1 {
|
||||
wildcard := "*." + domain[strings.Index(domain, ".")+1:]
|
||||
|
||||
if ip, ok := r.hosts[wildcard]; ok {
|
||||
return strings.Split(ip, ","), true
|
||||
return strings.Split(ip, ","), r.ttl, true
|
||||
}
|
||||
}
|
||||
|
||||
for host, ip := range r.hosts {
|
||||
if glob.Glob(host, domain) {
|
||||
return strings.Split(ip, ","), true
|
||||
return strings.Split(ip, ","), r.ttl, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
return nil, time.Duration(0), false
|
||||
}
|
||||
|
||||
func (r *RedisHosts) Set(domain, ip string) (bool, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
return r.redis.Hset(r.key, strings.ToLower(domain), []byte(ip))
|
||||
return r.redis.HSet(r.key, strings.ToLower(domain), []byte(ip)).Result()
|
||||
}
|
||||
|
||||
func (r *RedisHosts) Refresh() {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.clear()
|
||||
err := r.redis.Hgetall(r.key, r.hosts)
|
||||
|
||||
var err error
|
||||
r.hosts, err = r.redis.HGetAll(r.key).Result()
|
||||
if err != nil {
|
||||
log.Warn("Update hosts records from redis failed %s", err)
|
||||
} else {
|
||||
|
|
80
main.go
80
main.go
|
@ -3,8 +3,12 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v7"
|
||||
"github.com/spf13/viper"
|
||||
"meow.tf/joker/godns/cache"
|
||||
"meow.tf/joker/godns/hosts"
|
||||
"meow.tf/joker/godns/log"
|
||||
"meow.tf/joker/godns/resolver"
|
||||
"meow.tf/joker/godns/settings"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
@ -36,29 +40,79 @@ func main() {
|
|||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
|
||||
serverSettings := settings.Server()
|
||||
|
||||
server := &Server{
|
||||
host: serverSettings.Host,
|
||||
port: serverSettings.Port,
|
||||
host: viper.GetString("server.host"),
|
||||
networks: viper.GetStringSlice("server.nets"),
|
||||
rTimeout: 5 * time.Second,
|
||||
wTimeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
server.Run()
|
||||
var resolverCache, negCache cache.Cache
|
||||
r := resolver.NewResolver(settings.ResolvSettings{
|
||||
Timeout: viper.GetInt("resolv.timeout"),
|
||||
Interval: viper.GetInt("resolv.interval"),
|
||||
SetEDNS0: viper.GetBool("resolv.edns0"),
|
||||
ServerListFile: viper.GetStringSlice("resolv.server-list"),
|
||||
ResolvFile: viper.GetString("resolv.file"),
|
||||
})
|
||||
|
||||
log.Info("godns %s (%s) start", Version, runtime.Version())
|
||||
cacheDuration := viper.GetDuration("cache.expire")
|
||||
|
||||
if settings.Debug() {
|
||||
redisConfig := settings.RedisSettings{
|
||||
Host: viper.GetString("cache.redis.host"),
|
||||
Port: viper.GetInt("cache.redis.port"),
|
||||
DB: viper.GetInt("cache.redis.db"),
|
||||
Password: viper.GetString("cache.redis.password"),
|
||||
}
|
||||
|
||||
backend := viper.GetString("cache.backend")
|
||||
|
||||
switch backend {
|
||||
case "memory":
|
||||
cacheMaxCount := viper.GetInt("cache.memory.maxCount")
|
||||
|
||||
negCache = cache.NewMemoryCache(cacheDuration/2, cacheMaxCount)
|
||||
resolverCache = cache.NewMemoryCache(cacheDuration, cacheMaxCount)
|
||||
case "memcached":
|
||||
servers := viper.GetStringSlice("cache.memcached.servers")
|
||||
|
||||
resolverCache = cache.NewMemcachedCache(servers, int32(cacheDuration.Seconds()))
|
||||
negCache = cache.NewMemcachedCache(servers, int32(cacheDuration.Seconds()/2))
|
||||
case "redis":
|
||||
resolverCache = cache.NewRedisCache(redisConfig, cacheDuration)
|
||||
negCache = cache.NewRedisCache(redisConfig, cacheDuration/2)
|
||||
default:
|
||||
log.Error("Invalid cache backend %s", backend)
|
||||
panic("Invalid cache backend")
|
||||
}
|
||||
|
||||
providers := make([]hosts.Provider, 0)
|
||||
|
||||
if viper.GetBool("hosts.file.enable") {
|
||||
providers = append(providers, hosts.NewFileProvider(viper.GetString("hosts.file.file"), viper.GetDuration("hosts.file.ttl")))
|
||||
}
|
||||
|
||||
if viper.GetBool("hosts.redis.enable") {
|
||||
rc := redis.NewClient(&redis.Options{Addr: redisConfig.Addr(), DB: redisConfig.DB, Password: redisConfig.Password})
|
||||
|
||||
providers = append(providers, hosts.NewRedisProvider(rc, viper.GetString("hosts.redis.key"), viper.GetDuration("hosts.redis.ttl")))
|
||||
}
|
||||
|
||||
h := hosts.NewHosts(providers)
|
||||
|
||||
server.Run(NewHandler(r, resolverCache, negCache, h))
|
||||
|
||||
log.Info("godns %s (%s)", Version, runtime.Version())
|
||||
|
||||
if viper.GetBool("debug") {
|
||||
go profileCPU()
|
||||
go profileMEM()
|
||||
}
|
||||
|
||||
sig := make(chan os.Signal)
|
||||
signal.Notify(sig, os.Interrupt)
|
||||
|
||||
|
||||
<- sig
|
||||
|
||||
log.Info("signal received, stopping")
|
||||
}
|
||||
|
||||
|
@ -93,8 +147,6 @@ func profileMEM() {
|
|||
}
|
||||
|
||||
func initLogger() {
|
||||
logSettings := settings.Log()
|
||||
|
||||
if viper.GetBool("log.stdout") {
|
||||
log.SetLogger("console", nil)
|
||||
}
|
||||
|
@ -103,9 +155,5 @@ func initLogger() {
|
|||
log.SetLogger("file", map[string]interface{}{"file": file})
|
||||
}
|
||||
|
||||
log.SetLevel(logSettings.LogLevel())
|
||||
}
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
log.SetLevel(settings.LogLevelFor(viper.GetString("log.level")))
|
||||
}
|
||||
|
|
|
@ -68,10 +68,6 @@ func NewResolver(c settings.ResolvSettings) *Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
if len(c.DOHServer) > 0 {
|
||||
r.servers = append([]string{c.DOHServer}, r.servers...)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -141,8 +137,7 @@ func (r *Resolver) parseServerListFile(buf *os.File) {
|
|||
|
||||
}
|
||||
|
||||
func (r *Resolver) ReadServerListFile(path string) {
|
||||
files := strings.Split(path, ";")
|
||||
func (r *Resolver) ReadServerListFile(files []string) {
|
||||
for _, file := range files {
|
||||
buf, err := os.Open(file)
|
||||
if err != nil {
|
||||
|
|
11
server.go
11
server.go
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"meow.tf/joker/godns/log"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -11,21 +10,17 @@ import (
|
|||
|
||||
type Server struct {
|
||||
host string
|
||||
port int
|
||||
networks []string
|
||||
rTimeout time.Duration
|
||||
wTimeout time.Duration
|
||||
}
|
||||
|
||||
func (s *Server) Run() {
|
||||
handler := NewHandler()
|
||||
|
||||
nets := viper.GetStringSlice("networks")
|
||||
|
||||
func (s *Server) Run(handler *Handler) {
|
||||
var addr string
|
||||
var split []string
|
||||
|
||||
// Defaults: tcp, udp
|
||||
for _, net := range nets {
|
||||
for _, net := range s.networks {
|
||||
split = strings.Split(net, ":")
|
||||
|
||||
net = split[0]
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
package settings
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"meow.tf/joker/godns/log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/caarlos0/env"
|
||||
)
|
||||
|
||||
var (
|
||||
settings Settings
|
||||
)
|
||||
|
||||
var LogLevelMap = map[string]int{
|
||||
|
@ -24,7 +14,6 @@ var LogLevelMap = map[string]int{
|
|||
}
|
||||
|
||||
type HostsSettings struct {
|
||||
Enable bool `toml:"enable" env:"HOSTS_ENABLE"`
|
||||
HostsFile string `toml:"host-file" env:"HOSTS_FILE"`
|
||||
RedisEnable bool `toml:"redis-enable" env:"REDIS_HOSTS_ENABLE"`
|
||||
RedisKey string `toml:"redis-key" env:"REDIS_HOSTS_KEY"`
|
||||
|
@ -48,9 +37,8 @@ type ResolvSettings struct {
|
|||
Timeout int `toml:"timeout" env:"RESOLV_TIMEOUT"`
|
||||
Interval int `toml:"interval" env:"RESOLV_INTERVAL"`
|
||||
SetEDNS0 bool `toml:"setedns0" env:"RESOLV_EDNS0"`
|
||||
ServerListFile string `toml:"server-list-file" env:"SERVER_LIST_FILE"`
|
||||
ServerListFile []string `toml:"server-list-file" env:"SERVER_LIST_FILE"`
|
||||
ResolvFile string `toml:"resolv-file" env:"RESOLV_FILE"`
|
||||
DOHServer string `toml:"dns-over-https" env:"DNS_HTTPS_SERVER"`
|
||||
}
|
||||
|
||||
type DNSServerSettings struct {
|
||||
|
@ -79,10 +67,10 @@ type LogSettings struct {
|
|||
Level string `toml:"level" env:"LOG_LEVEL"`
|
||||
}
|
||||
|
||||
func (ls LogSettings) LogLevel() int {
|
||||
l, ok := LogLevelMap[ls.Level]
|
||||
func LogLevelFor(level string) int {
|
||||
l, ok := LogLevelMap[level]
|
||||
if !ok {
|
||||
panic("Config error: invalid log level: " + ls.Level)
|
||||
panic("Config error: invalid log level: " + level)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
@ -92,55 +80,3 @@ type CacheSettings struct {
|
|||
Expire int `toml:"expire" env:"CACHE_EXPIRE"`
|
||||
Maxcount int `toml:"maxcount" env:"CACHE_MAX_COUNT"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
var configFile string
|
||||
|
||||
flag.StringVar(&configFile, "c", "/etc/godns.conf", "Look for godns toml-formatting config file in this directory")
|
||||
flag.Parse()
|
||||
|
||||
if _, err := toml.DecodeFile(configFile, &settings); err != nil {
|
||||
fmt.Printf("%s is not a valid toml config file\n", configFile)
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
env.Parse(&settings.ResolvConfig)
|
||||
env.Parse(&settings.Redis)
|
||||
env.Parse(&settings.Memcache)
|
||||
env.Parse(&settings.Log)
|
||||
env.Parse(&settings.Cache)
|
||||
env.Parse(&settings.Hosts)
|
||||
}
|
||||
|
||||
func Resolver() ResolvSettings {
|
||||
return settings.ResolvConfig
|
||||
}
|
||||
|
||||
func Cache() CacheSettings {
|
||||
return settings.Cache
|
||||
}
|
||||
|
||||
func Server() DNSServerSettings {
|
||||
return settings.Server
|
||||
}
|
||||
|
||||
func Hosts() HostsSettings {
|
||||
return settings.Hosts
|
||||
}
|
||||
|
||||
func Redis() RedisSettings {
|
||||
return settings.Redis
|
||||
}
|
||||
|
||||
func Memcache() MemcacheSettings {
|
||||
return settings.Memcache
|
||||
}
|
||||
|
||||
func Debug() bool {
|
||||
return settings.Debug
|
||||
}
|
||||
|
||||
func Log() LogSettings {
|
||||
return settings.Log
|
||||
}
|
Loading…
Reference in New Issue