From 91bb369d383ecd9ccc6d04ff4cfd12414c4d65dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Foremski?= Date: Tue, 15 Nov 2016 19:34:42 -0800 Subject: [PATCH] HTTP/2 support thanks to @voidd (#19, #7) --- README.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++------ dingo.go | 13 ++++---- gdns.go | 4 ++- https.go | 27 +++++++++++----- 4 files changed, 115 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b9d8359..059ff6e 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,94 @@ # dingo -A caching DNS proxy for the [Google DNS-over-HTTPS](https://developers.google.com/speed/public-dns/docs/dns-over-https). -It effectively encrypts all your DNS traffic. +A DNS stub resolver in Go for the [Google +DNS-over-HTTPS](https://developers.google.com/speed/public-dns/docs/dns-over-https). +It effectively encrypts all your DNS traffic. It also supports +[OpenResolve](https://www.openresolve.com/) by OpenDNS. -For now, it resolves DNS queries over HTTPS/1.1, in a few independent threads (configurable). -Future plans include HTTP/2.0 and QUIC support, better caching, and support for other resolvers. +The ultimate goal for the project is to provide a secure, caching DNS client that +communicates with recursive DNS resolvers over encrypted channels only. For now, +it resolves DNS queries over HTTP/2 in independent threads. The plans for +future include better caching and support for QUIC. -You can start it as root using: +## Quick start + +Download a pre-built binary for your platform from [the latest +release](https://github.com/pforemski/dingo/releases/latest) (or build your own binaries). + +Run dingo as root on port 53. For example, on Linux: +``` +$ sudo ./dingo-linux-amd64 -port=53 +``` + +Update your DNS configuration. On Linux, edit your `/etc/resolv.conf` as root (remember to +make backup first), e.g.: +``` +$ sudo sh -c "echo nameserver 127.0.0.1 > /etc/resolv.conf" +``` + +## Tuning dingo + +You will probably want to change the default Google DNS-over-HTTPS server IP address, using the +`-gdns:server` option. First, resolve `dns.google.com` to IP address, which should give you the +server closest to you: +``` +$ host dns.google.com +dns.google.com has address 216.58.209.174 +dns.google.com has IPv6 address 2a00:1450:401b:800::200e +``` + +Next, pass it to dingo. If you prefer IPv6, enclose the address in brackets, e.g.: +``` +$ sudo ./dingo-linux-amd64 -port=53 -gdns:server=[2a00:1450:401b:800::200e] +``` + +To see all options, run `dingo -h`: +``` +Usage of dingo-linux-amd64: + -bind string + IP address to bind to (default "127.0.0.1") + -dbg int + debugging level (default 2) + -gdns:auto + Google DNS: try to lookup the closest IPv4 server + -gdns:edns string + Google DNS: EDNS client subnet (set 0.0.0.0/0 to disable) + -gdns:host string + Google DNS: HTTP 'Host' header (real FQDN, encrypted in TLS) (default "dns.google.com") + -gdns:nopad + Google DNS: disable random padding + -gdns:server string + Google DNS: server address (default "216.58.195.78") + -gdns:sni string + Google DNS: SNI string to send (should match server certificate) (default "www.google.com") + -gdns:workers int + Google DNS: number of independent workers (default 10) + -h1 + use HTTP/1.1 transport + -odns:host string + OpenDNS: HTTP 'Host' header (real FQDN, encrypted in TLS) (default "api.openresolve.com") + -odns:server string + OpenDNS: web server address (default "67.215.70.81") + -odns:sni string + OpenDNS: TLS SNI string to send (unencrypted, must validate as server cert) (default "www.openresolve.com") + -odns:workers int + OpenDNS: number of independent workers + -port int + listen on port number (default 32000) + +``` + +Finally, you will need to make dingo start in background each time you boot your machine. In Linux, +you might want to use the [GNU Screen](https://en.wikipedia.org/wiki/GNU_Screen), which can start +processes in background. For example, you might want to add the following line to your +`/etc/rc.local`: ``` -root@localhost:~# go run ./dingo.go ./gdns.go -port=53 +screen -dmS dingo /path/to/bin/dingo -port=53 -gdns:server=[2a00:1450:401b:800::200e] ``` -Remember to prepare your Go environment and download all dependencies first. +## Author -Alternatively, use pre-built binaries in the `./release` sub-directory. +Pawel Foremski, [pjf@foremski.pl](mailto:pjf@foremski.pl) -Note that you will need to update your `/etc/resolv.conf` file to use `dingo` as your system-wide resolver. +Find me on: [LinkedIn](https://www.linkedin.com/in/pforemski), +[Twitter](https://twitter.com/pforemski) diff --git a/dingo.go b/dingo.go index 931cb97..5a37f92 100644 --- a/dingo.go +++ b/dingo.go @@ -23,15 +23,16 @@ import "math/rand" /* command-line arguments */ var ( - bindip = flag.String("bind", "127.0.0.1", "IP address to bind to") - port = flag.Int("port", 32000, "listen on port number") - dbglvl = flag.Int("dbg", 2, "debugging level") + opt_bindip = flag.String("bind", "127.0.0.1", "IP address to bind to") + opt_port = flag.Int("port", 32000, "listen on port number") + opt_h1 = flag.Bool("h1", false, "use HTTPS/1.1 transport") + opt_dbglvl = flag.Int("dbg", 2, "debugging level") ) /**********************************************************************/ /* logging stuff */ -func dbg(lvl int, fmt string, v ...interface{}) { if (*dbglvl >= lvl) { dbglog.Printf(fmt, v...) } } +func dbg(lvl int, fmt string, v ...interface{}) { if (*opt_dbglvl >= lvl) { dbglog.Printf(fmt, v...) } } func die(msg error) { dbglog.Fatalln("fatal error:", msg.Error()) } var dbglog *log.Logger @@ -165,7 +166,7 @@ func main() { rcache = cache.New(24*time.Hour, 60*time.Second) /* listen */ - laddr := net.UDPAddr{ IP: net.ParseIP(*bindip), Port: *port } + laddr := net.UDPAddr{ IP: net.ParseIP(*opt_bindip), Port: *opt_port } uc, err := net.ListenUDP("udp", &laddr) if err != nil { die(err) } @@ -173,7 +174,7 @@ func main() { for _, mod := range Modules { mod.Start() } /* accept new connections forever */ - dbg(1, "dingo ver. 0.12 listening on %s UDP port %d", *bindip, laddr.Port) + dbg(1, "dingo ver. 0.13 listening on %s UDP port %d", *opt_bindip, laddr.Port) var buf []byte for { buf = make([]byte, 1500) diff --git a/gdns.go b/gdns.go index 3a5a8ab..16b8508 100644 --- a/gdns.go +++ b/gdns.go @@ -64,7 +64,9 @@ func (R *Gdns) Start() { func (R *Gdns) worker(server string) { var https = NewHttps(*R.sni) - for q := range qchan { *q.rchan <- *R.resolve(https, server, q.Name, q.Type) } + for q := range qchan { + *q.rchan <- *R.resolve(https, server, q.Name, q.Type) + } } func (R *Gdns) resolve(https *Https, server string, qname string, qtype int) *Reply { diff --git a/https.go b/https.go index 033d9dd..d4122c8 100644 --- a/https.go +++ b/https.go @@ -13,21 +13,34 @@ import "net/http" import "io/ioutil" import "crypto/tls" import "errors" +import "golang.org/x/net/http2" type Https struct { - client http.Client - transport http.Transport - tlscfg tls.Config + client http.Client } func NewHttps(sni string) *Https { H := Https{} - /* basic setup */ + /* TLS setup */ + tlscfg := new(tls.Config) + tlscfg.ServerName = sni + + /* HTTP transport */ + var tr http.RoundTripper + if (*opt_h1) { + h1 := new(http.Transport) + h1.TLSClientConfig = tlscfg + tr = h1 + } else { + h2 := new(http2.Transport) + h2.TLSClientConfig = tlscfg + tr = h2 + } + + /* HTTP client */ H.client.Timeout = time.Second * 10 - H.client.Transport = &H.transport - H.transport.TLSClientConfig = &H.tlscfg - H.tlscfg.ServerName = sni + H.client.Transport = tr return &H }