Compare commits

..

9 Commits

Author SHA1 Message Date
Star Brilliant
34adf40b36 Release 1.3.0 2018-04-16 13:35:38 +08:00
Star Brilliant
b9c1bcaad2 Put [::1] into default listen addresses 2018-04-16 13:18:51 +08:00
Star Brilliant
47df06b6e2 Merge pull request #9 from gdm85/master
Add client/server support for multiple listen addresses
2018-04-16 03:36:30 +08:00
gdm85
1abba72898 Add server support for multiple listen addresses 2018-04-15 19:57:17 +02:00
gdm85
ce656ac3f7 Add client support for multiple listen addresses 2018-04-15 19:57:01 +02:00
Star Brilliant
83f20767ea Update to IETF draft-07 2018-04-13 02:46:03 +08:00
Star Brilliant
07db7ba200 Merge pull request #7 from joubin/master
Linux (Ubuntu) easy install
2018-04-13 02:32:03 +08:00
Joubin Jabbari
cdb8599c9f Made changes per pull request feedback #7. 2018-04-12 08:24:33 -10:00
Joubin Jabbari
196207631b Added instructions and script for easy installation under Ubuntu
Tested with:

  * Ubuntu 16.04 LTS
  * Raspbian (Jesse, Pi 3 B+)
2018-04-09 16:17:45 -10:00
12 changed files with 221 additions and 68 deletions

62
Changelog.md Normal file
View File

@@ -0,0 +1,62 @@
# Changelog
This Changelog records major changes between versions.
Not all changes are recorded. Please check git log for details.
## Version 1.3.0
- Breaking change: Add client / server support for multiple listen address
The `listen` option in the configuration file is a list now
## Version 1.2.1
- Update protocol to IETF draft-07
- Update installation documentations for Ubuntu / Debian
## Version 1.2.0
- Add installation documentations for Ubuntu / Debian
- Include CloudFlare DOH server (1.1.1.1, 1.0.0.1) in default configuration
- Fix a problem causing `go get` to fail due to relative paths
- Add documentation about `/etc/hosts` preloading
## Version 1.1.4
- Add `no_cookies` option
- Add documentation on privacy issues
- Adapt for CloudFlare DNS service
- Fix a problem causing a single network failure blocking future requests
- Add experimental macOS support
## Version 1.1.3
- Unsupported Content-Type now generates HTTP error code 415
## Version 1.1.2
- Adapt to IETF protocol
- Optimize for HTTP caches
## Version 1.1.1
- Adapt to IETF protocol
- Optimize for HTTP caches
- Add documentation for uninstallation instructions
- Fix build issues
## Version 1.1.0
- Adpat to IETF protocol
- Fix issues regarding to HTTP caching
- Require Go 1.9 to build now
- Fix systemd issue
## Version 1.0.1
- Fix build issues
## Version 1.0.0
- First release
- Relicense as MIT license

View File

@@ -42,8 +42,8 @@ import (
type Client struct {
conf *config
bootstrap []string
udpServer *dns.Server
tcpServer *dns.Server
udpServers []*dns.Server
tcpServers []*dns.Server
bootstrapResolver *net.Resolver
cookieJar *cookiejar.Jar
httpClientMux *sync.RWMutex
@@ -64,16 +64,21 @@ func NewClient(conf *config) (c *Client, err error) {
c = &Client{
conf: conf,
}
c.udpServer = &dns.Server{
Addr: conf.Listen,
Net: "udp",
Handler: dns.HandlerFunc(c.udpHandlerFunc),
UDPSize: 4096,
}
c.tcpServer = &dns.Server{
Addr: conf.Listen,
Net: "tcp",
Handler: dns.HandlerFunc(c.tcpHandlerFunc),
udpH := dns.HandlerFunc(c.udpHandlerFunc)
tcpH := dns.HandlerFunc(c.tcpHandlerFunc)
for _, addr := range conf.Listen {
c.udpServers = append(c.udpServers, &dns.Server{
Addr: addr,
Net: "udp",
Handler: udpH,
UDPSize: 4096,
})
c.tcpServers = append(c.tcpServers, &dns.Server{
Addr: addr,
Net: "tcp",
Handler: tcpH,
})
}
c.bootstrapResolver = net.DefaultResolver
if len(conf.Bootstrap) != 0 {
@@ -149,27 +154,25 @@ func (c *Client) newHTTPClient() error {
}
func (c *Client) Start() error {
result := make(chan error)
go func() {
err := c.udpServer.ListenAndServe()
if err != nil {
log.Println(err)
}
result <- err
}()
go func() {
err := c.tcpServer.ListenAndServe()
if err != nil {
log.Println(err)
}
result <- err
}()
err := <-result
if err != nil {
return err
result := make(chan error, len(c.udpServers)+len(c.tcpServers))
for _, srv := range append(c.udpServers, c.tcpServers...) {
go func(srv *dns.Server) {
err := srv.ListenAndServe()
if err != nil {
log.Println(err)
}
result <- err
}(srv)
}
err = <-result
return err
for i := 0; i < cap(result); i++ {
err := <-result
if err != nil {
return err
}
}
close(result)
return nil
}
func (c *Client) handlerFunc(w dns.ResponseWriter, r *dns.Msg, isTCP bool) {
@@ -182,21 +185,21 @@ func (c *Client) handlerFunc(w dns.ResponseWriter, r *dns.Msg, isTCP bool) {
if len(c.conf.UpstreamIETF) == 0 {
requestType = "application/dns-json"
} else if len(c.conf.UpstreamGoogle) == 0 {
requestType = "message/dns"
requestType = "application/dns-message"
} else {
numServers := len(c.conf.UpstreamGoogle) + len(c.conf.UpstreamIETF)
random := rand.Intn(numServers)
if random < len(c.conf.UpstreamGoogle) {
requestType = "application/dns-json"
} else {
requestType = "message/dns"
requestType = "application/dns-message"
}
}
var req *DNSRequest
if requestType == "application/dns-json" {
req = c.generateRequestGoogle(w, r, isTCP)
} else if requestType == "message/dns" {
} else if requestType == "application/dns-message" {
req = c.generateRequestIETF(w, r, isTCP)
} else {
panic("Unknown request Content-Type")
@@ -210,21 +213,21 @@ func (c *Client) handlerFunc(w dns.ResponseWriter, r *dns.Msg, isTCP bool) {
candidateType := strings.SplitN(req.response.Header.Get("Content-Type"), ";", 2)[0]
if candidateType == "application/json" {
contentType = "application/json"
} else if candidateType == "message/dns" {
contentType = "message/dns"
} else if candidateType == "application/dns-message" {
contentType = "application/dns-message"
} else if candidateType == "application/dns-udpwireformat" {
contentType = "message/dns"
contentType = "application/dns-message"
} else {
if requestType == "application/dns-json" {
contentType = "application/json"
} else if requestType == "message/dns" {
contentType = "message/dns"
} else if requestType == "application/dns-message" {
contentType = "application/dns-message"
}
}
if contentType == "application/json" {
c.parseResponseGoogle(w, r, isTCP, req)
} else if contentType == "message/dns" {
} else if contentType == "application/dns-message" {
c.parseResponseIETF(w, r, isTCP, req)
} else {
panic("Unknown response Content-Type")

View File

@@ -30,7 +30,7 @@ import (
)
type config struct {
Listen string `toml:"listen"`
Listen []string `toml:"listen"`
UpstreamGoogle []string `toml:"upstream_google"`
UpstreamIETF []string `toml:"upstream_ietf"`
Bootstrap []string `toml:"bootstrap"`
@@ -50,8 +50,8 @@ func loadConfig(path string) (*config, error) {
return nil, &configError{fmt.Sprintf("unknown option %q", key.String())}
}
if conf.Listen == "" {
conf.Listen = "127.0.0.1:53"
if len(conf.Listen) == 0 {
conf.Listen = []string{"127.0.0.1:53", "[::1]:53"}
}
if len(conf.UpstreamGoogle) == 0 && len(conf.UpstreamIETF) == 0 {
conf.UpstreamGoogle = []string{"https://dns.google.com/resolve"}

View File

@@ -1,5 +1,8 @@
# DNS listen port
listen = "127.0.0.1:53"
listen = [
"127.0.0.1:53",
"[::1]:53",
]
# HTTP path for upstream resolver
# If multiple servers are specified, a random one will be chosen each time.

View File

@@ -91,7 +91,7 @@ func (c *Client) generateRequestGoogle(w dns.ResponseWriter, r *dns.Msg, isTCP b
err: err,
}
}
req.Header.Set("Accept", "application/json, message/dns, application/dns-udpwireformat")
req.Header.Set("Accept", "application/json, application/dns-message, application/dns-udpwireformat")
req.Header.Set("User-Agent", "DNS-over-HTTPS/1.1 (+https://github.com/m13253/dns-over-https)")
c.httpClientMux.RLock()
resp, err := c.httpClient.Do(req)

View File

@@ -128,7 +128,7 @@ func (c *Client) generateRequestIETF(w dns.ResponseWriter, r *dns.Msg, isTCP boo
numServers := len(c.conf.UpstreamIETF)
upstream := c.conf.UpstreamIETF[rand.Intn(numServers)]
requestURL := fmt.Sprintf("%s?ct=application/dns-udpwireformat&dns=%s", upstream, requestBase64)
//requestURL := fmt.Sprintf("%s?ct=message/dns&dns=%s", upstream, requestBase64)
//requestURL := fmt.Sprintf("%s?ct=application/dns-message&dns=%s", upstream, requestBase64)
var req *http.Request
if len(requestURL) < 2048 {
@@ -151,9 +151,9 @@ func (c *Client) generateRequestIETF(w dns.ResponseWriter, r *dns.Msg, isTCP boo
err: err,
}
}
req.Header.Set("Content-Type", "message/dns")
req.Header.Set("Content-Type", "application/dns-message")
}
req.Header.Set("Accept", "message/dns, application/dns-udpwireformat, application/json")
req.Header.Set("Accept", "application/dns-message, application/dns-udpwireformat, application/json")
req.Header.Set("User-Agent", "DNS-over-HTTPS/1.1 (+https://github.com/m13253/dns-over-https)")
c.httpClientMux.RLock()
resp, err := c.httpClient.Do(req)
@@ -185,7 +185,7 @@ func (c *Client) parseResponseIETF(w dns.ResponseWriter, r *dns.Msg, isTCP bool,
log.Printf("HTTP error: %s\n", req.response.Status)
req.reply.Rcode = dns.RcodeServerFailure
contentType := req.response.Header.Get("Content-Type")
if contentType != "message/dns" && !strings.HasPrefix(contentType, "message/dns;") {
if contentType != "application/dns-message" && !strings.HasPrefix(contentType, "application/dns-message;") {
w.WriteMsg(req.reply)
return
}

View File

@@ -30,7 +30,7 @@ import (
)
type config struct {
Listen string `toml:"listen"`
Listen []string `toml:"listen"`
Cert string `toml:"cert"`
Key string `toml:"key"`
Path string `toml:"path"`
@@ -51,9 +51,10 @@ func loadConfig(path string) (*config, error) {
return nil, &configError{fmt.Sprintf("unknown option %q", key.String())}
}
if conf.Listen == "" {
conf.Listen = "127.0.0.1:8053"
if len(conf.Listen) == 0 {
conf.Listen = []string{"127.0.0.1:8053", "[::1]:8053"}
}
if conf.Path == "" {
conf.Path = "/dns-query"
}

View File

@@ -1,5 +1,8 @@
# HTTP listen port
listen = "127.0.0.1:8053"
listen = [
"127.0.0.1:8053",
"[::1]:8053",
]
# TLS certification file
cert = ""

View File

@@ -45,7 +45,7 @@ func (s *Server) parseRequestIETF(w http.ResponseWriter, r *http.Request) *DNSRe
errtext: fmt.Sprintf("Invalid argument value: \"dns\" = %q", requestBase64),
}
}
if len(requestBinary) == 0 && (r.Header.Get("Content-Type") == "message/dns" || r.Header.Get("Content-Type") == "application/dns-udpwireformat") {
if len(requestBinary) == 0 && (r.Header.Get("Content-Type") == "application/dns-message" || r.Header.Get("Content-Type") == "application/dns-udpwireformat") {
requestBinary, err = ioutil.ReadAll(r.Body)
if err != nil {
return &DNSRequest{
@@ -144,7 +144,7 @@ func (s *Server) generateResponseIETF(w http.ResponseWriter, r *http.Request, re
return
}
w.Header().Set("Content-Type", "message/dns")
w.Header().Set("Content-Type", "application/dns-message")
now := time.Now().UTC().Format(http.TimeFormat)
w.Header().Set("Date", now)
w.Header().Set("Last-Modified", now)

View File

@@ -75,10 +75,27 @@ func (s *Server) Start() error {
if s.conf.Verbose {
servemux = handlers.CombinedLoggingHandler(os.Stdout, servemux)
}
if s.conf.Cert != "" || s.conf.Key != "" {
return http.ListenAndServeTLS(s.conf.Listen, s.conf.Cert, s.conf.Key, servemux)
listeners := make(chan error, len(s.conf.Listen))
for _, addr := range s.conf.Listen {
if s.conf.Cert != "" || s.conf.Key != "" {
go func() {
listeners <- http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
}()
continue
}
go func() {
listeners <- http.ListenAndServe(addr, servemux)
}()
}
return http.ListenAndServe(s.conf.Listen, servemux)
// wait for all handlers
for i := 0; i < cap(listeners); i++ {
err := <-listeners
if err != nil {
return err
}
}
close(listeners)
return nil
}
func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
@@ -98,7 +115,7 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
if r.FormValue("name") != "" {
contentType = "application/dns-json"
} else if r.FormValue("dns") != "" {
contentType = "message/dns"
contentType = "application/dns-message"
}
}
var responseType string
@@ -108,10 +125,10 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
responseType = "application/json"
break
} else if responseCandidate == "application/dns-udpwireformat" {
responseType = "message/dns"
responseType = "application/dns-message"
break
} else if responseCandidate == "message/dns" {
responseType = "message/dns"
} else if responseCandidate == "application/dns-message" {
responseType = "application/dns-message"
break
}
}
@@ -119,17 +136,17 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
// Guess response Content-Type based on request Content-Type
if contentType == "application/dns-json" {
responseType = "application/json"
} else if contentType == "message/dns" {
responseType = "message/dns"
} else if contentType == "application/dns-message" {
responseType = "application/dns-message"
} else if contentType == "application/dns-udpwireformat" {
responseType = "message/dns"
responseType = "application/dns-message"
}
}
var req *DNSRequest
if contentType == "application/dns-json" {
req = s.parseRequestGoogle(w, r)
} else if contentType == "message/dns" {
} else if contentType == "application/dns-message" {
req = s.parseRequestIETF(w, r)
} else if contentType == "application/dns-udpwireformat" {
req = s.parseRequestIETF(w, r)
@@ -151,7 +168,7 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
if responseType == "application/json" {
s.generateResponseGoogle(w, r, req)
} else if responseType == "message/dns" {
} else if responseType == "application/dns-message" {
s.generateResponseIETF(w, r, req)
} else {
panic("Unknown response Content-Type")

51
linux-install.md Normal file
View File

@@ -0,0 +1,51 @@
# Ubuntu Install
> Tested on a clean install of `Ubuntu 16.04 LTS`
## Intalling go
Install `Go >= 1.9`
```bash
sudo apt update
sudo apt install golang-1.10 -y
```
Add the newly install `go` to the path
```bash
export PATH=$PATH:/usr/lib/go-1.10/bin
```
Test to make sure that you can execute `go`
```bash
go version
```
which should output something like
```bash
go version go1.10.1 linux/amd64
```
## Installing dns-over-https
Clone this repo
```bash
git clone https://github.com/m13253/dns-over-https.git
```
Change directory to the cloned repo
```bash
cd dns-over-https
```
make and install
```bash
make
sudo make install
```

13
linux-install.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
# See the linux-install.md (README) first.
set -e
sudo apt update
sudo apt install golang-1.10 git -y
export PATH=$PATH:/usr/lib/go-1.10/bin
cd /tmp
git clone https://github.com/m13253/dns-over-https.git
cd dns-over-https
make
sudo make install