add google https dns support

dns
Dingjun 8 years ago
parent 446fc256e6
commit 1f88a61e05

3
.gitignore vendored

@ -2,5 +2,4 @@
*.swp
*.json
*.txt
gdns
gdns_arm
gdns*

@ -9,3 +9,4 @@ a dns forward proxy write by go
- support configure different domains to forward to different upstream servers
- support tcp or udp to communicate to upstream servers
- support ip blacklist to filter unwanted dns reply
- support google https dns

@ -13,7 +13,7 @@
},
{
"domain_list_file":"domain2.txt",
"servers":["tcp:8.8.4.4:53"]
"servers":["tcp:8.8.4.4:53","https:74.125.200.100"]
}
]
}

@ -0,0 +1,181 @@
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"
// HTTPDns struct
type HTTPDns struct {
myip string
l sync.Mutex
}
func (h *HTTPDns) getMyIP() string {
if h.myip != "" {
return h.myip
}
go h.queryMyIP()
return ""
}
type ipAPI struct {
Ip string `json:"origin"`
}
func (h *HTTPDns) 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 *HTTPDns) 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 *HTTPDns) 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 := query(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)
}
return m1, 0, nil
}
// DefaultHTTPDnsClient default http dns client
var DefaultHTTPDnsClient = &HTTPDns{}
// 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: 3 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{ServerName: "dns.google.com"},
TLSHandshakeTimeout: 3 * time.Second,
},
}
func query(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,80 @@
package main
import (
"encoding/json"
"fmt"
"github.com/miekg/dns"
"io/ioutil"
"net"
"net/http"
"testing"
"time"
)
// MyIP my ip
type MyIP struct {
IP string `json:"origin"`
}
func myip() string {
res, err := http.Get("https://www.simicloud.com/media/httpbin/ip")
if err != nil {
return ""
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return ""
}
ip := MyIP{}
err = json.Unmarshal(data, &ip)
if err != nil {
return ""
}
return ip.IP
}
func TestQuery(t *testing.T) {
m := net.IPv4Mask(255, 255, 255, 0)
ip1 := net.ParseIP(myip())
ipnet := net.IPNet{ip1.Mask(m), m}
r, err := query("www.taobao.com", "a", ipnet.String(), "", "")
if err != nil {
t.Error(err)
}
fmt.Printf("%+v\n", r)
r, err = query("www.taobao.com", "a", "", "", "")
if err != nil {
t.Error(err)
}
fmt.Printf("%+v\n", r)
}
func TestHttpsQuery(t *testing.T) {
m := new(dns.Msg)
m.SetQuestion("www.taobao.com", dns.TypeA)
m2, _, err := DefaultHTTPDnsClient.Exchange(m, ServerAddr)
if err != nil {
t.Error(err)
}
fmt.Println(m2)
time.Sleep(1 * time.Second)
m2, _, err = DefaultHTTPDnsClient.Exchange(m, ServerAddr)
if err != nil {
t.Error(err)
}
fmt.Println(m2)
}
func TestGetMyIP(t *testing.T) {
a := DefaultHTTPDnsClient.getMyIP()
time.Sleep(4 * time.Second)
a = DefaultHTTPDnsClient.getMyIP()
if a == "" {
t.Errorf("get ip failed")
}
}

@ -38,8 +38,12 @@ func (r *routers) checkBlacklist(m *dns.Msg) bool {
return false
}
type dnsClient interface {
Exchange(*dns.Msg, string) (*dns.Msg, time.Duration, error)
}
func (r *routers) query(m *dns.Msg, servers []addr) (*dns.Msg, error) {
var up *dns.Client
var up dnsClient
var lastErr error
// query cache
@ -56,6 +60,8 @@ func (r *routers) query(m *dns.Msg, servers []addr) (*dns.Msg, error) {
up = r.tcp
case "udp":
up = r.udp
case "https":
up = DefaultHTTPDnsClient
default:
up = r.udp
}

Loading…
Cancel
Save