From 5349b53416ba49dd3ee4e40482a91e00467fc47a Mon Sep 17 00:00:00 2001 From: kenshin Date: Tue, 23 Jul 2013 19:10:38 +0800 Subject: [PATCH] first commit --- README.MD | 16 +++++++++++++ cache.go | 13 +++++++++++ godns.conf | 33 +++++++++++++++++++++++++++ handler.go | 54 ++++++++++++++++++++++++++++++++++++++++++++ main.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ resolver.go | 40 +++++++++++++++++++++++++++++++++ server.go | 56 +++++++++++++++++++++++++++++++++++++++++++++ settings.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 336 insertions(+) create mode 100644 README.MD create mode 100644 cache.go create mode 100644 godns.conf create mode 100644 handler.go create mode 100644 main.go create mode 100644 resolver.go create mode 100644 server.go create mode 100644 settings.go diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..b32b4d9 --- /dev/null +++ b/README.MD @@ -0,0 +1,16 @@ +GODNS +==== + +A tiny dns cache server written by go. + + +Similar as [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) ,but support some difference features: + + +* Keep hosts configuration in redis instead of local file /etc/hosts + So can be updated from remote server + +* Atuo-Reload when hosts configuration changed. (Yes,dnsmasq need restart) + +* Cache records save in memory or redis configurable + diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..ec8e64c --- /dev/null +++ b/cache.go @@ -0,0 +1,13 @@ +package main + +type Cache struct { + config CacheSettings +} + +func (c *Cache) Get() { + +} + +func (c *Cache) Set() { + +} diff --git a/godns.conf b/godns.conf new file mode 100644 index 0000000..b09cb1c --- /dev/null +++ b/godns.conf @@ -0,0 +1,33 @@ +#Toml config file + + +Title = "GODNS" +Version = "0.1.1" +Author = "kenshin" + +Debug = true + +[server] +host = "127.0.0.1" +port = 53 + +[resolv] +resolv-file = "/etc/resolv.conf" +timeout = 30 # 30 seconds + +[redis] +host = "kenshinx.me" +port = 6379 +db = 0 +password ="" + +[log] +#If didn't set the log file,log will be redirected to console. +file = "" + + +[cache] +# backend option [memory|redis] +backend = "memory" +expire = 600 # 10 minutes +maxcount = 100000 diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..a88ba18 --- /dev/null +++ b/handler.go @@ -0,0 +1,54 @@ +package main + +import ( + "github.com/miekg/dns" + // "log" +) + +type GODNSHandler struct { + resolver *Resolver + cache *Cache +} + +func NewHandler() *GODNSHandler { + + var ( + clientConfig *dns.ClientConfig + cacheConfig CacheSettings + ) + + resolvConfig := settings.ResolvConfig + clientConfig, err := dns.ClientConfigFromFile(resolvConfig.ResolvFile) + if err != nil { + logger.Printf(":%s is not a valid resolv.conf file\n", resolvConfig.ResolvFile) + logger.Println(err) + panic(err) + } + clientConfig.Timeout = resolvConfig.Timeout + resolver := &Resolver{clientConfig} + + cacheConfig = settings.Cache + cache := &Cache{cacheConfig} + + return &GODNSHandler{resolver, cache} +} + +func (h *GODNSHandler) do(net string, w dns.ResponseWriter, req *dns.Msg) { + + qname := req.Question[0].Name + qtype := req.Question[0].Qtype + qclass := req.Question[0].Qclass + + Debug("Question: %s %s %s", qname, dns.ClassToString[qclass], dns.TypeToString[qtype]) + + h.resolver.Lookup(net, req) + +} + +func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) { + h.do("tcp", w, req) +} + +func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) { + h.do("udp", w, req) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..e7da0f8 --- /dev/null +++ b/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "log" + "os" + "os/signal" + "time" +) + +var ( + logger *log.Logger +) + +func main() { + + logger = initLogger(settings.Log.File) + + server := &Server{ + host: settings.Server.Host, + port: settings.Server.Port, + rTimeout: 5 * time.Second, + wTimeout: 5 * time.Second, + } + + server.Run() + + sig := make(chan os.Signal) + signal.Notify(sig, os.Interrupt) + +forever: + for { + select { + case <-sig: + logger.Printf("signal received, stopping") + break forever + } + } + +} + +func Debug(format string, v ...interface{}) { + if settings.Debug { + logger.Printf(format, v...) + } +} + +func initLogger(log_file string) (logger *log.Logger) { + if log_file != "" { + f, err := os.Create(log_file) + if err != nil { + os.Exit(1) + } + logger = log.New(f, "[godns]", log.Ldate|log.Ltime) + } else { + logger = log.New(os.Stdout, "[godns]", log.Ldate|log.Ltime) + } + return logger + +} diff --git a/resolver.go b/resolver.go new file mode 100644 index 0000000..8b3740b --- /dev/null +++ b/resolver.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "github.com/miekg/dns" + "time" +) + +type Resolver struct { + config *dns.ClientConfig +} + +func (r *Resolver) Lookup(net string, req *dns.Msg) { + c := &dns.Client{ + Net: net, + ReadTimeout: r.Timeout(), + WriteTimeout: r.Timeout(), + } + + for _, nameserver := range r.Nameservers() { + r, rtt, _ := c.Exchange(req, nameserver) + fmt.Println(r) + fmt.Println(rtt) + + } + // r,rtt,_:c.Exchange(req, a) + +} + +func (r *Resolver) Nameservers() (ns []string) { + for _, server := range r.config.Servers { + nameserver := server + ":" + r.config.Port + ns = append(ns, nameserver) + } + return +} + +func (r *Resolver) Timeout() time.Duration { + return time.Duration(r.config.Timeout) * time.Second +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..5625e97 --- /dev/null +++ b/server.go @@ -0,0 +1,56 @@ +package main + +import ( + "github.com/miekg/dns" + "strconv" + "time" +) + +type Server struct { + host string + port int + rTimeout time.Duration + wTimeout time.Duration +} + +func (s *Server) Addr() string { + return s.host + ":" + strconv.Itoa(s.port) +} + +func (s *Server) Run() { + + Handler := NewHandler() + + tcpHandler := dns.NewServeMux() + tcpHandler.HandleFunc(".", Handler.DoTCP) + + udpHandler := dns.NewServeMux() + udpHandler.HandleFunc(".", Handler.DoUDP) + + tcpServer := &dns.Server{Addr: s.Addr(), + Net: "tcp", + Handler: tcpHandler, + ReadTimeout: s.rTimeout, + WriteTimeout: s.wTimeout} + + udpServer := &dns.Server{Addr: s.Addr(), + Net: "udp", + Handler: udpHandler, + UDPSize: 65535, + ReadTimeout: s.rTimeout, + WriteTimeout: s.wTimeout} + + go s.start(udpServer) + go s.start(tcpServer) + +} + +func (s *Server) start(ds *dns.Server) { + + logger.Printf("Start %s listener on %s\n", ds.Net, s.Addr()) + err := ds.ListenAndServe() + if err != nil { + logger.Fatalf("Start %s listener on %s failed:%s", ds.Net, s.Addr(), err.Error()) + } + +} diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..0afd815 --- /dev/null +++ b/settings.go @@ -0,0 +1,65 @@ +package main + +import ( + "flag" + "fmt" + "github.com/BurntSushi/toml" + "os" + "time" +) + +var ( + settings Settings +) + +type Settings struct { + Version string + Debug bool + Server DNSServerSettings `toml:"server"` + ResolvConfig ResolvSettings `toml:"resolv"` + Redis RedisSettings `toml:"redis"` + Log LogSettings `toml:"log"` + Cache CacheSettings `toml:"cache"` +} + +type ResolvSettings struct { + ResolvFile string `toml:"resolv-file"` + Timeout int +} + +type DNSServerSettings struct { + Host string + Port int +} + +type RedisSettings struct { + Host string + Port int + DB int + Password string +} + +type LogSettings struct { + File string +} + +type CacheSettings struct { + Backend string + Expire time.Duration + Maxcount uint32 +} + +func init() { + + var configFile string + + flag.StringVar(&configFile, "c", "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) + } + +}