first commit

This commit is contained in:
kenshin 2013-07-23 19:10:38 +08:00
commit 5349b53416
8 changed files with 336 additions and 0 deletions

16
README.MD Normal file
View File

@ -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

13
cache.go Normal file
View File

@ -0,0 +1,13 @@
package main
type Cache struct {
config CacheSettings
}
func (c *Cache) Get() {
}
func (c *Cache) Set() {
}

33
godns.conf Normal file
View File

@ -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

54
handler.go Normal file
View File

@ -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)
}

59
main.go Normal file
View File

@ -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
}

40
resolver.go Normal file
View File

@ -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
}

56
server.go Normal file
View File

@ -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())
}
}

65
settings.go Normal file
View File

@ -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)
}
}