commit 5349b53416ba49dd3ee4e40482a91e00467fc47a Author: kenshin Date: Tue Jul 23 19:10:38 2013 +0800 first commit 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) + } + +}