type Config struct { Nameservers []string `yaml:"nameservers"` Blocklist []string `yaml:"blocklist"` BlockAddress4 string `yaml:"blockAddress4"` BlockAddress6 string `yaml:"blockAddress6"` ConfigUpdate bool `yaml:"configUpdate"` UpdateInterval time.Duration `yaml:"updateInterval"` }
func configWatcher() { watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close() err = watcher.Add(*configFile) if err != nil { log.Fatal(err) } for { select { case event := <-watcher.Events: if event.Op&fsnotify.Write == fsnotify.Write { log.Println("Config file updated, reload config") c, err := loadConfig() if err != nil { log.Println("Bad config: ", err) } else { log.Println("Config successfuly updated") config = c if !c.ConfigUpdate { return } } } case err := <-watcher.Errors: log.Println("error:", err) } } }
type BlackList struct { data map[string]struct{} } func (b *BlackList) Add(server string) bool { server = strings.Trim(server, " ") if len(server) == 0 { return false } if !strings.HasSuffix(server, ".") { server += "." } b.data[server] = struct{}{} return true } func (b *BlackList) Contains(server string) bool { _, ok := b.data[server] return ok }
type Cache interface { Get(reqType uint16, domain string) dns.RR Set(reqType uint16, domain string, ip dns.RR) } type CacheItem struct { Ip dns.RR Die time.Time } type MemoryCache struct { cache map[uint16]map[string]*CacheItem locker sync.RWMutex } func (c *MemoryCache) Get(reqType uint16, domain string) dns.RR { c.locker.RLock() defer c.locker.RUnlock() if m, ok := c.cache[reqType]; ok { if ip, ok := m[domain]; ok { if ip.Die.After(time.Now()) { return ip.Ip } } } return nil } func (c *MemoryCache) Set(reqType uint16, domain string, ip dns.RR) { c.locker.Lock() defer c.locker.Unlock() var m map[string]*CacheItem m, ok := c.cache[reqType] if !ok { m = make(map[string]*CacheItem) c.cache[reqType] = m } m[domain] = &CacheItem{ Ip: ip, Die: time.Now().Add(time.Duration(ip.Header().Ttl) * time.Second), } }
func Lookup(req *dns.Msg) (*dns.Msg, error) { c := &dns.Client{ Net: "tcp", ReadTimeout: time.Second * 5, WriteTimeout: time.Second * 5, } qName := req.Question[0].Name res := make(chan *dns.Msg, 1) var wg sync.WaitGroup L := func(nameserver string) { defer wg.Done() r, _, err := c.Exchange(req, nameserver) totalRequestsToGoogle.Inc() if err != nil { log.Printf("%s socket error on %s", qName, nameserver) log.Printf("error:%s", err.Error()) return } if r != nil && r.Rcode != dns.RcodeSuccess { if r.Rcode == dns.RcodeServerFailure { return } } select { case res <- r: default: } } ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() // Start lookup on each nameserver top-down, in every second for _, nameserver := range config.Nameservers { wg.Add(1) go L(nameserver) // but exit early, if we have an answer select { case r := <-res: return r, nil case <-ticker.C: continue } } // wait for all the namservers to finish wg.Wait() select { case r := <-res: return r, nil default: return nil, errors.New("can't resolve ip for" + qName) } }
var ( totalRequestsTcp = prometheus.NewCounter(prometheus.CounterOpts(prometheus.Opts{ Namespace: "dns", Subsystem: "requests", Name: "total", Help: "total requests", ConstLabels: map[string]string{ "type": "tcp", }, })) ) func runPrometheus() { prometheus.MustRegister(totalRequestsTcp) http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe(":9970", nil)) }
Source: https://habr.com/ru/post/348280/
All Articles