restructure code
add tls, DoH support remove black list, per domain config change config format change log librarynghttp2
parent
667dc1ff49
commit
90d650c118
@ -1,118 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/vharitonsky/iniflags"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var bind_addr string
|
|
||||||
|
|
||||||
var default_server ArgSrvs
|
|
||||||
|
|
||||||
var srv ArgSrvs
|
|
||||||
|
|
||||||
var logfile string
|
|
||||||
|
|
||||||
type ArgSrvs []string
|
|
||||||
|
|
||||||
var DefaultServer []*UpstreamServer
|
|
||||||
|
|
||||||
var blacklist_file string
|
|
||||||
|
|
||||||
var enable_cache = false
|
|
||||||
|
|
||||||
var region_file = ""
|
|
||||||
|
|
||||||
func (s *ArgSrvs) String() string {
|
|
||||||
//Sprintf("%s", s)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ArgSrvs) Set(s1 string) error {
|
|
||||||
*s = append(*s, s1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse_flags() {
|
|
||||||
iniflags.Parse()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
for _, s := range srv {
|
|
||||||
sv, err := parse_server(s)
|
|
||||||
if err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
} else {
|
|
||||||
Servers = append(Servers, sv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dsvr := range default_server {
|
|
||||||
proto, addr, err := parse_addr(dsvr)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var c *dns.Client
|
|
||||||
if proto == "udp" {
|
|
||||||
c = client_udp
|
|
||||||
} else {
|
|
||||||
c = client_tcp
|
|
||||||
}
|
|
||||||
|
|
||||||
upsrv := &UpstreamServer{
|
|
||||||
Addr: addr,
|
|
||||||
Proto: proto,
|
|
||||||
client: c,
|
|
||||||
}
|
|
||||||
DefaultServer = append(DefaultServer, upsrv)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(DefaultServer) == 0 {
|
|
||||||
log.Fatal("please special a -upstream")
|
|
||||||
}
|
|
||||||
|
|
||||||
a, err := load_domain(blacklist_file)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
} else {
|
|
||||||
Blacklist_ips = a
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostfile == "" {
|
|
||||||
hostfile = GetHost()
|
|
||||||
}
|
|
||||||
|
|
||||||
if hostfile != "" {
|
|
||||||
record_hosts, err = ReadHosts(hostfile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if region_file != "" {
|
|
||||||
ip_region = parse_net(region_file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
flag.Var(&srv, "server", `special the filter and the upstream server to use when match
|
|
||||||
format:
|
|
||||||
FILTER_FILE_NAME,PROTOCOL:SERVER_NAME:PORT
|
|
||||||
example:
|
|
||||||
filter1.json,udp:8.8.8.8:53
|
|
||||||
means the domains in the filter1.json will use the google dns server by udp
|
|
||||||
you can specail multiple filter and upstream server
|
|
||||||
`)
|
|
||||||
|
|
||||||
flag.StringVar(&bind_addr, "bind", ":53", "the address bind to")
|
|
||||||
flag.Var(&default_server, "upstream", "special the upstream server to use")
|
|
||||||
flag.StringVar(&logfile, "logfile", "", "the logfile, default stdout")
|
|
||||||
flag.StringVar(&blacklist_file, "blacklist", "", "the blacklist file")
|
|
||||||
flag.BoolVar(&debug, "debug", false, "output debug log, default false")
|
|
||||||
flag.StringVar(&hostfile, "hosts", "", "load special ip from hosts or /etc/hosts")
|
|
||||||
flag.BoolVar(&enable_cache, "enable_cache", false, "enable cache or not")
|
|
||||||
flag.StringVar(®ion_file, "region_file", "", "local country region ip range file")
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestArgs(t *testing.T) {
|
|
||||||
var a ArgSrvs
|
|
||||||
|
|
||||||
(&a).Set("aa")
|
|
||||||
|
|
||||||
if len(a) != 1 {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a[0] != "aa" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,529 +0,0 @@
|
|||||||
{
|
|
||||||
"0.0.0.0": 1,
|
|
||||||
"1.1.1.1": 1,
|
|
||||||
"1.1.127.45": 1,
|
|
||||||
"1.1.67.51": 1,
|
|
||||||
"1.2.3.4": 1,
|
|
||||||
"1.209.208.200": 1,
|
|
||||||
"1.226.83.147": 1,
|
|
||||||
"1.234.21.83": 1,
|
|
||||||
"1.234.29.40": 1,
|
|
||||||
"1.234.39.14": 1,
|
|
||||||
"1.234.4.91": 1,
|
|
||||||
"1.234.70.80": 1,
|
|
||||||
"1.234.83.104": 1,
|
|
||||||
"1.244.115.172": 1,
|
|
||||||
"1.33.170.68": 1,
|
|
||||||
"1.33.188.62": 1,
|
|
||||||
"1.33.190.228": 1,
|
|
||||||
"1.33.190.70": 1,
|
|
||||||
"1.33.191.58": 1,
|
|
||||||
"2.1.1.2": 1,
|
|
||||||
"2.187.253.121": 1,
|
|
||||||
"2.228.123.7": 1,
|
|
||||||
"2.228.154.8": 1,
|
|
||||||
"4.17.143.131": 1,
|
|
||||||
"4.193.80.0": 1,
|
|
||||||
"4.21.70.9": 1,
|
|
||||||
"4.30.13.168": 1,
|
|
||||||
"4.30.187.9": 1,
|
|
||||||
"4.30.235.229": 1,
|
|
||||||
"4.31.139.146": 1,
|
|
||||||
"4.34.180.178": 1,
|
|
||||||
"4.35.100.20": 1,
|
|
||||||
"4.35.234.200": 1,
|
|
||||||
"4.36.66.178": 1,
|
|
||||||
"4.53.17.215": 1,
|
|
||||||
"4.59.79.206": 1,
|
|
||||||
"4.78.167.196": 1,
|
|
||||||
"4.79.129.122": 1,
|
|
||||||
"4.59.90.226": 1,
|
|
||||||
"4.59.90.212": 1,
|
|
||||||
"4.59.90.237": 1,
|
|
||||||
"4.59.90.221": 1,
|
|
||||||
"4.59.90.246": 1,
|
|
||||||
"4.59.90.232": 1,
|
|
||||||
"4.59.90.242": 1,
|
|
||||||
"4.59.90.241": 1,
|
|
||||||
"4.59.90.217": 1,
|
|
||||||
"4.59.90.227": 1,
|
|
||||||
"4.59.90.247": 1,
|
|
||||||
"4.59.90.231": 1,
|
|
||||||
"4.59.90.236": 1,
|
|
||||||
"4.59.90.251": 1,
|
|
||||||
"4.59.90.216": 1,
|
|
||||||
"4.59.90.222": 1,
|
|
||||||
|
|
||||||
"5.10.105.41": 1,
|
|
||||||
"5.10.68.187": 1,
|
|
||||||
"5.10.68.188": 1,
|
|
||||||
"5.10.69.29": 1,
|
|
||||||
"5.10.77.72": 1,
|
|
||||||
"5.100.152.24": 1,
|
|
||||||
"5.100.225.204": 1,
|
|
||||||
"5.100.228.206": 1,
|
|
||||||
"5.100.231.27": 1,
|
|
||||||
"5.100.248.208": 1,
|
|
||||||
"5.144.129.20": 1,
|
|
||||||
"5.35.251.108": 1,
|
|
||||||
"5.9.118.111": 1,
|
|
||||||
"5.9.120.140": 1,
|
|
||||||
"5.9.136.210": 1,
|
|
||||||
"5.9.242.232": 1,
|
|
||||||
"5.9.5.26": 1,
|
|
||||||
"5.9.65.105": 1,
|
|
||||||
"8.105.84.0": 1,
|
|
||||||
"8.34.161.150": 1,
|
|
||||||
"8.7.198.45": 1,
|
|
||||||
"12.87.133.0": 1,
|
|
||||||
"14.102.249.18": 1,
|
|
||||||
"16.63.155.0": 1,
|
|
||||||
"20.139.56.0": 1,
|
|
||||||
"23.23.14.192": 1,
|
|
||||||
"23.89.5.60": 1,
|
|
||||||
"24.51.184.0": 1,
|
|
||||||
"28.121.126.139": 1,
|
|
||||||
"28.13.216.0": 1,
|
|
||||||
"31.169.90.4": 1,
|
|
||||||
"31.170.8.8": 1,
|
|
||||||
"31.210.156.212": 1,
|
|
||||||
"31.22.4.60": 1,
|
|
||||||
"31.222.185.202": 1,
|
|
||||||
"31.25.191.134": 1,
|
|
||||||
"31.13.99.18": 1,
|
|
||||||
"34.254.247.151": 1,
|
|
||||||
"37.1.205.21": 1,
|
|
||||||
"37.1.207.129": 1,
|
|
||||||
"37.140.238.35": 1,
|
|
||||||
"37.187.134.150": 1,
|
|
||||||
"37.187.149.129": 1,
|
|
||||||
"37.187.251.35": 1,
|
|
||||||
"37.208.111.120": 1,
|
|
||||||
"37.252.122.184": 1,
|
|
||||||
"37.58.78.79": 1,
|
|
||||||
"37.59.25.95": 1,
|
|
||||||
"37.61.54.158": 1,
|
|
||||||
"37.99.194.148": 1,
|
|
||||||
"38.117.98.231": 1,
|
|
||||||
"41.79.20.9": 1,
|
|
||||||
"43.253.199.12": 1,
|
|
||||||
"46.137.219.7": 1,
|
|
||||||
"46.165.231.144": 1,
|
|
||||||
"46.20.126.252": 1,
|
|
||||||
"46.20.13.100": 1,
|
|
||||||
"46.229.175.95": 1,
|
|
||||||
"46.243.6.170": 1,
|
|
||||||
"46.30.212.198": 1,
|
|
||||||
"46.38.24.209": 1,
|
|
||||||
"46.82.174.68": 1,
|
|
||||||
"49.2.123.56": 1,
|
|
||||||
"49.212.153.128": 1,
|
|
||||||
"50.116.6.162": 1,
|
|
||||||
"50.18.183.233": 1,
|
|
||||||
"50.57.11.12": 1,
|
|
||||||
"50.63.202.13": 1,
|
|
||||||
"50.87.148.140": 1,
|
|
||||||
"50.87.169.77": 1,
|
|
||||||
"50.93.207.101": 1,
|
|
||||||
"50.97.134.91": 1,
|
|
||||||
"54.174.40.182": 1,
|
|
||||||
"54.187.136.30": 1,
|
|
||||||
"54.187.39.38": 1,
|
|
||||||
"54.191.193.138": 1,
|
|
||||||
"54.200.3.32": 1,
|
|
||||||
"54.206.98.127": 1,
|
|
||||||
"54.209.238.28": 1,
|
|
||||||
"54.209.87.186": 1,
|
|
||||||
"54.218.38.198": 1,
|
|
||||||
"54.229.147.183": 1,
|
|
||||||
"54.235.199.154": 1,
|
|
||||||
"54.244.22.77": 1,
|
|
||||||
"54.246.169.32": 1,
|
|
||||||
"54.246.202.250": 1,
|
|
||||||
"54.68.166.130": 1,
|
|
||||||
"54.76.135.1": 1,
|
|
||||||
"54.83.51.191": 1,
|
|
||||||
"54.86.21.64": 1,
|
|
||||||
"54.86.223.202": 1,
|
|
||||||
"54.88.252.91": 1,
|
|
||||||
"59.124.74.28": 1,
|
|
||||||
"59.24.3.173": 1,
|
|
||||||
"61.54.28.6": 1,
|
|
||||||
"62.138.115.35": 1,
|
|
||||||
"62.75.221.31": 1,
|
|
||||||
"62.92.17.213": 1,
|
|
||||||
"64.14.72.41": 1,
|
|
||||||
"64.150.184.98": 1,
|
|
||||||
"64.22.110.34": 1,
|
|
||||||
"64.33.88.161": 1,
|
|
||||||
"64.33.99.47": 1,
|
|
||||||
"64.34.161.142": 1,
|
|
||||||
"64.50.179.133": 1,
|
|
||||||
"64.66.163.251": 1,
|
|
||||||
"64.79.69.250": 1,
|
|
||||||
"64.79.84.141": 1,
|
|
||||||
"64.91.254.97": 1,
|
|
||||||
"65.104.202.252": 1,
|
|
||||||
"65.160.219.113": 1,
|
|
||||||
"65.183.39.139": 1,
|
|
||||||
"66.146.2.241": 1,
|
|
||||||
"66.187.204.50": 1,
|
|
||||||
"66.206.11.194": 1,
|
|
||||||
"66.39.61.161": 1,
|
|
||||||
"66.45.252.237": 1,
|
|
||||||
"66.55.151.148": 1,
|
|
||||||
"66.85.134.186": 1,
|
|
||||||
"66.96.147.160": 1,
|
|
||||||
"67.137.227.11": 1,
|
|
||||||
"67.225.220.248": 1,
|
|
||||||
"68.71.58.18": 1,
|
|
||||||
"69.16.196.113": 1,
|
|
||||||
"69.167.172.162": 1,
|
|
||||||
"69.171.13.49": 1,
|
|
||||||
"69.174.244.221": 1,
|
|
||||||
"69.175.75.202": 1,
|
|
||||||
"69.195.124.90": 1,
|
|
||||||
"69.30.23.10": 1,
|
|
||||||
"69.50.192.218": 1,
|
|
||||||
"69.61.60.122": 1,
|
|
||||||
"70.42.243.33": 1,
|
|
||||||
"72.14.205.104": 1,
|
|
||||||
"72.14.205.99": 1,
|
|
||||||
"72.167.32.10": 1,
|
|
||||||
"72.20.110.50": 1,
|
|
||||||
"72.29.94.240": 1,
|
|
||||||
"72.32.4.243": 1,
|
|
||||||
"72.47.228.79": 1,
|
|
||||||
"72.5.1.109": 1,
|
|
||||||
"72.52.244.56": 1,
|
|
||||||
"74.117.117.122": 1,
|
|
||||||
"74.117.57.138": 1,
|
|
||||||
"74.124.195.73": 1,
|
|
||||||
"74.125.127.102": 1,
|
|
||||||
"74.125.155.102": 1,
|
|
||||||
"74.125.204.121": 1,
|
|
||||||
"74.125.39.102": 1,
|
|
||||||
"74.125.39.113": 1,
|
|
||||||
"74.207.236.174": 1,
|
|
||||||
"74.208.125.184": 1,
|
|
||||||
"74.220.215.67": 1,
|
|
||||||
"74.82.166.166": 1,
|
|
||||||
"75.98.175.166": 1,
|
|
||||||
"76.164.217.116": 1,
|
|
||||||
"77.4.7.92": 1,
|
|
||||||
"78.108.178.26": 1,
|
|
||||||
"78.140.172.33": 1,
|
|
||||||
"78.16.49.15": 1,
|
|
||||||
"78.24.135.99": 1,
|
|
||||||
"79.127.127.68": 1,
|
|
||||||
"79.136.125.49": 1,
|
|
||||||
"79.98.34.60": 1,
|
|
||||||
"80.190.96.26": 1,
|
|
||||||
"80.241.209.19": 1,
|
|
||||||
"80.241.92.180": 1,
|
|
||||||
"80.245.171.70": 1,
|
|
||||||
"80.70.184.118": 1,
|
|
||||||
"80.72.41.146": 1,
|
|
||||||
"80.82.117.209": 1,
|
|
||||||
"80.82.201.154": 1,
|
|
||||||
"80.92.117.132": 1,
|
|
||||||
"82.145.47.117": 1,
|
|
||||||
"83.125.118.122": 1,
|
|
||||||
"83.222.124.187": 1,
|
|
||||||
"83.222.5.171": 1,
|
|
||||||
"84.124.59.165": 1,
|
|
||||||
"85.111.18.138": 1,
|
|
||||||
"85.190.0.110": 1,
|
|
||||||
"85.25.171.103": 1,
|
|
||||||
"85.92.134.229": 1,
|
|
||||||
"87.106.57.209": 1,
|
|
||||||
"87.230.46.50": 1,
|
|
||||||
"88.198.69.101": 1,
|
|
||||||
"88.214.195.67": 1,
|
|
||||||
"89.108.118.129": 1,
|
|
||||||
"89.111.181.74": 1,
|
|
||||||
"89.186.95.11": 1,
|
|
||||||
"89.30.125.204": 1,
|
|
||||||
"89.31.55.106": 1,
|
|
||||||
"90.156.201.42": 1,
|
|
||||||
"91.121.245.154": 1,
|
|
||||||
"91.186.28.41": 1,
|
|
||||||
"91.198.129.47": 1,
|
|
||||||
"91.217.73.22": 1,
|
|
||||||
"91.221.37.35": 1,
|
|
||||||
"91.223.175.25": 1,
|
|
||||||
"91.238.30.54": 1,
|
|
||||||
"91.239.201.16": 1,
|
|
||||||
"92.53.106.175": 1,
|
|
||||||
"92.53.96.9": 1,
|
|
||||||
"92.63.110.174": 1,
|
|
||||||
"93.115.240.148": 1,
|
|
||||||
"93.158.121.72": 1,
|
|
||||||
"93.187.205.2": 1,
|
|
||||||
"93.46.8.89": 1,
|
|
||||||
"93.93.187.49": 1,
|
|
||||||
"94.136.188.30": 1,
|
|
||||||
"94.141.31.140": 1,
|
|
||||||
"94.23.147.142": 1,
|
|
||||||
"94.23.156.11": 1,
|
|
||||||
"94.23.193.224": 1,
|
|
||||||
"94.23.199.144": 1,
|
|
||||||
"95.163.95.47": 1,
|
|
||||||
"95.211.150.70": 1,
|
|
||||||
"95.211.229.156": 1,
|
|
||||||
"95.211.58.97": 1,
|
|
||||||
"95.85.22.163": 1,
|
|
||||||
"96.126.97.15": 1,
|
|
||||||
"96.127.172.221": 1,
|
|
||||||
"96.30.51.148": 1,
|
|
||||||
"97.74.80.22": 1,
|
|
||||||
"98.129.229.202": 1,
|
|
||||||
"98.158.152.159": 1,
|
|
||||||
"98.158.178.141": 1,
|
|
||||||
"104.200.31.226": 1,
|
|
||||||
"104.28.1.22": 1,
|
|
||||||
"104.28.14.112": 1,
|
|
||||||
"104.28.20.14": 1,
|
|
||||||
"104.28.30.59": 1,
|
|
||||||
"106.186.120.157": 1,
|
|
||||||
"106.187.39.80": 1,
|
|
||||||
"107.6.34.101": 1,
|
|
||||||
"108.168.215.230": 1,
|
|
||||||
"108.168.250.3": 1,
|
|
||||||
"108.179.196.77": 1,
|
|
||||||
"108.179.250.106": 1,
|
|
||||||
"108.61.250.218": 1,
|
|
||||||
"109.123.115.205": 1,
|
|
||||||
"109.206.173.212": 1,
|
|
||||||
"109.234.159.38": 1,
|
|
||||||
"109.71.81.130": 1,
|
|
||||||
"110.173.154.142": 1,
|
|
||||||
"110.74.163.40": 1,
|
|
||||||
"112.175.60.31": 1,
|
|
||||||
"113.11.194.190": 1,
|
|
||||||
"113.160.102.90": 1,
|
|
||||||
"118.145.17.184": 1,
|
|
||||||
"118.219.253.245": 1,
|
|
||||||
"118.5.49.6": 1,
|
|
||||||
"119.18.62.130": 1,
|
|
||||||
"119.235.57.82": 1,
|
|
||||||
"119.245.217.155": 1,
|
|
||||||
"119.9.94.83": 1,
|
|
||||||
"120.196.0.5": 1,
|
|
||||||
"120.198.243.113": 1,
|
|
||||||
"120.198.243.114": 1,
|
|
||||||
"120.198.243.115": 1,
|
|
||||||
"120.198.243.116": 1,
|
|
||||||
"120.198.243.14": 1,
|
|
||||||
"120.198.243.15": 1,
|
|
||||||
"120.198.243.151": 1,
|
|
||||||
"120.198.243.48": 1,
|
|
||||||
"120.198.243.52": 1,
|
|
||||||
"120.198.243.53": 1,
|
|
||||||
"120.197.234.71": 1,
|
|
||||||
"120.89.93.248": 1,
|
|
||||||
"122.214.2.171": 1,
|
|
||||||
"122.218.101.190": 1,
|
|
||||||
"123.126.249.238": 1,
|
|
||||||
"123.30.175.29": 1,
|
|
||||||
"123.50.49.171": 1,
|
|
||||||
"125.230.148.48": 1,
|
|
||||||
"127.0.0.2": 1,
|
|
||||||
"128.121.126.139": 1,
|
|
||||||
"128.199.180.162": 1,
|
|
||||||
"133.192.181.66": 1,
|
|
||||||
"133.242.165.24": 1,
|
|
||||||
"133.42.48.3": 1,
|
|
||||||
"137.135.129.175": 1,
|
|
||||||
"141.101.118.102": 1,
|
|
||||||
"141.8.195.47": 1,
|
|
||||||
"141.8.195.78": 1,
|
|
||||||
"141.8.225.80": 1,
|
|
||||||
"142.4.5.109": 1,
|
|
||||||
"144.76.106.232": 1,
|
|
||||||
"144.76.127.114": 1,
|
|
||||||
"144.76.21.13": 1,
|
|
||||||
"145.253.183.23": 1,
|
|
||||||
"147.87.244.32": 1,
|
|
||||||
"155.92.182.118": 1,
|
|
||||||
"157.205.32.64": 1,
|
|
||||||
"157.7.143.209": 1,
|
|
||||||
"159.106.121.75": 1,
|
|
||||||
"159.24.3.173": 1,
|
|
||||||
"159.253.20.179": 1,
|
|
||||||
"159.50.88.77": 1,
|
|
||||||
"162.159.243.101": 1,
|
|
||||||
"162.243.137.163": 1,
|
|
||||||
"162.253.33.134": 1,
|
|
||||||
"164.109.96.232": 1,
|
|
||||||
"164.138.221.68": 1,
|
|
||||||
"168.156.168.21": 1,
|
|
||||||
"169.132.13.103": 1,
|
|
||||||
"171.17.130.53": 1,
|
|
||||||
"171.25.204.141": 1,
|
|
||||||
"173.192.219.59": 1,
|
|
||||||
"173.194.127.144": 1,
|
|
||||||
"173.201.216.6": 1,
|
|
||||||
"173.224.209.14": 1,
|
|
||||||
"173.236.228.108": 1,
|
|
||||||
"173.244.184.10": 1,
|
|
||||||
"173.255.194.174": 1,
|
|
||||||
"173.255.230.196": 1,
|
|
||||||
"174.142.113.142": 1,
|
|
||||||
"174.142.22.25": 1,
|
|
||||||
"176.10.37.81": 1,
|
|
||||||
"176.57.216.145": 1,
|
|
||||||
"178.18.82.216": 1,
|
|
||||||
"178.236.177.77": 1,
|
|
||||||
"178.32.111.136": 1,
|
|
||||||
"178.32.156.59": 1,
|
|
||||||
"178.32.247.82": 1,
|
|
||||||
"178.33.212.162": 1,
|
|
||||||
"178.49.132.135": 1,
|
|
||||||
"178.62.242.156": 1,
|
|
||||||
"178.62.75.99": 1,
|
|
||||||
"178.79.182.248": 1,
|
|
||||||
"180.153.225.168": 1,
|
|
||||||
"180.179.171.121": 1,
|
|
||||||
"180.87.182.227": 1,
|
|
||||||
"181.224.155.41": 1,
|
|
||||||
"183.111.141.95": 1,
|
|
||||||
"184.154.10.146": 1,
|
|
||||||
"184.169.132.244": 1,
|
|
||||||
"184.72.253.232": 1,
|
|
||||||
"185.25.150.45": 1,
|
|
||||||
"185.53.61.50": 1,
|
|
||||||
"188.132.250.186": 1,
|
|
||||||
"188.165.31.24": 1,
|
|
||||||
"188.226.207.251": 1,
|
|
||||||
"188.40.108.13": 1,
|
|
||||||
"188.5.4.96": 1,
|
|
||||||
"189.163.17.5": 1,
|
|
||||||
"192.104.44.6": 1,
|
|
||||||
"192.121.151.106": 1,
|
|
||||||
"192.67.198.6": 1,
|
|
||||||
"192.95.98.202": 1,
|
|
||||||
"193.105.145.158": 1,
|
|
||||||
"193.169.66.88": 1,
|
|
||||||
"193.203.48.18": 1,
|
|
||||||
"193.234.233.149": 1,
|
|
||||||
"193.238.151.98": 1,
|
|
||||||
"193.239.132.44": 1,
|
|
||||||
"193.48.96.218": 1,
|
|
||||||
"193.57.244.117": 1,
|
|
||||||
"193.91.26.132": 1,
|
|
||||||
"194.149.250.20": 1,
|
|
||||||
"194.185.115.1": 1,
|
|
||||||
"194.187.94.6": 1,
|
|
||||||
"194.67.144.70": 1,
|
|
||||||
"195.146.235.33": 1,
|
|
||||||
"195.149.210.211": 1,
|
|
||||||
"195.154.243.151": 1,
|
|
||||||
"195.191.149.103": 1,
|
|
||||||
"195.2.88.68": 1,
|
|
||||||
"195.211.72.200": 1,
|
|
||||||
"195.43.82.170": 1,
|
|
||||||
"195.49.201.30": 1,
|
|
||||||
"195.50.195.15": 1,
|
|
||||||
"195.74.38.62": 1,
|
|
||||||
"195.74.78.21": 1,
|
|
||||||
"195.77.241.242": 1,
|
|
||||||
"195.8.125.64": 1,
|
|
||||||
"197.4.4.12": 1,
|
|
||||||
"198.143.143.36": 1,
|
|
||||||
"198.57.205.133": 1,
|
|
||||||
"198.57.222.88": 1,
|
|
||||||
"198.58.124.68": 1,
|
|
||||||
"199.167.31.142": 1,
|
|
||||||
"199.21.68.222": 1,
|
|
||||||
"199.79.63.83": 1,
|
|
||||||
"200.229.206.115": 1,
|
|
||||||
"200.98.234.14": 1,
|
|
||||||
"201.77.211.143": 1,
|
|
||||||
"202.106.1.2": 1,
|
|
||||||
"202.181.7.85": 1,
|
|
||||||
"202.218.219.10": 1,
|
|
||||||
"202.6.96.25": 1,
|
|
||||||
"203.113.173.22": 1,
|
|
||||||
"203.133.238.172": 1,
|
|
||||||
"203.161.230.171": 1,
|
|
||||||
"203.199.57.81": 1,
|
|
||||||
"203.98.7.65": 1,
|
|
||||||
"206.108.51.91": 1,
|
|
||||||
"206.113.150.70": 1,
|
|
||||||
"207.12.88.98": 1,
|
|
||||||
"207.126.59.27": 1,
|
|
||||||
"207.140.149.247": 1,
|
|
||||||
"207.58.177.166": 1,
|
|
||||||
"208.109.138.55": 1,
|
|
||||||
"208.109.205.232": 1,
|
|
||||||
"208.112.102.122": 1,
|
|
||||||
"208.43.134.107": 1,
|
|
||||||
"208.43.33.194": 1,
|
|
||||||
"208.56.31.43": 1,
|
|
||||||
"208.73.211.164": 1,
|
|
||||||
"208.86.154.112": 1,
|
|
||||||
"208.93.0.150": 1,
|
|
||||||
"209.116.71.109": 1,
|
|
||||||
"209.126.106.182": 1,
|
|
||||||
"209.141.48.35": 1,
|
|
||||||
"209.145.54.50": 1,
|
|
||||||
"209.188.7.186": 1,
|
|
||||||
"209.204.148.22": 1,
|
|
||||||
"209.220.30.174": 1,
|
|
||||||
"209.235.224.25": 1,
|
|
||||||
"209.36.73.33": 1,
|
|
||||||
"209.43.1.130": 1,
|
|
||||||
"209.56.158.42": 1,
|
|
||||||
"209.62.154.94": 1,
|
|
||||||
"209.85.229.138": 1,
|
|
||||||
"210.175.255.154": 1,
|
|
||||||
"210.209.110.199": 1,
|
|
||||||
"210.230.192.183": 1,
|
|
||||||
"211.139.136.73": 1,
|
|
||||||
"211.139.204.59": 1,
|
|
||||||
"211.43.203.33": 1,
|
|
||||||
"211.5.133.18": 1,
|
|
||||||
"211.8.69.27": 1,
|
|
||||||
"211.94.66.147": 1,
|
|
||||||
"212.227.98.130": 1,
|
|
||||||
"212.45.52.219": 1,
|
|
||||||
"212.68.42.67": 1,
|
|
||||||
"212.77.104.29": 1,
|
|
||||||
"213.108.66.21": 1,
|
|
||||||
"213.133.111.102": 1,
|
|
||||||
"213.169.251.35": 1,
|
|
||||||
"213.174.158.108": 1,
|
|
||||||
"213.186.33.5": 1,
|
|
||||||
"213.19.161.141": 1,
|
|
||||||
"213.207.85.148": 1,
|
|
||||||
"213.238.166.227": 1,
|
|
||||||
"216.12.205.2": 1,
|
|
||||||
"216.139.213.144": 1,
|
|
||||||
"216.178.241.101": 1,
|
|
||||||
"216.198.246.103": 1,
|
|
||||||
"216.221.188.182": 1,
|
|
||||||
"216.234.179.13": 1,
|
|
||||||
"216.250.115.144": 1,
|
|
||||||
"216.38.0.92": 1,
|
|
||||||
"216.70.88.29": 1,
|
|
||||||
"216.92.58.37": 1,
|
|
||||||
"217.160.42.85": 1,
|
|
||||||
"217.172.183.9": 1,
|
|
||||||
"217.30.184.161": 1,
|
|
||||||
"218.44.251.212": 1,
|
|
||||||
"220.110.150.90": 1,
|
|
||||||
"220.247.224.8": 1,
|
|
||||||
"220.250.64.24": 1,
|
|
||||||
"221.213.49.149": 1,
|
|
||||||
"221.179.46.190": 1,
|
|
||||||
"221.179.46.194": 1,
|
|
||||||
"221.8.69.27": 1,
|
|
||||||
"222.122.56.219": 1,
|
|
||||||
"243.185.187.3": 1,
|
|
||||||
"243.185.187.30": 1,
|
|
||||||
"243.185.187.39": 1,
|
|
||||||
"249.129.46.48": 1,
|
|
||||||
"253.157.14.165": 1,
|
|
||||||
"255.255.255.255": 1
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
//"fmt"
|
|
||||||
"errors"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ip_region []*net.IPNet
|
|
||||||
|
|
||||||
type res struct {
|
|
||||||
m *dns.Msg
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func _query(m *dns.Msg, s *UpstreamServer, c chan *res) {
|
|
||||||
res1 := make(chan *res)
|
|
||||||
|
|
||||||
go query_one(s, m, res1)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case r := <-res1:
|
|
||||||
c <- r
|
|
||||||
case <-time.After(600 * time.Millisecond):
|
|
||||||
c <- &res{err: errors.New("timed out")}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func query(m *dns.Msg) *dns.Msg {
|
|
||||||
resch := make(chan *res, len(DefaultServer))
|
|
||||||
for _, s := range DefaultServer {
|
|
||||||
go _query(m, s, resch)
|
|
||||||
}
|
|
||||||
|
|
||||||
delayed := []*dns.Msg{}
|
|
||||||
slen := len(DefaultServer)
|
|
||||||
|
|
||||||
for i := 0; i < slen; i++ {
|
|
||||||
r := <-resch
|
|
||||||
r1 := *r
|
|
||||||
if r1.err != nil {
|
|
||||||
logger.Error("error %s\n", r1.err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop the result with no error but has an empty result
|
|
||||||
if r1.m.Rcode == dns.RcodeSuccess &&
|
|
||||||
len(r1.m.Answer) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop blacklist
|
|
||||||
if in_blacklist(r1.m) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check ip region
|
|
||||||
if answer_in_region(r1.m, ip_region) {
|
|
||||||
return r1.m
|
|
||||||
} else {
|
|
||||||
delayed = append(delayed, r1.m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(delayed) == 0 {
|
|
||||||
logger.Error("empty delayed list")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// return first ok result
|
|
||||||
for _, m1 := range delayed {
|
|
||||||
if m1.Rcode == dns.RcodeSuccess {
|
|
||||||
return m1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return NXDOMAIN result
|
|
||||||
for _, m1 := range delayed {
|
|
||||||
if m1.Rcode == dns.RcodeNameError {
|
|
||||||
return m1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// errror
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func query_one(srv *UpstreamServer, m *dns.Msg, ch chan *res) {
|
|
||||||
m1, err := srv.query(m)
|
|
||||||
select {
|
|
||||||
case ch <- &res{m1, err}:
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/go-yaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type conf struct {
|
||||||
|
UpstreamServers []string `yaml:"upstream_servers"`
|
||||||
|
BootstrapServers []string `yaml:"bootstrap_servers"`
|
||||||
|
Listen []listen `yaml:"listen"`
|
||||||
|
UpstreamTimeout int `yaml:"upstream_timeout"`
|
||||||
|
UpstreamInsecure bool `yaml:"upstream_insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type listen struct {
|
||||||
|
Addr string `yaml:"addr"`
|
||||||
|
Cert string `yaml:"cert"`
|
||||||
|
Key string `yaml:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(f string) (*conf, error) {
|
||||||
|
data, err := ioutil.ReadFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var c conf
|
||||||
|
if err := yaml.Unmarshal(data, &c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
# upstream servers
|
||||||
|
# support DoH(DNS over https/http2), tcp, udp and tls(dns over tls)
|
||||||
|
upstream_servers:
|
||||||
|
- https://cloudflare-dns.com/dns-query
|
||||||
|
- tcp://1.0.0.1:53
|
||||||
|
- udp://1.0.0.1:53
|
||||||
|
- tls://cloudflare-dns:853
|
||||||
|
|
||||||
|
# bootstrap dns server,
|
||||||
|
# this used to resolve the domain name used in Doh and TLS,
|
||||||
|
# not needed if you don't use DoH and tls
|
||||||
|
bootstrap_severs:
|
||||||
|
- tcp://1.0.0.1:53
|
||||||
|
- udp://1.0.0.1:53
|
||||||
|
|
||||||
|
# listen ports
|
||||||
|
listen:
|
||||||
|
-
|
||||||
|
addr: tcp://0.0.0.0:1053
|
||||||
|
cert:
|
||||||
|
key:
|
||||||
|
-
|
||||||
|
addr: udp://0.0.0.0:1053
|
||||||
|
cert:
|
||||||
|
key:
|
||||||
|
-
|
||||||
|
addr: https://0.0.0.0:1054/dns-query
|
||||||
|
cert: server.crt
|
||||||
|
key: server.key
|
||||||
|
-
|
||||||
|
addr: tls://0.0.0.1:1055
|
||||||
|
cert: server.crt
|
||||||
|
key: server.key
|
||||||
|
|
||||||
|
# timeout for upstream server, second
|
||||||
|
upstream_timeout: 3
|
||||||
|
|
||||||
|
# check upstream certificate or not
|
||||||
|
# if you use self signed certificate, set this to true
|
||||||
|
upstream_insecure: true
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"211.139.136.73":1
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
bind = :53 # the address bind to
|
|
||||||
configUpdateInterval = 0
|
|
||||||
upstream = udp:114.114.114.114:53
|
|
||||||
server = domains.txt,udp:8.8.8.8:53
|
|
||||||
server = domains.txt,tcp:4.2.2.2:53
|
|
||||||
#logfile = error.log
|
|
||||||
blacklist = blacklist.txt
|
|
||||||
debug = true
|
|
@ -1,107 +0,0 @@
|
|||||||
{
|
|
||||||
"xunlei.com":1,
|
|
||||||
"getlantern.org":1,
|
|
||||||
"greatfire.org":1,
|
|
||||||
"tietuku.com":1,
|
|
||||||
"dw.com":1,
|
|
||||||
"igfw.net":1,
|
|
||||||
"rfa.org":1,
|
|
||||||
"intercomcdn.com":1,
|
|
||||||
"gorillatoolkit.org":1,
|
|
||||||
"shadowsocks.org":1,
|
|
||||||
"openvpn.net":1,
|
|
||||||
"golang.org":1,
|
|
||||||
"reuters.com":1,
|
|
||||||
"lwgod.com":1,
|
|
||||||
"buzzfed.com":1,
|
|
||||||
"chinagfw.org":1,
|
|
||||||
"feedburner.com":1,
|
|
||||||
"bbc.com":1,
|
|
||||||
"fanqianghou.com":1,
|
|
||||||
"qiwen.lu":1,
|
|
||||||
"dropbox.com":1,
|
|
||||||
"laonanren.com":1,
|
|
||||||
"wikipedia.org":1,
|
|
||||||
"powerapple.com":1,
|
|
||||||
"ift.tt":1,
|
|
||||||
"bowenpress.com":1,
|
|
||||||
"assets-cdn.github.com":1,
|
|
||||||
"bx.tl":1,
|
|
||||||
"googlecode.com":1,
|
|
||||||
"gist.github.com":1,
|
|
||||||
"googleapis.com":1,
|
|
||||||
"sharethis.com":1,
|
|
||||||
"googletagservices.com":1,
|
|
||||||
"ablwang.com":1,
|
|
||||||
"githubusercontent.com":1,
|
|
||||||
"wordpress.com":1,
|
|
||||||
"linkedin.com":1,
|
|
||||||
"wsj.net":1,
|
|
||||||
"wsj.com":1,
|
|
||||||
"flickr.com":1,
|
|
||||||
"w3schools.com":1,
|
|
||||||
"hikinggfw.org":1,
|
|
||||||
"imgur.com":1,
|
|
||||||
"akamai.net":1,
|
|
||||||
"outbrain.com":1,
|
|
||||||
"amazonaws.com":1,
|
|
||||||
"wp.com":1,
|
|
||||||
"goo.gl":1,
|
|
||||||
"aboluowang.com":1,
|
|
||||||
"youmaker.com":1,
|
|
||||||
"gravatar.com":1,
|
|
||||||
"cmake.org":1,
|
|
||||||
"sourceforge.net":1,
|
|
||||||
"idv.tw":1,
|
|
||||||
"ntdtv.com":1,
|
|
||||||
"sourceforge.net":1,
|
|
||||||
"fbcdn.net":1,
|
|
||||||
"dw.de":1,
|
|
||||||
"imgur.com":1,
|
|
||||||
"bit.ly":1,
|
|
||||||
"fw.cm":1,
|
|
||||||
"cdninstagram.com":1,
|
|
||||||
"udn.com.tw":1,
|
|
||||||
"instagram.com":1,
|
|
||||||
"ltn.com.tw":1,
|
|
||||||
"wp.com":1,
|
|
||||||
"boxun.com":1,
|
|
||||||
"xys.org":1,
|
|
||||||
"allinfa.com":1,
|
|
||||||
"facebook.net":1,
|
|
||||||
"atgfw.org":1,
|
|
||||||
"pao-pao.net":1,
|
|
||||||
"torproject.org":1,
|
|
||||||
"nytimes.com":1,
|
|
||||||
"ifanqiang.com":1,
|
|
||||||
"googlevideo.com":1,
|
|
||||||
"freeweibo.com":1,
|
|
||||||
"ggpht.com":1,
|
|
||||||
"ytimg.com":1,
|
|
||||||
"renminbao.com":1,
|
|
||||||
"appspot.com":1,
|
|
||||||
"chinadigitaltimes.net":1,
|
|
||||||
"letscorp.net":1,
|
|
||||||
"t.co":1,
|
|
||||||
"dongtaiwang.com":1,
|
|
||||||
"kanzhongguo.com":1,
|
|
||||||
"youtube.com":1,
|
|
||||||
"haxx.se":1,
|
|
||||||
"feedly.com":1,
|
|
||||||
"peacehall.com":1,
|
|
||||||
"voachinese.com":1,
|
|
||||||
"voanews.com":1,
|
|
||||||
"secretchina.com":1,
|
|
||||||
"epochtimes.com":1,
|
|
||||||
"gstatic.com":1,
|
|
||||||
"facebook.com":1,
|
|
||||||
"blogblog.com":1,
|
|
||||||
"blogspot.com":1,
|
|
||||||
"googleusercontent.com":1,
|
|
||||||
"blogger.com":1,
|
|
||||||
"chromium.org":1,
|
|
||||||
"twimg.com":1,
|
|
||||||
"twitter.com":1,
|
|
||||||
"t66y.com":1,
|
|
||||||
"google.com":1
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
command line options
|
|
||||||
=====================
|
|
||||||
|
|
||||||
###bind###
|
|
||||||
|
|
||||||
special the addres the dns server listen to
|
|
||||||
|
|
||||||
example:
|
|
||||||
`-bind 0.0.0.0:53`,
|
|
||||||
`-bind :53`,
|
|
||||||
`-bind 127.0.0.1:53`
|
|
||||||
|
|
||||||
###server###
|
|
||||||
|
|
||||||
special a filter file and the upstream dns server to use
|
|
||||||
format
|
|
||||||
|
|
||||||
**file_name**,**proto**:**addr**:**port**
|
|
||||||
|
|
||||||
**file_name** is the file name contains the domain list
|
|
||||||
|
|
||||||
**proto** is the upstream dns server protocol, `tcp` or `udp`
|
|
||||||
|
|
||||||
**addr** is the ip address of upstream dns server
|
|
||||||
|
|
||||||
**port** is the upstream dns server port
|
|
||||||
|
|
||||||
this options can special multipe times
|
|
||||||
|
|
||||||
example:
|
|
||||||
|
|
||||||
`-server domain1.json:udp:8.8.8.8:53`,
|
|
||||||
|
|
||||||
`-server domain1.json:tcp:4.2.2.2:53`,
|
|
||||||
|
|
||||||
`-server domin2.json:udp:49.32.34.44:5353`
|
|
||||||
|
|
||||||
see [example filter file](ex_config.md#filter-file)
|
|
||||||
|
|
||||||
###upstream###
|
|
||||||
|
|
||||||
special the default upstream dns server
|
|
||||||
|
|
||||||
format
|
|
||||||
|
|
||||||
**proto**:**addr**:**port**
|
|
||||||
|
|
||||||
**proto** is the upstream dns server protocol, `tcp` or `udp`
|
|
||||||
|
|
||||||
**addr** is the ip address of upstream dns server
|
|
||||||
|
|
||||||
**port** is the upstream dns server port
|
|
||||||
|
|
||||||
example:
|
|
||||||
|
|
||||||
`-upstream udp:10.10.1.1:53`
|
|
||||||
|
|
||||||
###logfile###
|
|
||||||
|
|
||||||
special the file name the log save to
|
|
||||||
|
|
||||||
example:
|
|
||||||
|
|
||||||
`-logfile /var/log/gdns.log`
|
|
||||||
|
|
||||||
###debug###
|
|
||||||
|
|
||||||
output the debug log or not, default false
|
|
||||||
|
|
||||||
this options is only used for debugging
|
|
||||||
|
|
||||||
###blacklist###
|
|
||||||
|
|
||||||
special the blacklist file
|
|
||||||
|
|
||||||
if the reply dns message contains the ip in the blacklist, the message will be dropped
|
|
||||||
|
|
||||||
example:
|
|
||||||
|
|
||||||
`-blacklist fakeip.json`
|
|
||||||
|
|
||||||
see [example black list](ex_config.md#blacklist-file)
|
|
@ -1,61 +0,0 @@
|
|||||||
Configure example
|
|
||||||
=============
|
|
||||||
|
|
||||||
Configure file
|
|
||||||
======
|
|
||||||
Use `gdns -h > config.ini` generate a example configure file
|
|
||||||
|
|
||||||
a configure file like this:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
bind = :53 # the address bind to
|
|
||||||
blacklist = # the blacklist file
|
|
||||||
configUpdateInterval = 0 # Update interval for re-reading config file set via -config flag. Zero disables config file re-reading.
|
|
||||||
debug = false # output debug log, default false
|
|
||||||
logfile = error.log # the logfile, default stdout
|
|
||||||
server = filter1.txt,udp:8.8.8.8:53 # special the filter and the upstream server to use when match
|
|
||||||
# format:
|
|
||||||
# FILTER_FILE_NAME,PROTOCOL:SERVER_NAME:PORT
|
|
||||||
# example:
|
|
||||||
# filter1.json,udp:8.8.8.8:53
|
|
||||||
# means the domains in the filter1.json will use the google dns server by udp
|
|
||||||
# you can specail multiple filter and upstream server
|
|
||||||
#
|
|
||||||
upstream = udp:114.114.114.114:53 # the default upstream server to use
|
|
||||||
```
|
|
||||||
comamnd
|
|
||||||
`gdns -config dns.ini`
|
|
||||||
use the dns.ini as a configure file
|
|
||||||
|
|
||||||
Filter file
|
|
||||||
===========
|
|
||||||
The filter file is a domains name list
|
|
||||||
|
|
||||||
command line
|
|
||||||
`--server domain1.json,udp:8.8.8.8:53`
|
|
||||||
means the domain name listed in domoin1.json will use 8.8.8.8 as the upstream server through udp
|
|
||||||
|
|
||||||
a filter file like this
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"twitter.com":1,
|
|
||||||
"facebook.com":1,
|
|
||||||
"google.com":1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
you can special multiple filter file and upstream dns server
|
|
||||||
|
|
||||||
Blacklist file
|
|
||||||
=============
|
|
||||||
The blacklist file contains the ip that the message will be dropped when the ip dispeared in the upstream server reply
|
|
||||||
|
|
||||||
the blacklist file like this
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"113.123.21.43":1,
|
|
||||||
"31.53.23.12":1
|
|
||||||
}
|
|
||||||
```
|
|
@ -1,54 +0,0 @@
|
|||||||
# gdns
|
|
||||||
a dns proxy server write by go
|
|
||||||
|
|
||||||
gdns much like dnsmasq or chinadns, but it can run on windows.
|
|
||||||
|
|
||||||
Features
|
|
||||||
========
|
|
||||||
|
|
||||||
support different domains use different upstream dns servers
|
|
||||||
|
|
||||||
support contact to the upstream dns server by tcp or udp
|
|
||||||
|
|
||||||
support blacklist list to block the fake ip
|
|
||||||
|
|
||||||
Install
|
|
||||||
=======
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# get the depended library
|
|
||||||
go get github.com/miekg/dns
|
|
||||||
go get github.com/vharitonsky/iniflags
|
|
||||||
|
|
||||||
git clone https://github.com/fangdingjun/gdns
|
|
||||||
cd gdns
|
|
||||||
go build
|
|
||||||
|
|
||||||
# generate a sample config file
|
|
||||||
./gdns -dumpflags > dns.ini
|
|
||||||
|
|
||||||
# edit the dns.ini and run, need root privileges to bind on port 53
|
|
||||||
sudo ./gdns -config dns.ini
|
|
||||||
|
|
||||||
# test it
|
|
||||||
dig @localhost twitter.com
|
|
||||||
```
|
|
||||||
|
|
||||||
Arguments
|
|
||||||
===========
|
|
||||||
|
|
||||||
use `gdns -h` to show the command line arguments.
|
|
||||||
|
|
||||||
all arguments can specialed in config file or in command line.
|
|
||||||
|
|
||||||
there is a sample file in the `config_sample/` directory.
|
|
||||||
|
|
||||||
Third-part library
|
|
||||||
==================
|
|
||||||
use
|
|
||||||
[dns](https://github.com/miekg/dns)
|
|
||||||
library to parse the dns message
|
|
||||||
|
|
||||||
use
|
|
||||||
[iniflags](https://github.com/vharitonsky/iniflags)
|
|
||||||
library to process the command line arguments and the config file
|
|
File diff suppressed because it is too large
Load Diff
@ -1,36 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import math
|
|
||||||
import netaddr
|
|
||||||
import urllib2
|
|
||||||
url = "http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest"
|
|
||||||
data = urllib2.urlopen(url).read()
|
|
||||||
|
|
||||||
with open("delegated-apnic-lastest", "w") as fp:
|
|
||||||
fp.write(data)
|
|
||||||
|
|
||||||
results = []
|
|
||||||
with open("delegated-apnic-latest") as fp:
|
|
||||||
for line in fp:
|
|
||||||
# format
|
|
||||||
# apnic|CN|ipv4|1.0.0.0|256|2012232|allocated
|
|
||||||
l = line.strip()
|
|
||||||
if not l:
|
|
||||||
continue
|
|
||||||
if l[0] == '#':
|
|
||||||
continue
|
|
||||||
lns = l.split("|")
|
|
||||||
if len(lns) != 7:
|
|
||||||
continue
|
|
||||||
if lns[2] != 'ipv4':
|
|
||||||
continue
|
|
||||||
if lns[1] != 'CN':
|
|
||||||
continue
|
|
||||||
ip = lns[3]
|
|
||||||
mask = 32-int(math.log(int(lns[4]), 2))
|
|
||||||
cidr = "%s/%d" % (ip, mask)
|
|
||||||
results.append(cidr)
|
|
||||||
r = netaddr.cidr_merge(results)
|
|
||||||
|
|
||||||
with open("region_cn.txt", "w") as fp:
|
|
||||||
for a in r:
|
|
||||||
fp.write("%s,%s,%d\n" % (a.ip,a.netmask, a.prefixlen))
|
|
@ -1,132 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HostRecord struct {
|
|
||||||
/* RR record */
|
|
||||||
rr dns.RR
|
|
||||||
|
|
||||||
/* type, dns.A or dns.AAAA */
|
|
||||||
t uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
type Hosts map[string][]HostRecord
|
|
||||||
|
|
||||||
/*
|
|
||||||
get special type of record form Hosts
|
|
||||||
*/
|
|
||||||
func (h Hosts) Get(n string, t uint16) dns.RR {
|
|
||||||
n1 := dns.Fqdn(n)
|
|
||||||
if hr, ok := h[n1]; ok {
|
|
||||||
for _, v := range hr {
|
|
||||||
if v.t == t {
|
|
||||||
return v.rr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
read and parse the hosts file
|
|
||||||
*/
|
|
||||||
func ReadHosts(fn string) (Hosts, error) {
|
|
||||||
fp, err := os.Open(fn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer fp.Close()
|
|
||||||
|
|
||||||
hts := Hosts{}
|
|
||||||
|
|
||||||
bf := bufio.NewReader(fp)
|
|
||||||
|
|
||||||
for {
|
|
||||||
var t uint16
|
|
||||||
bline, _, err := bf.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
sline := string(bline)
|
|
||||||
sline = strings.TrimSpace(sline)
|
|
||||||
|
|
||||||
/* empty line */
|
|
||||||
if sline == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
/* comment */
|
|
||||||
if sline[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lns := strings.Fields(sline)
|
|
||||||
|
|
||||||
if len(lns) < 2 {
|
|
||||||
return nil, errors.New(fmt.Sprintf("invalid hosts line: %s", sline))
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := net.ParseIP(lns[0])
|
|
||||||
if ip == nil {
|
|
||||||
return nil, errors.New(fmt.Sprintf("invalid ip: %s", lns[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Index(lns[0], ".") != -1 {
|
|
||||||
t = dns.TypeA
|
|
||||||
} else {
|
|
||||||
t = dns.TypeAAAA
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dn := range lns[1:] {
|
|
||||||
|
|
||||||
dd := dns.Fqdn(strings.TrimSpace(dn))
|
|
||||||
|
|
||||||
/* ignore space */
|
|
||||||
if dd == "." {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
s := fmt.Sprintf("%s 36000 IN %s %s", dd,
|
|
||||||
dns.TypeToString[t], lns[0])
|
|
||||||
|
|
||||||
r, err := dns.NewRR(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := hts[dd]; ok {
|
|
||||||
hts[dd] = append(hts[dd], HostRecord{r, t})
|
|
||||||
} else {
|
|
||||||
hts[dd] = []HostRecord{HostRecord{r, t}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
return the path of hosts file
|
|
||||||
*/
|
|
||||||
func GetHost() string {
|
|
||||||
var p string
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
p = filepath.Join(os.Getenv("SYSTEMROOT"),
|
|
||||||
"system32/drivers/etc/hosts")
|
|
||||||
} else {
|
|
||||||
p = "/etc/hosts"
|
|
||||||
}
|
|
||||||
return filepath.Clean(p)
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"testing"
|
|
||||||
//"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestReadHosts(t *testing.T) {
|
|
||||||
a, err := ReadHosts("testdata/hosts.txt")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range a {
|
|
||||||
for _, v1 := range v {
|
|
||||||
t.Logf("%s: %s\n", k, v1.rr.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r1 := a.Get("localhost", dns.TypeA)
|
|
||||||
if dnsa, ok := r1.(*dns.A); ok {
|
|
||||||
if dnsa.A.String() != "127.0.0.1" {
|
|
||||||
t.Errorf("get failed a\n")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("type not a\n")
|
|
||||||
}
|
|
||||||
r2 := a.Get("localhost", dns.TypeAAAA)
|
|
||||||
if dnsaa, ok := r2.(*dns.AAAA); ok {
|
|
||||||
if dnsaa.AAAA.String() != "::1" {
|
|
||||||
t.Errorf("get failed aaaa\n")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("type not aaaa\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetHost(t *testing.T) {
|
|
||||||
t.Logf("host: %s\n", GetHost())
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// parse ip range region file
|
|
||||||
// format
|
|
||||||
// ip, netmask, prefixlen
|
|
||||||
func parse_net(fn string) []*net.IPNet {
|
|
||||||
fp, err := os.Open(fn)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
return []*net.IPNet{}
|
|
||||||
}
|
|
||||||
defer fp.Close()
|
|
||||||
|
|
||||||
nets := []*net.IPNet{}
|
|
||||||
|
|
||||||
reader := bufio.NewReader(fp)
|
|
||||||
for {
|
|
||||||
line, err := reader.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
line = strings.Trim(line, "\r\n")
|
|
||||||
fnds := strings.FieldsFunc(line, func(c rune) bool {
|
|
||||||
if unicode.IsSpace(c) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c == ',' {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(fnds) != 3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, net1, err := net.ParseCIDR(
|
|
||||||
fmt.Sprintf("%s/%s", fnds[0], fnds[2])); err == nil {
|
|
||||||
nets = append(nets, net1)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nets
|
|
||||||
}
|
|
||||||
|
|
||||||
// test ip in ip range region
|
|
||||||
func in_region(ip net.IP, nets []*net.IPNet) bool {
|
|
||||||
for _, n := range nets {
|
|
||||||
if n.Contains(ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// test dns reply A or AAAA in special ip range region
|
|
||||||
func answer_in_region(m *dns.Msg, nets []*net.IPNet) bool {
|
|
||||||
// no region loaded, return true
|
|
||||||
if len(nets) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rr := range m.Answer {
|
|
||||||
if a, ok := rr.(*dns.A); ok {
|
|
||||||
if in_region(a.A, nets) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if aaaa, ok := rr.(*dns.AAAA); ok {
|
|
||||||
if in_region(aaaa.AAAA, nets) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
//"fmt"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
//"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseNet(t *testing.T) {
|
|
||||||
nets := parse_net("region_cn.txt")
|
|
||||||
t.Logf("get %d networks\n", len(nets))
|
|
||||||
t.Logf("1st %s\n", nets[0].String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQuery(t *testing.T) {
|
|
||||||
ip_region = parse_net("region_cn.txt")
|
|
||||||
var c *dns.Client
|
|
||||||
for _, srv := range []string{
|
|
||||||
"tcp:114.114.114.114:53",
|
|
||||||
"udp:8.8.8.8:53",
|
|
||||||
"udp:192.168.41.1:53",
|
|
||||||
"udp:4.2.2.2:53",
|
|
||||||
} {
|
|
||||||
proto, addr, err := parse_addr(srv)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if proto == "tcp" {
|
|
||||||
c = client_tcp
|
|
||||||
} else {
|
|
||||||
c = client_udp
|
|
||||||
}
|
|
||||||
upsrv := &UpstreamServer{
|
|
||||||
Addr: addr,
|
|
||||||
Proto: proto,
|
|
||||||
client: c,
|
|
||||||
}
|
|
||||||
DefaultServer = append(DefaultServer, upsrv)
|
|
||||||
}
|
|
||||||
blacklist_file = "blacklist.txt"
|
|
||||||
a, err := load_domain(blacklist_file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
} else {
|
|
||||||
Blacklist_ips = a
|
|
||||||
}
|
|
||||||
logger = NewLogger("", true)
|
|
||||||
for _, dn := range []string{
|
|
||||||
"www.google.com",
|
|
||||||
"www.sina.com.cn",
|
|
||||||
"www.taobao.com",
|
|
||||||
"www.ifeng.com",
|
|
||||||
"twitter.com",
|
|
||||||
"www.facebook.com",
|
|
||||||
"plus.google.com",
|
|
||||||
"drive.google.com",
|
|
||||||
"dongtaiwang.com",
|
|
||||||
"www.ratafee.nl",
|
|
||||||
"cc.ratafee.nl",
|
|
||||||
"noddcade.xx.ffs.aafde",
|
|
||||||
"ndfddcade.xx.ffs.aafde",
|
|
||||||
"sddf32dsf.comd.ffdasdf.fdsd3eaaaaa",
|
|
||||||
"www.google.com.hk",
|
|
||||||
} {
|
|
||||||
m := new(dns.Msg)
|
|
||||||
m.SetQuestion(dns.Fqdn(dn), dns.TypeA)
|
|
||||||
t1 := time.Now()
|
|
||||||
m1 := query(m)
|
|
||||||
t2 := time.Now()
|
|
||||||
if m1 == nil {
|
|
||||||
t.Errorf("query %s failed", dn)
|
|
||||||
} else {
|
|
||||||
t.Logf("query time: %s\n", t2.Sub(t1))
|
|
||||||
t.Logf("result of %s\n", dn)
|
|
||||||
for _, a1 := range m1.Answer {
|
|
||||||
t.Logf("%s\n", a1)
|
|
||||||
}
|
|
||||||
//.Printf("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
//var LogLevel int
|
|
||||||
|
|
||||||
type LogOut struct {
|
|
||||||
//out *os.File
|
|
||||||
debug bool
|
|
||||||
dbglog *log.Logger
|
|
||||||
errlog *log.Logger
|
|
||||||
infolog *log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogger(logfile string, debug bool) *LogOut {
|
|
||||||
var out *os.File
|
|
||||||
var err error
|
|
||||||
if logfile != "" {
|
|
||||||
out, err = os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
out = os.Stdout
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out = os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
return &LogOut{
|
|
||||||
debug,
|
|
||||||
log.New(out, "DEBUG: ", log.LstdFlags),
|
|
||||||
log.New(out, "ERROR: ", log.LstdFlags),
|
|
||||||
log.New(out, "INFO: ", log.LstdFlags),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogOut) Debug(format string, args ...interface{}) {
|
|
||||||
if l.debug {
|
|
||||||
l.dbglog.Printf(format, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogOut) Error(format string, args ...interface{}) {
|
|
||||||
l.errlog.Printf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogOut) Info(format string, args ...interface{}) {
|
|
||||||
l.infolog.Printf(format, args...)
|
|
||||||
}
|
|
@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/fangdingjun/go-log"
|
||||||
|
"github.com/fangdingjun/go-log/formatters"
|
||||||
|
"github.com/fangdingjun/go-log/writers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var configfile string
|
||||||
|
var logFileCount int
|
||||||
|
var logFileSize int64
|
||||||
|
var loglevel string
|
||||||
|
var logfile string
|
||||||
|
|
||||||
|
flag.StringVar(&logfile, "log_file", "", "log file, default stdout")
|
||||||
|
flag.IntVar(&logFileCount, "log_count", 10, "max count of log to keep")
|
||||||
|
flag.Int64Var(&logFileSize, "log_size", 10, "max log file size MB")
|
||||||
|
flag.StringVar(&loglevel, "log_level", "INFO",
|
||||||
|
"log level, values:\nOFF, FATAL, PANIC, ERROR, WARN, INFO, DEBUG")
|
||||||
|
flag.StringVar(&configfile, "c", "", "config file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if logfile != "" {
|
||||||
|
log.Default.Out = &writers.FixedSizeFileWriter{
|
||||||
|
MaxCount: logFileCount,
|
||||||
|
Name: logfile,
|
||||||
|
MaxSize: logFileSize * 1024 * 1024,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if loglevel != "" {
|
||||||
|
lv, err := log.ParseLevel(loglevel)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
log.Default.Level = lv
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Default.Formatter = &formatters.TextFormatter{
|
||||||
|
TimeFormat: "2006-01-02 15:04:05.000"}
|
||||||
|
|
||||||
|
cfg, err := loadConfig(configfile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if cfg.UpstreamTimeout == 0 {
|
||||||
|
cfg.UpstreamTimeout = 5
|
||||||
|
}
|
||||||
|
initDNSClient(cfg)
|
||||||
|
log.Debugf("%+v", cfg)
|
||||||
|
makeServers(cfg)
|
||||||
|
select {}
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
site_name: gdns
|
|
||||||
docs_dir: docs
|
|
||||||
pages:
|
|
||||||
- Usage: 'index.md'
|
|
||||||
- Command line options: 'command_line.md'
|
|
||||||
- Config example: 'ex_config.md'
|
|
||||||
|
|
||||||
theme: readthedocs
|
|
@ -1,97 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
. "fmt"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func load_domain(f string) (Kv, error) {
|
|
||||||
var m1 Kv
|
|
||||||
c, err := ioutil.ReadFile(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(c, &m1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return m1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse_addr(s string) (string, string, error) {
|
|
||||||
s2 := strings.SplitN(s, ":", 2)
|
|
||||||
|
|
||||||
if len(s2) != 2 {
|
|
||||||
msg := Sprintf("error %s not well formatted", s)
|
|
||||||
err := errors.New(msg)
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s2[0] != "tcp" && s2[0] != "udp" {
|
|
||||||
msg := Sprintf("invalid %s, only tcp or udp allowed", s2[0])
|
|
||||||
err := errors.New(msg)
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
host, port, err := net.SplitHostPort(s2[1])
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check host */
|
|
||||||
ip := net.ParseIP(host)
|
|
||||||
if ip == nil {
|
|
||||||
return "", "", errors.New(Sprintf("invalid host %s", host))
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check port */
|
|
||||||
_, err = strconv.Atoi(port)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s2[0], s2[1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse_server(s string) (*UpstreamServer, error) {
|
|
||||||
s1 := strings.Split(s, ",")
|
|
||||||
|
|
||||||
if len(s1) != 2 {
|
|
||||||
msg := Sprintf("error %s not well formatted", s)
|
|
||||||
err := errors.New(msg)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
proto, addr, err := parse_addr(s1[1])
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var c *dns.Client
|
|
||||||
if proto == "tcp" {
|
|
||||||
c = client_tcp
|
|
||||||
} else {
|
|
||||||
c = client_udp
|
|
||||||
}
|
|
||||||
|
|
||||||
d, err := load_domain(s1[0])
|
|
||||||
if err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var sv *UpstreamServer = &UpstreamServer{
|
|
||||||
Addr: addr,
|
|
||||||
domains: d,
|
|
||||||
client: c,
|
|
||||||
Proto: proto,
|
|
||||||
}
|
|
||||||
|
|
||||||
return sv, nil
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseAddr(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
var s1, s2 string
|
|
||||||
s1, s2, err = parse_addr("udp:123.2.3.4:321")
|
|
||||||
t.Logf("parse result %s, %s\n", s1, s2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
s1, s2, err = parse_addr("tcp:123.2.3.4:321")
|
|
||||||
t.Logf("parse result %s, %s\n", s1, s2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = parse_addr("1.2.3.4:333")
|
|
||||||
t.Log(err)
|
|
||||||
if err == nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = parse_addr("cc:1.2.3.4:33")
|
|
||||||
t.Log(err)
|
|
||||||
if err == nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, err = parse_addr("tcp:1.2.3.4:33:33")
|
|
||||||
t.Log(err)
|
|
||||||
if err == nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseServer(t *testing.T) {
|
|
||||||
_, err := parse_server("aa.txt:tcp:1.2.3.4:32")
|
|
||||||
t.Log(err)
|
|
||||||
if err == nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
sv, err := parse_server("noexists.txt,tcp:1.2.3.4:32")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if sv.Addr != "1.2.3.4:32" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if sv.Proto != "tcp" {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
if sv.domains != nil {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,231 +1,160 @@
|
|||||||
/*
|
|
||||||
gdns is a dns proxy server write by go.
|
|
||||||
|
|
||||||
gdns much like dnsmasq or chinadns, but it can run on windows.
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
support different domains use different upstream dns servers
|
|
||||||
support contact to the upstream dns server by tcp or udp
|
|
||||||
support blacklist list to block the fake ip
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
generate a config file and edit it
|
|
||||||
$ gdns -dumpflags > dns.ini
|
|
||||||
|
|
||||||
|
|
||||||
run it
|
|
||||||
$ sudo gdns -config dns.ini
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"crypto/tls"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
"net"
|
||||||
"github.com/miekg/dns"
|
"net/url"
|
||||||
"log"
|
"strconv"
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var client_udp *dns.Client = &dns.Client{}
|
|
||||||
|
|
||||||
var client_tcp *dns.Client = &dns.Client{Net: "tcp"}
|
|
||||||
|
|
||||||
var Servers []*UpstreamServer = nil
|
|
||||||
|
|
||||||
var logger *LogOut = nil
|
|
||||||
|
|
||||||
var Blacklist_ips Kv = nil
|
"github.com/fangdingjun/go-log"
|
||||||
|
)
|
||||||
|
|
||||||
var debug bool = false
|
type server struct {
|
||||||
|
addr *url.URL
|
||||||
|
cert string
|
||||||
|
key string
|
||||||
|
upstreams []*url.URL
|
||||||
|
bootstrap []*url.URL
|
||||||
|
}
|
||||||
|
|
||||||
var dns_cache *lru.Cache
|
func (srv *server) serve() {
|
||||||
|
switch srv.addr.Scheme {
|
||||||
|
case "udp":
|
||||||
|
srv.serveUDP()
|
||||||
|
case "tcp":
|
||||||
|
srv.serveTCP()
|
||||||
|
case "tls":
|
||||||
|
srv.serveTLS()
|
||||||
|
case "https":
|
||||||
|
srv.serveHTTPS()
|
||||||
|
default:
|
||||||
|
log.Fatalf("unsupported type %s", srv.addr.Scheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var hostfile string = ""
|
func (srv *server) serveUDP() {
|
||||||
var record_hosts Hosts = nil
|
ip, port, _ := net.SplitHostPort(srv.addr.Host)
|
||||||
|
_ip := net.ParseIP(ip)
|
||||||
|
_port, _ := strconv.Atoi(port)
|
||||||
|
|
||||||
func in_blacklist(m *dns.Msg) bool {
|
udpconn, err := net.ListenUDP("udp", &net.UDPAddr{IP: _ip, Port: _port})
|
||||||
if Blacklist_ips == nil {
|
if err != nil {
|
||||||
return false
|
log.Fatalf("listen udp error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if m == nil {
|
defer udpconn.Close()
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rr := range m.Answer {
|
buf := make([]byte, 4096)
|
||||||
/* A */
|
for {
|
||||||
if t, ok := rr.(*dns.A); ok {
|
n, addr, err := udpconn.ReadFrom(buf)
|
||||||
ip := t.A.String()
|
if err != nil {
|
||||||
if _, ok1 := Blacklist_ips[ip]; ok1 {
|
log.Debugln(err)
|
||||||
logger.Debug("%s is in blacklist\n", ip)
|
break
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
buf1 := make([]byte, n)
|
||||||
|
copy(buf1, buf[:n])
|
||||||
|
go srv.handleUDP(buf1, addr, udpconn)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* AAAA */
|
func (srv *server) serveTCP() {
|
||||||
if t, ok := rr.(*dns.AAAA); ok {
|
l, err := net.Listen("tcp", srv.addr.Host)
|
||||||
ip := t.AAAA.String()
|
if err != nil {
|
||||||
if _, ok1 := Blacklist_ips[ip]; ok1 {
|
log.Fatalln("listen tcp", err)
|
||||||
logger.Debug("%s is in blacklist\n", ip)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
defer l.Close()
|
||||||
|
log.Debugf("listen tcp://%s", l.Addr().String())
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugln(err)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
go srv.handleTCP(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRoot(w dns.ResponseWriter, r *dns.Msg) {
|
func (srv *server) serveTLS() {
|
||||||
var err error
|
cert, err := tls.LoadX509KeyPair(srv.cert, srv.key)
|
||||||
var res *dns.Msg
|
if err != nil {
|
||||||
domain := r.Question[0].Name
|
log.Fatalln("load certificate failed", err)
|
||||||
|
|
||||||
/*
|
|
||||||
reply from hosts
|
|
||||||
*/
|
|
||||||
if record_hosts != nil {
|
|
||||||
rr := record_hosts.Get(domain, r.Question[0].Qtype)
|
|
||||||
if rr != nil {
|
|
||||||
msg := new(dns.Msg)
|
|
||||||
msg.SetReply(r)
|
|
||||||
msg.Answer = append(msg.Answer, rr)
|
|
||||||
w.WriteMsg(msg)
|
|
||||||
logger.Debug("%s query %s %s %s, reply from hosts\n",
|
|
||||||
w.RemoteAddr(),
|
|
||||||
domain,
|
|
||||||
dns.ClassToString[r.Question[0].Qclass],
|
|
||||||
dns.TypeToString[r.Question[0].Qtype],
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
key := fmt.Sprintf("%s_%s", domain, dns.TypeToString[r.Question[0].Qtype])
|
|
||||||
|
|
||||||
if enable_cache {
|
|
||||||
// reply from cache
|
|
||||||
if a, ok := dns_cache.Get(key); ok {
|
|
||||||
msg := new(dns.Msg)
|
|
||||||
msg.SetReply(r)
|
|
||||||
|
|
||||||
aa := strings.Split(a.(string), "|")
|
|
||||||
for _, a1 := range aa {
|
|
||||||
rr, _ := dns.NewRR(a1)
|
|
||||||
if rr != nil {
|
|
||||||
msg.Answer = append(msg.Answer, rr)
|
|
||||||
}
|
}
|
||||||
|
l, err := tls.Listen("tcp", srv.addr.Host,
|
||||||
|
&tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
//NextProtos: []string{"h2"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("listen tls", err)
|
||||||
}
|
}
|
||||||
|
defer l.Close()
|
||||||
w.WriteMsg(msg)
|
log.Debugf("listen tls://%s", l.Addr().String())
|
||||||
logger.Debug("%s query %s %s %s, reply from cache\n",
|
for {
|
||||||
w.RemoteAddr(),
|
conn, err := l.Accept()
|
||||||
domain,
|
if err != nil {
|
||||||
dns.ClassToString[r.Question[0].Qclass],
|
log.Debugln("tls accept", err)
|
||||||
dns.TypeToString[r.Question[0].Qtype],
|
break
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
go srv.handleTCP(conn)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// forward to upstream server
|
func (srv *server) serveHTTPS() {
|
||||||
for i := 0; i < 2; i++ {
|
cert, err := tls.LoadX509KeyPair(srv.cert, srv.key)
|
||||||
for _, sv := range Servers {
|
|
||||||
if sv.match(domain) {
|
|
||||||
|
|
||||||
res, err = sv.query(r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("%s", err)
|
log.Fatalln("load certificate failed", err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debug("%s query %s %s %s, forward to %s:%s, %s\n",
|
|
||||||
w.RemoteAddr(),
|
|
||||||
domain,
|
|
||||||
dns.ClassToString[r.Question[0].Qclass],
|
|
||||||
dns.TypeToString[r.Question[0].Qtype],
|
|
||||||
sv.Proto, sv.Addr,
|
|
||||||
dns.RcodeToString[res.Rcode],
|
|
||||||
)
|
|
||||||
|
|
||||||
if res.Rcode == dns.RcodeSuccess &&
|
|
||||||
!in_blacklist(res) && len(res.Answer) > 0 {
|
|
||||||
if enable_cache {
|
|
||||||
// add to cache
|
|
||||||
v := []string{}
|
|
||||||
for _, as := range res.Answer {
|
|
||||||
v = append(v, as.String())
|
|
||||||
}
|
|
||||||
dns_cache.Add(key, strings.Join(v, "|"))
|
|
||||||
}
|
|
||||||
w.WriteMsg(res)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
l, err := tls.Listen("tcp", srv.addr.Host,
|
||||||
|
&tls.Config{
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("listen https", err)
|
||||||
}
|
}
|
||||||
|
defer l.Close()
|
||||||
// fallback to default upstream server
|
log.Debugf("listen https://%s", l.Addr().String())
|
||||||
for i := 0; i < 2; i++ {
|
for {
|
||||||
logger.Debug("%s query %s %s %s, use default server\n",
|
conn, err := l.Accept()
|
||||||
w.RemoteAddr(),
|
if err != nil {
|
||||||
domain,
|
log.Debugln("https accept", err)
|
||||||
dns.ClassToString[r.Question[0].Qclass],
|
break
|
||||||
dns.TypeToString[r.Question[0].Qtype],
|
|
||||||
)
|
|
||||||
res := query(r)
|
|
||||||
if res != nil {
|
|
||||||
//logger.Debug("get: %s", res)
|
|
||||||
if enable_cache && res.Rcode == dns.RcodeSuccess &&
|
|
||||||
len(res.Answer) > 0 {
|
|
||||||
// add to cache
|
|
||||||
v := []string{}
|
|
||||||
for _, as := range res.Answer {
|
|
||||||
v = append(v, as.String())
|
|
||||||
}
|
|
||||||
dns_cache.Add(key, strings.Join(v, "|"))
|
|
||||||
}
|
|
||||||
w.WriteMsg(res)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
go srv.handleHTTPSConn(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
dns.HandleFailed(w, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func makeServers(c *conf) {
|
||||||
parse_flags()
|
upstreams := []*url.URL{}
|
||||||
|
bootstraps := []*url.URL{}
|
||||||
var err error
|
for _, a := range c.UpstreamServers {
|
||||||
if enable_cache {
|
u, err := url.Parse(a)
|
||||||
// create cache
|
|
||||||
dns_cache, err = lru.New(1000)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
upstreams = append(upstreams, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
dns.HandleFunc(".", handleRoot)
|
for _, a := range c.BootstrapServers {
|
||||||
|
u, err := url.Parse(a)
|
||||||
logger = NewLogger(logfile, debug)
|
|
||||||
|
|
||||||
logger.Info("Listen on %s\n", bind_addr)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
/* listen tcp */
|
|
||||||
err := dns.ListenAndServe(bind_addr, "tcp", nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}()
|
bootstraps = append(bootstraps, u)
|
||||||
|
}
|
||||||
|
|
||||||
/* listen udp */
|
for _, l := range c.Listen {
|
||||||
err = dns.ListenAndServe(bind_addr, "udp", nil)
|
u, err := url.Parse(l.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
srv := &server{
|
||||||
|
addr: u,
|
||||||
|
cert: l.Cert,
|
||||||
|
key: l.Key,
|
||||||
|
upstreams: upstreams,
|
||||||
|
bootstrap: bootstraps,
|
||||||
|
}
|
||||||
|
go srv.serve()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/fangdingjun/go-log"
|
||||||
|
"github.com/fangdingjun/nghttp2-go"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (srv *server) handleHTTPSConn(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
tlsconn := c.(*tls.Conn)
|
||||||
|
if err := tlsconn.Handshake(); err != nil {
|
||||||
|
log.Errorln("handshake", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state := tlsconn.ConnectionState()
|
||||||
|
if state.NegotiatedProtocol != "h2" {
|
||||||
|
log.Errorln("http2 is needed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h2conn, err := nghttp2.Server(tlsconn, srv)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("create http2 error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h2conn.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) handleHTTP2Req(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctype := r.Header.Get("content-type")
|
||||||
|
if ctype != "application/dns-message" {
|
||||||
|
http.Error(w, "dns message is required", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("read request body", err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := new(dns.Msg)
|
||||||
|
if err := msg.Unpack(data); err != nil {
|
||||||
|
log.Errorln("parse dns message", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reply := false
|
||||||
|
for _, up := range srv.upstreams {
|
||||||
|
m, err := queryUpstream(msg, up)
|
||||||
|
if err == nil {
|
||||||
|
w.Header().Set("content-type", "application/dns-message")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
d, _ := m.Pack()
|
||||||
|
w.Write(d)
|
||||||
|
reply = true
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
log.Errorf("https query upstream %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !reply {
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.RequestURI != srv.addr.Path {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srv.handleHTTP2Req(w, r)
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/fangdingjun/go-log"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (srv *server) handleTCP(c net.Conn) {
|
||||||
|
defer c.Close()
|
||||||
|
log.Debugln("tcp from", c.RemoteAddr())
|
||||||
|
conn := dns.Conn{Conn: c}
|
||||||
|
for {
|
||||||
|
msg, err := conn.ReadMsg()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugln("tcp read message", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
reply := false
|
||||||
|
for _, up := range srv.upstreams {
|
||||||
|
m, err := queryUpstream(msg, up)
|
||||||
|
if err == nil {
|
||||||
|
log.Debugln("got reply", m.String())
|
||||||
|
conn.WriteMsg(m)
|
||||||
|
reply = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Debugln("query upstream", up.String(), err)
|
||||||
|
}
|
||||||
|
if !reply {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInblacklist(t *testing.T) {
|
|
||||||
logger = NewLogger("", false)
|
|
||||||
Blacklist_ips = Kv{"1.2.3.4": 1, "2.3.4.5": 1}
|
|
||||||
test_ips := map[string]bool{
|
|
||||||
"1.2.3.4": true,
|
|
||||||
"2.3.4.5": true,
|
|
||||||
"2.3.4.1": false,
|
|
||||||
"1.2.4.3": false,
|
|
||||||
}
|
|
||||||
|
|
||||||
for ip, r := range test_ips {
|
|
||||||
msg := new(dns.Msg)
|
|
||||||
s := fmt.Sprintf("example.com. IN A %s", ip)
|
|
||||||
rr, err := dns.NewRR(s)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
msg.Answer = append(msg.Answer, rr)
|
|
||||||
if in_blacklist(msg) != r {
|
|
||||||
t.Errorf("%s must match in %v result\n", ip, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/fangdingjun/go-log"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (srv *server) handleUDP(buf []byte, addr net.Addr, conn *net.UDPConn) {
|
||||||
|
msg := new(dns.Msg)
|
||||||
|
if err := msg.Unpack(buf); err != nil {
|
||||||
|
log.Debugln("udp parse msg", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, up := range srv.upstreams {
|
||||||
|
m, err := queryUpstream(msg, up)
|
||||||
|
if err == nil {
|
||||||
|
d, _ := m.Pack()
|
||||||
|
conn.WriteTo(d, addr)
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
log.Debugln("udp query upstream err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
127.0.0.1 localhost localhost.localdomain
|
|
||||||
192.168.1.1 gw
|
|
||||||
::1 localhost
|
|
||||||
|
|
||||||
|
|
||||||
192.243.112.217 www.ratafee.nl
|
|
||||||
2607:8700:103:9b22:: www.ratafee.nl
|
|
||||||
# 127.0.0.1 ccc.com
|
|
@ -1,36 +1,128 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fangdingjun/go-log"
|
||||||
|
nghttp2 "github.com/fangdingjun/nghttp2-go"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Kv map[string]int
|
var dnsClientTCP *dns.Client
|
||||||
|
var dnsClientHTTPS *dns.Client
|
||||||
|
var dnsClientUDP *dns.Client
|
||||||
|
var dnsClientTLS *dns.Client
|
||||||
|
|
||||||
type UpstreamServer struct {
|
func queryUpstream(msg *dns.Msg, upstream *url.URL) (*dns.Msg, error) {
|
||||||
domains Kv
|
switch upstream.Scheme {
|
||||||
Proto string
|
case "tcp":
|
||||||
Addr string
|
return queryUpstreamTCP(msg, upstream)
|
||||||
client *dns.Client
|
case "https":
|
||||||
|
return queryUpstreamHTTPS(msg, upstream)
|
||||||
|
case "udp":
|
||||||
|
return queryUpstreamUDP(msg, upstream)
|
||||||
|
case "tls":
|
||||||
|
return queryUpstreamTLS(msg, upstream)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return nil, errors.New("unknown upstream type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UpstreamServer) match(d string) bool {
|
func queryUpstreamUDP(msg *dns.Msg, upstream *url.URL) (*dns.Msg, error) {
|
||||||
if srv.domains == nil {
|
log.Debugln("query upstream", upstream.String())
|
||||||
return false
|
m, _, err := dnsClientUDP.Exchange(msg, upstream.Host)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("query udp error %s", err)
|
||||||
}
|
}
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
s := strings.Split(strings.Trim(d, "."), ".")
|
func queryUpstreamTCP(msg *dns.Msg, upstream *url.URL) (*dns.Msg, error) {
|
||||||
for i := 0; i < len(s); i++ {
|
log.Debugln("query upstream", upstream.String())
|
||||||
s1 := strings.Join(s[i:], ".")
|
m, _, err := dnsClientTCP.Exchange(msg, upstream.Host)
|
||||||
if _, ok := srv.domains[s1]; ok {
|
if err != nil {
|
||||||
return true
|
log.Debugf("query tcp error %s", err)
|
||||||
}
|
}
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryUpstreamTLS(msg *dns.Msg, upstream *url.URL) (*dns.Msg, error) {
|
||||||
|
log.Debugln("query upstream", upstream.String())
|
||||||
|
m, _, err := dnsClientTLS.Exchange(msg, upstream.Host)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("query tls error %s", err)
|
||||||
}
|
}
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
func queryUpstreamHTTPS(msg *dns.Msg, upstream *url.URL) (*dns.Msg, error) {
|
||||||
|
log.Debugln("query upstream", upstream.String())
|
||||||
|
m, _, err := dnsClientHTTPS.Exchange(msg, upstream.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("query https error %s", err)
|
||||||
|
}
|
||||||
|
return m, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UpstreamServer) query(req *dns.Msg) (*dns.Msg, error) {
|
func initDNSClient(c *conf) {
|
||||||
res, _, err := srv.client.Exchange(req, srv.Addr)
|
var resolver = new(net.Resolver)
|
||||||
return res, err
|
if len(c.BootstrapServers) > 0 {
|
||||||
|
log.Debugf("init dns client, bootstrap servers %v", c.BootstrapServers)
|
||||||
|
resolver = &net.Resolver{
|
||||||
|
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
for _, a := range c.BootstrapServers {
|
||||||
|
u, _ := url.Parse(a)
|
||||||
|
conn, err := net.Dial(u.Scheme, u.Host)
|
||||||
|
if err == nil {
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("dial failed")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
Resolver: resolver,
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsClientTLS = &dns.Client{
|
||||||
|
Net: "tcp-tls",
|
||||||
|
Timeout: time.Duration(c.UpstreamTimeout) * time.Second,
|
||||||
|
Dialer: dialer,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: c.UpstreamInsecure,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dnsClientUDP = &dns.Client{
|
||||||
|
Net: "udp",
|
||||||
|
Timeout: time.Duration(c.UpstreamTimeout) * time.Second,
|
||||||
|
}
|
||||||
|
dnsClientTCP = &dns.Client{
|
||||||
|
Net: "tcp",
|
||||||
|
Timeout: time.Duration(c.UpstreamTimeout) * time.Second,
|
||||||
|
}
|
||||||
|
dnsClientHTTPS = &dns.Client{
|
||||||
|
Net: "https",
|
||||||
|
Timeout: time.Duration(c.UpstreamTimeout) * time.Second,
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Transport: &nghttp2.Transport{
|
||||||
|
DialTLS: func(network, addr string, cfg *tls.Config) (*tls.Conn, error) {
|
||||||
|
log.Debugln("dial to", network, addr)
|
||||||
|
conn, err := tls.DialWithDialer(dialer, network, addr, cfg)
|
||||||
|
return conn, err
|
||||||
|
},
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: c.UpstreamInsecure,
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestServerMathNil(t *testing.T) {
|
|
||||||
srv := UpstreamServer{} // initial with nil
|
|
||||||
|
|
||||||
domains := []string{"twitter.com", "google.com", "abc.com"}
|
|
||||||
|
|
||||||
for _, d := range domains {
|
|
||||||
if srv.match(d) {
|
|
||||||
t.Errorf("%s must match in false result\n", d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServerMatch(t *testing.T) {
|
|
||||||
d := Kv{"twitter.com": 1, "google.com": 1, "cn": 1}
|
|
||||||
srv := UpstreamServer{domains: d}
|
|
||||||
|
|
||||||
test_domains := map[string]bool{
|
|
||||||
"twitter.com": true,
|
|
||||||
"pbs.twitter.com": true,
|
|
||||||
"abc.pbs.twitter.com": true,
|
|
||||||
"efg.abc.pbs.twitter.com": true,
|
|
||||||
"google.com": true,
|
|
||||||
"plus.google.com": true,
|
|
||||||
"cc.plus.google.com": true,
|
|
||||||
"dd.cc.plus.google.com": true,
|
|
||||||
"twitter.abc.com": false,
|
|
||||||
"twitter.com.aa.com": false,
|
|
||||||
"google.com.cccc.com": false,
|
|
||||||
"google.com.aeddasdfc3.com": false,
|
|
||||||
"ip.cn": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
for d, r := range test_domains {
|
|
||||||
if srv.match(d) != r {
|
|
||||||
t.Errorf("%s must match in %v result\n", d, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue