diff --git a/cache.go b/cache.go index 3281418..d06d429 100644 --- a/cache.go +++ b/cache.go @@ -1,15 +1,11 @@ package main import ( - "crypto/md5" - "encoding/json" "fmt" - "sync" "time" - "github.com/bradfitz/gomemcache/memcache" - "github.com/hoisie/redis" "github.com/miekg/dns" + "crypto/md5" ) type KeyNotFound struct { @@ -56,178 +52,10 @@ type Cache interface { Full() bool } -type MemoryCache struct { - Backend map[string]Mesg - Expire time.Duration - Maxcount int - mu sync.RWMutex -} - -func (c *MemoryCache) Get(key string) (*dns.Msg, error) { - c.mu.RLock() - mesg, ok := c.Backend[key] - c.mu.RUnlock() - if !ok { - return nil, KeyNotFound{key} - } - - if mesg.Expire.Before(time.Now()) { - c.Remove(key) - return nil, KeyExpired{key} - } - - return mesg.Msg, nil - -} - -func (c *MemoryCache) Set(key string, msg *dns.Msg) error { - if c.Full() && !c.Exists(key) { - return CacheIsFull{} - } - - expire := time.Now().Add(c.Expire) - mesg := Mesg{msg, expire} - c.mu.Lock() - c.Backend[key] = mesg - c.mu.Unlock() - return nil -} - -func (c *MemoryCache) Remove(key string) error { - c.mu.Lock() - delete(c.Backend, key) - c.mu.Unlock() - return nil -} - -func (c *MemoryCache) Exists(key string) bool { - c.mu.RLock() - _, ok := c.Backend[key] - c.mu.RUnlock() - return ok -} - -func (c *MemoryCache) Length() int { - c.mu.RLock() - defer c.mu.RUnlock() - return len(c.Backend) -} - -func (c *MemoryCache) Full() bool { - // if Maxcount is zero. the cache will never be full. - if c.Maxcount == 0 { - return false - } - return c.Length() >= c.Maxcount -} - -/* -Memcached backend -*/ - -func NewMemcachedCache(servers []string, expire int32) *MemcachedCache { - c := memcache.New(servers...) - return &MemcachedCache{ - backend: c, - expire: expire, - } -} - -type MemcachedCache struct { - backend *memcache.Client - expire int32 -} - -func (m *MemcachedCache) Set(key string, msg *dns.Msg) error { - var val []byte - var err error - - // handle cases for negacache where it sets nil values - if msg == nil { - val = []byte("nil") - } else { - val, err = msg.Pack() - } - if err != nil { - err = SerializerError{err} - } - return m.backend.Set(&memcache.Item{Key: key, Value: val, Expiration: m.expire}) -} - -func (m *MemcachedCache) Get(key string) (*dns.Msg, error) { - var msg dns.Msg - item, err := m.backend.Get(key) - if err != nil { - err = KeyNotFound{key} - return &msg, err - } - err = msg.Unpack(item.Value) - if err != nil { - err = SerializerError{err} - } - return &msg, err -} - -func (m *MemcachedCache) Exists(key string) bool { - _, err := m.backend.Get(key) - if err != nil { - return true - } - return false -} - -func (m *MemcachedCache) Remove(key string) error { - return m.backend.Delete(key) -} - -func (m *MemcachedCache) Full() bool { - // memcache is never full (LRU) - return false -} - -/* -TODO: Redis cache Backend -*/ - -type RedisCache struct { - Backend *redis.Client - Serializer JsonSerializer - Expire time.Duration - Maxcount int -} - -func (c *RedisCache) Get() { - -} - -func (c *RedisCache) Set() { - -} - -func (c *RedisCache) Remove() { - -} - func KeyGen(q Question) string { h := md5.New() h.Write([]byte(q.String())) x := h.Sum(nil) key := fmt.Sprintf("%x", x) return key -} - -/* we need to define marsheling to encode and decode - */ -type JsonSerializer struct { -} - -func (*JsonSerializer) Dumps(mesg *dns.Msg) (encoded []byte, err error) { - encoded, err = json.Marshal(*mesg) - return -} - -func (*JsonSerializer) Loads(data []byte) (*dns.Msg, error) { - var mesg dns.Msg - err := json.Unmarshal(data, &mesg) - return &mesg, err -} +} \ No newline at end of file diff --git a/cache_memcached.go b/cache_memcached.go new file mode 100644 index 0000000..a30f829 --- /dev/null +++ b/cache_memcached.go @@ -0,0 +1,66 @@ +package main + +import ( + "github.com/bradfitz/gomemcache/memcache" + "github.com/miekg/dns" +) + +func NewMemcachedCache(servers []string, expire int32) *MemcachedCache { + c := memcache.New(servers...) + return &MemcachedCache{ + backend: c, + expire: expire, + } +} + +type MemcachedCache struct { + backend *memcache.Client + expire int32 +} + +func (m *MemcachedCache) Set(key string, msg *dns.Msg) error { + var val []byte + var err error + + // handle cases for negacache where it sets nil values + if msg == nil { + val = []byte("nil") + } else { + val, err = msg.Pack() + } + if err != nil { + err = SerializerError{err} + } + return m.backend.Set(&memcache.Item{Key: key, Value: val, Expiration: m.expire}) +} + +func (m *MemcachedCache) Get(key string) (*dns.Msg, error) { + var msg dns.Msg + item, err := m.backend.Get(key) + if err != nil { + err = KeyNotFound{key} + return &msg, err + } + err = msg.Unpack(item.Value) + if err != nil { + err = SerializerError{err} + } + return &msg, err +} + +func (m *MemcachedCache) Exists(key string) bool { + _, err := m.backend.Get(key) + if err != nil { + return true + } + return false +} + +func (m *MemcachedCache) Remove(key string) error { + return m.backend.Delete(key) +} + +func (m *MemcachedCache) Full() bool { + // memcache is never full (LRU) + return false +} diff --git a/cache_memory.go b/cache_memory.go new file mode 100644 index 0000000..45ee65d --- /dev/null +++ b/cache_memory.go @@ -0,0 +1,74 @@ +package main + +import ( + "time" + "sync" + "github.com/miekg/dns" +) + +type MemoryCache struct { + Cache + + Backend map[string]Mesg + Expire time.Duration + Maxcount int + mu sync.RWMutex +} + +func (c *MemoryCache) Get(key string) (*dns.Msg, error) { + c.mu.RLock() + mesg, ok := c.Backend[key] + c.mu.RUnlock() + if !ok { + return nil, KeyNotFound{key} + } + + if mesg.Expire.Before(time.Now()) { + c.Remove(key) + return nil, KeyExpired{key} + } + + return mesg.Msg, nil + +} + +func (c *MemoryCache) Set(key string, msg *dns.Msg) error { + if c.Full() && !c.Exists(key) { + return CacheIsFull{} + } + + expire := time.Now().Add(c.Expire) + mesg := Mesg{msg, expire} + c.mu.Lock() + c.Backend[key] = mesg + c.mu.Unlock() + return nil +} + +func (c *MemoryCache) Remove(key string) error { + c.mu.Lock() + delete(c.Backend, key) + c.mu.Unlock() + return nil +} + +func (c *MemoryCache) Exists(key string) bool { + c.mu.RLock() + _, ok := c.Backend[key] + c.mu.RUnlock() + return ok +} + +func (c *MemoryCache) Length() int { + c.mu.RLock() + defer c.mu.RUnlock() + return len(c.Backend) +} + +func (c *MemoryCache) Full() bool { + // if Maxcount is zero. the cache will never be full. + if c.Maxcount == 0 { + return false + } + return c.Length() >= c.Maxcount +} diff --git a/cache_redis.go b/cache_redis.go new file mode 100644 index 0000000..208ad57 --- /dev/null +++ b/cache_redis.go @@ -0,0 +1,70 @@ +package main + +import ( + "github.com/hoisie/redis" + "github.com/miekg/dns" +) + +func NewRedisCache(c RedisSettings, expire int32) *RedisCache { + rc := &redis.Client{Addr: c.Addr(), Db: c.DB, Password: c.Password} + + return &RedisCache{ + backend: rc, + expire: expire, + } +} + +type RedisCache struct { + Cache + + backend *redis.Client + expire int32 +} + +func (m *RedisCache) Set(key string, msg *dns.Msg) error { + var val []byte + var err error + + // handle cases for negacache where it sets nil values + if msg == nil { + val = []byte("nil") + } else { + val, err = msg.Pack() + } + if err != nil { + err = SerializerError{err} + } + return m.backend.Setex(key, int64(m.expire), val) +} + +func (m *RedisCache) Get(key string) (*dns.Msg, error) { + var msg dns.Msg + item, err := m.backend.Get(key) + if err != nil { + err = KeyNotFound{key} + return &msg, err + } + err = msg.Unpack(item) + if err != nil { + err = SerializerError{err} + } + return &msg, err +} + +func (m *RedisCache) Exists(key string) bool { + exists, err := m.backend.Exists(key) + if err != nil { + return true + } + return exists +} + +func (m *RedisCache) Remove(key string) error { + _, err := m.backend.Del(key) + return err +} + +func (m *RedisCache) Full() bool { + // memcache is never full (LRU) + return false +} diff --git a/etc/godns.conf b/etc/godns.conf index a94be7a..5bd363a 100644 --- a/etc/godns.conf +++ b/etc/godns.conf @@ -16,6 +16,7 @@ port = 53 # Semicolon separate multiple files. server-list-file = "./etc/apple.china.conf;./etc/google.china.conf" resolv-file = "/etc/resolv.conf" +dns-over-https = "https://cloudflare-dns.com/dns-query" timeout = 5 # 5 seconds # The concurrency interval request upstream recursive server # Match the PR15, https://github.com/kenshinx/godns/pull/15 diff --git a/handler.go b/handler.go index 1de7ee1..cf97b4c 100644 --- a/handler.go +++ b/handler.go @@ -60,13 +60,12 @@ func NewHandler() *GODNSHandler { settings.Memcache.Servers, int32(cacheConfig.Expire/2)) case "redis": - // cache = &MemoryCache{ - // Backend: make(map[string]*dns.Msg), - // Expire: time.Duration(cacheConfig.Expire) * time.Second, - // Serializer: new(JsonSerializer), - // Maxcount: cacheConfig.Maxcount, - // } - panic("Redis cache backend not implement yet") + cache = NewRedisCache( + settings.Redis, + int32(cacheConfig.Expire)) + negCache = NewRedisCache( + settings.Redis, + int32(cacheConfig.Expire/2)) default: logger.Error("Invalid cache backend %s", cacheConfig.Backend) panic("Invalid cache backend")