gdns: first version
commit
13c78296f2
@ -0,0 +1,2 @@
|
||||
*~
|
||||
dns*
|
@ -0,0 +1,21 @@
|
||||
gdns
|
||||
====
|
||||
|
||||
gdns is a dns proxy server
|
||||
|
||||
|
||||
features
|
||||
=======
|
||||
|
||||
- support forward the query by rule,
|
||||
different domains use different upstream server
|
||||
- support ip black list
|
||||
- support google https dns
|
||||
|
||||
usage
|
||||
=====
|
||||
|
||||
go get github.com/fangdingjun/gdns
|
||||
cp $GOPATH/src/github.com/fangdingjun/gdns/config_example.yaml config.yaml
|
||||
vim config.yaml
|
||||
$GOPATH/bin/gdns -c config.yaml
|
@ -0,0 +1,212 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/go-yaml/yaml"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type conf struct {
|
||||
Listen []addr
|
||||
BlacklistFile string
|
||||
HostFile string
|
||||
ForwardRules []rule
|
||||
DefaultUpstream []addr
|
||||
Timeout int
|
||||
Debug bool
|
||||
blacklist item
|
||||
hosts hostitem
|
||||
}
|
||||
|
||||
type rule struct {
|
||||
Server []addr
|
||||
DomainFile string
|
||||
domains item
|
||||
}
|
||||
|
||||
type addr struct {
|
||||
Host string
|
||||
Port int
|
||||
Network string
|
||||
}
|
||||
|
||||
func loadConfig(f string) (*conf, error) {
|
||||
c := new(conf)
|
||||
data, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(data, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.Debug {
|
||||
logLevel = DEBUG
|
||||
}
|
||||
|
||||
if c.blacklist == nil {
|
||||
c.blacklist = item{}
|
||||
}
|
||||
|
||||
if c.Timeout == 0 {
|
||||
c.Timeout = 2
|
||||
}
|
||||
|
||||
if err := loadItemFile(c.blacklist, c.BlacklistFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range c.ForwardRules {
|
||||
if c.ForwardRules[i].domains == nil {
|
||||
c.ForwardRules[i].domains = item{}
|
||||
}
|
||||
if err := loadItemFile(c.ForwardRules[i].domains,
|
||||
c.ForwardRules[i].DomainFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.hosts == nil {
|
||||
c.hosts = hostitem{}
|
||||
}
|
||||
|
||||
if err := loadHostsFile(c.hosts, c.HostFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func loadHostsFile(h hostitem, f string) error {
|
||||
if f == "" {
|
||||
return nil
|
||||
}
|
||||
fd, err := os.Open(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
r := bufio.NewReader(fd)
|
||||
for {
|
||||
s, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
s1 := strings.Trim(s, " \t\r\n")
|
||||
|
||||
// ignore blank line and comment
|
||||
if s1 == "" || s1[0] == '#' {
|
||||
continue
|
||||
}
|
||||
s1 = strings.Replace(s1, "\t", " ", -1)
|
||||
s1 = strings.Trim(s1, " \t\r\n")
|
||||
ss := strings.Split(s1, " ")
|
||||
|
||||
// ipv4
|
||||
t := 1
|
||||
if strings.Index(ss[0], ":") != -1 {
|
||||
// ipv6
|
||||
t = 28
|
||||
}
|
||||
|
||||
for _, s2 := range ss[1:] {
|
||||
if s2 == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
h.add(s2, ss[0], t)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadItemFile(it item, f string) error {
|
||||
if f == "" {
|
||||
return nil
|
||||
}
|
||||
fd, err := os.Open(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
r := bufio.NewReader(fd)
|
||||
for {
|
||||
s, err := r.ReadString('\n')
|
||||
if s != "" {
|
||||
s1 := strings.Trim(s, " \r\n")
|
||||
if s1 != "" && s1[0] != '#' {
|
||||
it.add(s1)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type item map[string]int
|
||||
|
||||
func (it item) has(s string) bool {
|
||||
ss := strings.Split(s, ".")
|
||||
|
||||
for i := 0; i < len(ss); i++ {
|
||||
s1 := strings.Join(ss[i:], ".")
|
||||
if _, ok := it[s1]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (it item) exists(s string) bool {
|
||||
_, ok := it[s]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (it item) add(s string) {
|
||||
it[s] = 1
|
||||
}
|
||||
|
||||
type hostitem map[string][]hostentry
|
||||
|
||||
func (ht hostitem) get(domain string, t int) string {
|
||||
if v, ok := ht[domain]; ok {
|
||||
for _, v1 := range v {
|
||||
if v1.domain == domain && v1.t == t {
|
||||
return v1.ip
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (ht hostitem) add(domain, ip string, t int) {
|
||||
if v, ok := ht[domain]; ok {
|
||||
exists := false
|
||||
for _, v1 := range v {
|
||||
if v1.domain == domain && v1.ip == ip && v1.t == t {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
ht[domain] = append(ht[domain], hostentry{domain, ip, t})
|
||||
}
|
||||
} else {
|
||||
v1 := []hostentry{{domain, ip, t}}
|
||||
ht[domain] = v1
|
||||
}
|
||||
}
|
||||
|
||||
type hostentry struct {
|
||||
domain string
|
||||
ip string
|
||||
t int
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
//"github.com/go-yaml/yaml"
|
||||
//"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConf(t *testing.T) {
|
||||
c, err := loadConfig("testdata/config.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
//fmt.Printf("%#v\n", c)
|
||||
if len(c.Listen) != 2 {
|
||||
t.Errorf("expected listers 2, got %d", len(c.Listen))
|
||||
}
|
||||
|
||||
if !c.blacklist.exists("1.2.3.4") {
|
||||
fmt.Printf("%#v\n", c.blacklist)
|
||||
t.Errorf("blacklist load failed")
|
||||
}
|
||||
if c.hosts.get("localhost", 1) != "127.0.0.1" {
|
||||
fmt.Printf("%#v\n", c.hosts)
|
||||
t.Errorf("hosts load failed")
|
||||
}
|
||||
if c.hosts.get("localhost", 28) != "::1" {
|
||||
fmt.Printf("%#v\n", c.hosts)
|
||||
t.Errorf("hosts load failed")
|
||||
}
|
||||
if len(c.ForwardRules) != 2 {
|
||||
fmt.Printf("%#v\n", c.ForwardRules)
|
||||
t.Errorf("expected rules 2, got %d", len(c.ForwardRules))
|
||||
}
|
||||
if !c.ForwardRules[0].domains.has("a.com") {
|
||||
fmt.Printf("%#v\n", c.ForwardRules[0].domains)
|
||||
t.Errorf("some domains should exit, may be load config failed")
|
||||
}
|
||||
if !c.ForwardRules[1].domains.has("d.com") {
|
||||
fmt.Printf("%#v\n", c.ForwardRules[1].domains)
|
||||
t.Errorf("some domains should exit, may be load config failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestItemExists(t *testing.T) {
|
||||
it := item{
|
||||
"google.cn": 1,
|
||||
"www.baidu.com": 1,
|
||||
"org": 1,
|
||||
}
|
||||
|
||||
testdata := []struct {
|
||||
d string
|
||||
b bool
|
||||
}{
|
||||
{"google.cn", true},
|
||||
{"www.google.cn", false},
|
||||
{"www.a.org", false},
|
||||
}
|
||||
|
||||
for _, d := range testdata {
|
||||
b1 := it.exists(d.d)
|
||||
if b1 != d.b {
|
||||
t.Errorf("%s, expected %v, got %v", d.d, d.b, b1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestItemHas(t *testing.T) {
|
||||
it := item{
|
||||
"google.cn": 1,
|
||||
"www.baidu.com": 1,
|
||||
"org": 1,
|
||||
}
|
||||
|
||||
testdata := []struct {
|
||||
d string
|
||||
b bool
|
||||
}{
|
||||
{"google.cn", true},
|
||||
{"www.google.cn", true},
|
||||
{"www.a.org", true},
|
||||
{"pan.baidu.com", false},
|
||||
{"abc.org", true},
|
||||
}
|
||||
|
||||
for _, d := range testdata {
|
||||
b1 := it.has(d.d)
|
||||
if b1 != d.b {
|
||||
t.Errorf("%s, expected %v, got %v", d.d, d.b, b1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestItemAdd(t *testing.T) {
|
||||
it := item{}
|
||||
it.add("www.example.org")
|
||||
if !it.exists("www.example.org") {
|
||||
t.Errorf("add failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostitem(t *testing.T) {
|
||||
ht := hostitem{}
|
||||
testdata := []hostentry{
|
||||
{"www.google.com", "127.0.0.1", 1},
|
||||
{"www.google.com", "127.0.0.2", 28},
|
||||
{"www.example.org", "127.0.0.3", 28},
|
||||
{"www.abc.org", "127.0.0.4", 1},
|
||||
}
|
||||
|
||||
for _, d := range testdata {
|
||||
ht.add(d.domain, d.ip, d.t)
|
||||
ip := ht.get(d.domain, d.t)
|
||||
if ip != d.ip {
|
||||
t.Errorf("%s, expected %s, got %s", d.domain, d.ip, ip)
|
||||
}
|
||||
}
|
||||
//fmt.Printf("%v\n", ht)
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
|
||||
listen:
|
||||
- network: tcp
|
||||
host: 0.0.0.0
|
||||
port: 1053
|
||||
|
||||
- network: udp
|
||||
host: 0.0.0.0
|
||||
port: 1053
|
||||
|
||||
blacklistfile: ./testdata/blacklist.dat
|
||||
hostfile: ./testdata/hosts
|
||||
timeout: 2
|
||||
debug: true
|
||||
|
||||
defaultupstream:
|
||||
-
|
||||
network: tcp
|
||||
host: 8.8.8.8
|
||||
port: 53
|
||||
-
|
||||
network:udp
|
||||
host: 8.8.8.8
|
||||
port: 53
|
||||
|
||||
forwardrules:
|
||||
- domainfile: ./testdata/cn.dat
|
||||
server:
|
||||
- network: tcp
|
||||
host: 114.114.114.114
|
||||
port: 53
|
||||
|
||||
- domainfile: testdata/us.dat
|
||||
server:
|
||||
- network: tcp
|
||||
host: 8.8.8.8
|
||||
port: 53
|
||||
|
||||
- network: https
|
||||
host: 74.175.200.100
|
||||
port: 443
|
@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ServerAddr is Google dns server ip
|
||||
var ServerAddr = "74.125.200.100"
|
||||
var queryIPApi = "https://www.simicloud.com/media/httpbin/ip"
|
||||
|
||||
// GoogleHTTPDns struct
|
||||
type GoogleHTTPDns struct {
|
||||
myip string
|
||||
l sync.Mutex
|
||||
}
|
||||
|
||||
func (h *GoogleHTTPDns) getMyIP() string {
|
||||
if h.myip != "" {
|
||||
return h.myip
|
||||
}
|
||||
go h.queryMyIP()
|
||||
return ""
|
||||
}
|
||||
|
||||
type ipAPI struct {
|
||||
IP string `json:"origin"`
|
||||
}
|
||||
|
||||
func (h *GoogleHTTPDns) queryMyIP() {
|
||||
h.l.Lock()
|
||||
defer h.l.Unlock()
|
||||
if h.myip != "" {
|
||||
//fmt.Printf("myip: %s\n", h.myip)
|
||||
return
|
||||
}
|
||||
//fmt.Println("get ip...")
|
||||
res, err := http.Get(queryIPApi)
|
||||
if err != nil {
|
||||
//fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
d, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
//fmt.Println(err)
|
||||
return
|
||||
}
|
||||
//fmt.Printf("%s\n", string(d))
|
||||
ip := ipAPI{}
|
||||
err = json.Unmarshal(d, &ip)
|
||||
if err != nil {
|
||||
//fmt.Println(err)
|
||||
return
|
||||
}
|
||||
//fmt.Printf("got: %s\n", ip.Ip)
|
||||
h.myip = ip.IP
|
||||
}
|
||||
|
||||
func (h *GoogleHTTPDns) getMyNet() string {
|
||||
ip := h.getMyIP()
|
||||
if ip == "" {
|
||||
return ""
|
||||
}
|
||||
mask := net.IPv4Mask(255, 255, 255, 0)
|
||||
ipByte := net.ParseIP(ip)
|
||||
ipnet := net.IPNet{ipByte.Mask(mask), mask}
|
||||
return ipnet.String()
|
||||
}
|
||||
|
||||
// Exchange send query to server and return the response
|
||||
func (h *GoogleHTTPDns) Exchange(m *dns.Msg, addr string) (*dns.Msg, time.Duration, error) {
|
||||
name := m.Question[0].Name
|
||||
t := dns.TypeToString[m.Question[0].Qtype]
|
||||
mynet := h.getMyNet()
|
||||
r, err := queryGoogleHTTPDNS(name, t, mynet, "", addr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
m1 := new(dns.Msg)
|
||||
|
||||
m1.SetRcode(m, r.Status)
|
||||
for _, rr := range r.Answer {
|
||||
_rr := fmt.Sprintf("%s %d IN %s %s", rr.Name, rr.TTL,
|
||||
dns.TypeToString[uint16(rr.Type)], rr.Data)
|
||||
|
||||
an, err := dns.NewRR(_rr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
m1.Answer = append(m1.Answer, an)
|
||||
}
|
||||
m1.Truncated = r.TC
|
||||
m1.RecursionDesired = r.RD
|
||||
m1.RecursionAvailable = r.RA
|
||||
m1.AuthenticatedData = r.AD
|
||||
m1.CheckingDisabled = r.CD
|
||||
return m1, 0, nil
|
||||
|
||||
}
|
||||
|
||||
// Response represent the dns response from server
|
||||
type Response struct {
|
||||
Status int
|
||||
TC bool
|
||||
RD bool
|
||||
RA bool
|
||||
AD bool
|
||||
CD bool
|
||||
Question []RR
|
||||
Answer []RR
|
||||
Additional []RR
|
||||
EDNSClientSubnet string `json:"edns_client_subnet"`
|
||||
Comment string
|
||||
}
|
||||
|
||||
// RR represent the RR record
|
||||
type RR struct {
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type"`
|
||||
TTL int
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
var httpclient = &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{ServerName: "dns.google.com"},
|
||||
TLSHandshakeTimeout: 3 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
func queryGoogleHTTPDNS(name, t, ednsClientSubnet, padding, srvAddr string) (*Response, error) {
|
||||
srvaddr := ServerAddr
|
||||
if srvAddr != "" {
|
||||
srvaddr = srvAddr
|
||||
}
|
||||
v := url.Values{}
|
||||
v.Add("name", name)
|
||||
v.Add("type", t)
|
||||
|
||||
if ednsClientSubnet != "" {
|
||||
v.Add("edns_client_subnet", ednsClientSubnet)
|
||||
}
|
||||
|
||||
if padding != "" {
|
||||
v.Add("random_padding", padding)
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("https://%s/resolve?%s", srvaddr, v.Encode())
|
||||
r, _ := http.NewRequest("GET", u, nil)
|
||||
r.Host = "dns.google.com"
|
||||
//r.URL.Host = "dns.google.com"
|
||||
|
||||
res, err := httpclient.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := Response{}
|
||||
err = json.Unmarshal(data, &d)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type dnsClient interface {
|
||||
Exchange(m *dns.Msg, addr string) (*dns.Msg, time.Duration, error)
|
||||
}
|
||||
|
||||
type dnsHandler struct {
|
||||
cfg *conf
|
||||
tcpclient dnsClient
|
||||
udpclient dnsClient
|
||||
httpsclient dnsClient
|
||||
}
|
||||
|
||||
func newDNSHandler(cfg *conf) *dnsHandler {
|
||||
return &dnsHandler{
|
||||
cfg: cfg,
|
||||
tcpclient: &dns.Client{Net: "tcp", Timeout: 2 * time.Second},
|
||||
udpclient: &dns.Client{Net: "udp", Timeout: 2 * time.Second},
|
||||
httpsclient: &GoogleHTTPDns{},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ServerDNS implements the dns.Handler interface
|
||||
func (h *dnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
domain := r.Question[0].Name
|
||||
|
||||
if ok := h.answerFromHosts(w, r); ok {
|
||||
return
|
||||
}
|
||||
|
||||
srvs := h.getUpstreamServer(domain)
|
||||
if srvs == nil {
|
||||
srvs = h.cfg.DefaultUpstream
|
||||
}
|
||||
|
||||
if msg, err := h.getAnswerFromUpstream(r, srvs); err == nil {
|
||||
w.WriteMsg(msg)
|
||||
return
|
||||
}
|
||||
|
||||
dns.HandleFailed(w, r)
|
||||
|
||||
}
|
||||
|
||||
func (h *dnsHandler) getUpstreamServer(domain string) []addr {
|
||||
for _, srv := range h.cfg.ForwardRules {
|
||||
if ok := srv.domains.has(strings.Trim(domain, ".")); ok {
|
||||
return srv.Server
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *dnsHandler) queryUpstream(r *dns.Msg, srv addr, ch chan *dns.Msg) {
|
||||
var m *dns.Msg
|
||||
var err error
|
||||
|
||||
switch srv.Network {
|
||||
case "tcp":
|
||||
info("query %s IN %s, forward to %s:%d through tcp",
|
||||
r.Question[0].Name,
|
||||
dns.TypeToString[r.Question[0].Qtype],
|
||||
srv.Host,
|
||||
srv.Port)
|
||||
m, _, err = h.tcpclient.Exchange(r, fmt.Sprintf("%s:%d", srv.Host, srv.Port))
|
||||
case "udp":
|
||||
info("query %s IN %s, forward to %s:%d through udp",
|
||||
r.Question[0].Name,
|
||||
dns.TypeToString[r.Question[0].Qtype],
|
||||
srv.Host,
|
||||
srv.Port)
|
||||
m, _, err = h.tcpclient.Exchange(r, fmt.Sprintf("%s:%d", srv.Host, srv.Port))
|
||||
case "https":
|
||||
info("query %s IN %s, forward to %s:%d through https",
|
||||
r.Question[0].Name,
|
||||
dns.TypeToString[r.Question[0].Qtype],
|
||||
srv.Host,
|
||||
srv.Port)
|
||||
m, _, err = h.httpsclient.Exchange(r, fmt.Sprintf("%s:%d", srv.Host, srv.Port))
|
||||
default:
|
||||
// ignore
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
select {
|
||||
case ch <- m:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
errorlog("%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dnsHandler) getAnswerFromUpstream(r *dns.Msg, servers []addr) (*dns.Msg, error) {
|
||||
ch := make(chan *dns.Msg, 5)
|
||||
for _, srv := range servers {
|
||||
go func(a addr) {
|
||||
h.queryUpstream(r, a, ch)
|
||||
}(srv)
|
||||
}
|
||||
|
||||
var savedErr *dns.Msg
|
||||
for {
|
||||
select {
|
||||
case m := <-ch:
|
||||
if m.Rcode == dns.RcodeSuccess && !h.inBlacklist(m) {
|
||||
return m, nil
|
||||
}
|
||||
savedErr = m
|
||||
case <-time.After(time.Duration(h.cfg.Timeout) * time.Second):
|
||||
if savedErr != nil {
|
||||
return savedErr, nil
|
||||
}
|
||||
info("query %s IN %s, timeout", r.Question[0].Name, dns.TypeToString[r.Question[0].Qtype])
|
||||
return nil, errors.New("timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *dnsHandler) inBlacklist(m *dns.Msg) bool {
|
||||
var ip string
|
||||
for _, rr := range m.Answer {
|
||||
if a, ok := rr.(*dns.A); ok {
|
||||
ip = a.String()
|
||||
} else if aaaa, ok := rr.(*dns.AAAA); ok {
|
||||
ip = aaaa.String()
|
||||
} else {
|
||||
ip = ""
|
||||
}
|
||||
if ip != "" && h.cfg.blacklist.exists(ip) {
|
||||
info("%s in blacklist", ip)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *dnsHandler) answerFromHosts(w dns.ResponseWriter, r *dns.Msg) bool {
|
||||
domain := r.Question[0].Name
|
||||
t := r.Question[0].Qtype
|
||||
|
||||
ip := h.cfg.hosts.get(strings.Trim(domain, "."), int(t))
|
||||
if ip != "" {
|
||||
rr, _ := dns.NewRR(fmt.Sprintf("%s 3600 IN %s %s", domain, dns.TypeToString[t], ip))
|
||||
if rr == nil {
|
||||
return false
|
||||
}
|
||||
msg := new(dns.Msg)
|
||||
msg.SetReply(r)
|
||||
msg.Answer = append(msg.Answer, rr)
|
||||
w.WriteMsg(msg)
|
||||
info("query %s IN %s, reply from hosts", domain, dns.TypeToString[t])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
FATAL
|
||||
ERROR
|
||||
WARN
|
||||
NOTICE
|
||||
INFO
|
||||
DEBUG
|
||||
)
|
||||
|
||||
var logLevel = WARN
|
||||
|
||||
func logMsg(l int, fmt string, args ...interface{}) {
|
||||
if l <= logLevel {
|
||||
log.Printf(fmt, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func info(fmt string, args ...interface{}) {
|
||||
logMsg(INFO, fmt, args...)
|
||||
}
|
||||
|
||||
func errorlog(fmt string, args ...interface{}) {
|
||||
logMsg(ERROR, fmt, args...)
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/miekg/dns"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var configfile string
|
||||
|
||||
flag.StringVar(&configfile, "c", "config.yaml", "config file")
|
||||
flag.Parse()
|
||||
|
||||
config, err := loadConfig(configfile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
h := newDNSHandler(config)
|
||||
|
||||
for _, l := range config.Listen {
|
||||
go func(l addr) {
|
||||
if err := dns.ListenAndServe(
|
||||
fmt.Sprintf("%s:%d", l.Host, l.Port), l.Network, h); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(l)
|
||||
}
|
||||
|
||||
select {}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
# asdfas
|
||||
1.2.3.4
|
||||
2.3.1.21
|
||||
|
||||
3.1.1.1
|
@ -0,0 +1,8 @@
|
||||
#12
|
||||
|
||||
a.com
|
||||
b.org
|
||||
|
||||
cn
|
||||
|
||||
c.net
|
@ -0,0 +1,31 @@
|
||||
|
||||
listen:
|
||||
- network: tcp
|
||||
host: 0.0.0.0
|
||||
port: 1053
|
||||
|
||||
- network: udp
|
||||
host: 0.0.0.0
|
||||
port: 1053
|
||||
|
||||
blacklistfile: ./testdata/blacklist.dat
|
||||
hostfile: ./testdata/hosts
|
||||
timeout: 2
|
||||
debug: true
|
||||
|
||||
|
||||
forwardrules:
|
||||
- domainfile: ./testdata/cn.dat
|
||||
server:
|
||||
- network: tcp
|
||||
host: 114.114.114.114
|
||||
port: 53
|
||||
|
||||
- domainfile: testdata/us.dat
|
||||
server:
|
||||
- network: tcp
|
||||
host: 8.8.8.8
|
||||
port: 53
|
||||
- network: https
|
||||
host: 74.175.200.200
|
||||
port: 443
|
@ -0,0 +1,9 @@
|
||||
#asdf
|
||||
|
||||
1.2.3.1 a.com b.com c.aaa
|
||||
|
||||
1.2.9.1 addd bbb ddd aa
|
||||
::1 localhost
|
||||
127.0.0.1 localhost
|
||||
1.2.1.1 kkddaso
|
||||
# daas
|
@ -0,0 +1,12 @@
|
||||
#
|
||||
us
|
||||
b.cc
|
||||
a.cc
|
||||
|
||||
dda
|
||||
# da
|
||||
a.cf
|
||||
d.com
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue