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) } m1.Truncated = r.TC m1.RecursionDesired = r.RD m1.RecursionAvailable = r.RA m1.AuthenticatedData = r.AD m1.CheckingDisabled = r.CD 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: 5 * 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 }