Created
May 28, 2018 17:18
-
-
Save Regentag/046ab75a5d889a20673a91c1e060eff5 to your computer and use it in GitHub Desktop.
DNS Over HTTPS server in Golang.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DNS Over HTTPS server in Golang. | |
// 2018.5.29. | |
package main | |
import ( | |
"bytes" | |
"crypto/tls" | |
"flag" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"os" | |
"os/signal" | |
"strconv" | |
"syscall" | |
"github.com/miekg/dns" | |
) | |
/// Declare error clase | |
type htError struct { | |
msg string | |
} | |
func (e *htError) Error() string { | |
return e.msg | |
} | |
func NewErr(msg string) error { | |
return &htError{msg} | |
} | |
/// | |
const CLOUDFLARE_DNS = "1.1.1.1:53" | |
const CLOUDFLARE_DOH_HOST = "cloudflare-dns.com." | |
const CLOUDFLARE_DOH_URL = "https://cloudflare-dns.com/dns-query" | |
// Create HTTPS request and POST. | |
func makeHttpsRequest(wire []byte) (respWire []byte, err error) { | |
// disable security check for client | |
tr := &http.Transport{ | |
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, | |
} | |
client := &http.Client{Transport: tr} | |
buff := bytes.NewBuffer(wire) | |
resp, err := client.Post(CLOUDFLARE_DOH_URL, "application/dns-udpwireformat", buff) | |
if err == nil { | |
defer resp.Body.Close() | |
log.Printf("Response Status: %s\n", resp.Status) | |
if resp.StatusCode != 200 { | |
return nil, NewErr(resp.Status) | |
} | |
respBody, err := ioutil.ReadAll(resp.Body) | |
if err == nil { | |
return respBody, nil | |
} else { | |
// io: read error | |
return nil, err | |
} | |
} else { | |
// http error | |
return nil, err | |
} | |
} | |
type SecHandler struct { | |
ServiceType string | |
Host *dns.Msg | |
} | |
func (s SecHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { | |
// Check for "cloudflare-dns.com" host. | |
if len(r.Question) > 0 { | |
if r.Question[0].Name == CLOUDFLARE_DOH_HOST && | |
r.Question[0].Qtype == dns.TypeA { | |
s.Host.SetReply(r) | |
w.WriteMsg(s.Host) | |
// End func. | |
return | |
} | |
} | |
log.Printf("Request(%s)\n%s\n", s.ServiceType, r.String()) | |
wire, err := r.Pack() | |
if err == nil { | |
resp, err := makeHttpsRequest(wire) | |
if err == nil { | |
// Good response then | |
m := new(dns.Msg) | |
err := m.Unpack(resp) | |
if err == nil { | |
m.SetReply(r) | |
w.WriteMsg(m) | |
log.Printf("Response(%s)\n%s\n", s.ServiceType, m.String()) | |
} else { | |
log.Printf("Can't unpack message from wireformat: %s\n", err.Error()) | |
dns.HandleFailed(w, r) | |
} | |
} else { | |
log.Printf("HTTPS Request failed: %s\n", err.Error()) | |
dns.HandleFailed(w, r) | |
} | |
} else { | |
log.Printf("Can't pack message to wire format: %s\n", err.Error()) | |
dns.HandleFailed(w, r) | |
} | |
} | |
func getDohHostAddr() (*dns.Msg, error) { | |
m := new(dns.Msg) | |
m.SetQuestion(CLOUDFLARE_DOH_HOST, dns.TypeA) | |
client := new(dns.Client) | |
r, _, err := client.Exchange(m, CLOUDFLARE_DNS) | |
return r, err | |
} | |
func main() { | |
port := flag.Int("port", 53, "port to run on") | |
flag.Parse() | |
// get host | |
h, e := getDohHostAddr() | |
if e != nil { | |
log.Fatalf("Initialize error: %s", e) | |
} | |
go func() { | |
handler := SecHandler{"UDP", h} | |
srv := &dns.Server{Addr: ":" + strconv.Itoa(*port), Net: "udp"} | |
srv.Handler = handler | |
if err := srv.ListenAndServe(); err != nil { | |
log.Fatalf("Failed to set udp listener %s\n", err.Error()) | |
} | |
}() | |
go func() { | |
handler := SecHandler{"TCP", h} | |
srv := &dns.Server{Addr: ":" + strconv.Itoa(*port), Net: "tcp"} | |
srv.Handler = handler | |
if err := srv.ListenAndServe(); err != nil { | |
log.Fatalf("Failed to set tcp listener %s\n", err.Error()) | |
} | |
}() | |
log.Printf("DNS server on port %v\n", *port) | |
sig := make(chan os.Signal) | |
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) | |
s := <-sig | |
log.Printf("Signal (%v) received, stopping\n", s) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment