Compare commits

...

31 Commits

Author SHA1 Message Date
Star Brilliant
4cbe7c8c98 Release 1.3.4 2018-04-26 10:33:38 +08:00
Star Brilliant
7839d2c7b1 Bump version to 1.3.4 2018-04-26 10:18:36 +08:00
Star Brilliant
c4b2236cf9 Do not respond to network error, silently fail to prevent caching of SERVFAIL
Hopefully we can improve the availability of DoH under unstable network environments.
2018-04-26 10:17:14 +08:00
Star Brilliant
1b90731f20 Add version.go to Makefile 2018-04-25 18:36:06 +08:00
Star Brilliant
20624acf20 Update documents 2018-04-25 14:23:24 +08:00
Star Brilliant
01385b6d29 Update documents 2018-04-25 14:19:40 +08:00
Star Brilliant
5afdee6315 Put EDNS0 at the beginning of the OPT section 2018-04-25 03:05:06 +08:00
Star Brilliant
874a3613e4 Use dns.DefaultMsgSize instead of magic number 4096 2018-04-25 03:04:31 +08:00
Star Brilliant
dc14a70e9d Use dns.DefaultMsgSize instead of magic number 4096 2018-04-24 20:46:34 +08:00
Star Brilliant
58e4018ab2 Rename variables 2018-04-24 20:43:24 +08:00
Star Brilliant
f4516429ee Take User-Agent out of common library, that would be better for packaging 2018-04-24 14:25:33 +08:00
Star Brilliant
12df47f45f Release 1.3.2 2018-04-17 03:28:36 +08:00
Star Brilliant
450c10a594 Fix version number in User-Agent 2018-04-17 03:27:42 +08:00
Star Brilliant
e7c4450787 Fix build system 2018-04-17 03:27:21 +08:00
Star Brilliant
bd5ef5d61e Fix build 2018-04-16 21:36:26 +08:00
Star Brilliant
ff0e9529cb Update User-Agent 2018-04-16 21:35:33 +08:00
Star Brilliant
627e2d639d Release 1.3.1 2018-04-16 13:59:03 +08:00
Star Brilliant
7d5cf98d2b Fix the "address already in use" issue 2018-04-16 13:58:38 +08:00
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
Star Brilliant
236f7931e6 Update to IETF draft-06 2018-04-10 14:02:51 +08: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
Star Brilliant
9562c2fe5c Add 1.1.1.1 and 1.0.0.1 DOH 2018-04-04 00:14:28 +08:00
Star Brilliant
0a107be362 Use absolute path for ../json-dns 2018-04-02 21:07:49 +08:00
Star Brilliant
efa272bc52 Add documentation about /etc/hosts preloading 2018-04-02 17:19:39 +08:00
18 changed files with 342 additions and 89 deletions

81
Changelog.md Normal file
View File

@@ -0,0 +1,81 @@
# Changelog
This Changelog records major changes between versions.
Not all changes are recorded. Please check git log for details.
## Version 1.3.4
- doh-client now silently fails in case of network error to prevent caching of SERVFAIL
- EDNS0 is now inserted to the beginning of OPT section, to ensure DNSSEC signatures are at the end
- Improve building system
- Update documents
## Version 1.3.3
- Take User-Agent out of common library, that would be better for packaging
## Version 1.3.2
- Fix version string in HTTP User-Agent
## Version 1.3.1
- Fix the "address already in use" issue
## 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

@@ -2,6 +2,7 @@
GOBUILD=go build GOBUILD=go build
GOGET=go get -d -v GOGET=go get -d -v
GOGET_UPDATE=go get -d -u -v
PREFIX=/usr/local PREFIX=/usr/local
ifeq ($(shell uname),Darwin) ifeq ($(shell uname),Darwin)
CONFDIR=/usr/local/etc/dns-over-https CONFDIR=/usr/local/etc/dns-over-https
@@ -40,10 +41,12 @@ uninstall:
fi fi
deps: deps:
@# I am not sure if it is the correct way to keep the common library updated
$(GOGET_UPDATE) github.com/m13253/dns-over-https/json-dns
$(GOGET) ./doh-client ./doh-server $(GOGET) ./doh-client ./doh-server
doh-client/doh-client: deps doh-client/client.go doh-client/config.go doh-client/google.go doh-client/ietf.go doh-client/main.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go doh-client/doh-client: deps doh-client/client.go doh-client/config.go doh-client/google.go doh-client/ietf.go doh-client/main.go doh-client/version.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go
cd doh-client && $(GOBUILD) cd doh-client && $(GOBUILD)
doh-server/doh-server: deps doh-server/config.go doh-server/google.go doh-server/ietf.go doh-server/main.go doh-server/server.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go doh-server/doh-server: deps doh-server/config.go doh-server/google.go doh-server/ietf.go doh-server/main.go doh-server/server.go doh-server/version.go json-dns/error.go json-dns/globalip.go json-dns/marshal.go json-dns/response.go json-dns/unmarshal.go
cd doh-server && $(GOBUILD) cd doh-server && $(GOBUILD)

View File

@@ -70,6 +70,9 @@ The following is a typical DNS-over-HTTPS architecture:
Although DNS-over-HTTPS can work alone, a HTTP service muxer would be useful as Although DNS-over-HTTPS can work alone, a HTTP service muxer would be useful as
you can host DNS-over-HTTPS along with other HTTPS services. you can host DNS-over-HTTPS along with other HTTPS services.
HTTP/2 with at least TLS v1.3 is recommended. OCSP stapling must be enabled,
otherwise DNS recursion may happen.
## DNSSEC ## DNSSEC
DNS-over-HTTPS is compatible with DNSSEC, and requests DNSSEC signatures by DNS-over-HTTPS is compatible with DNSSEC, and requests DNSSEC signatures by
@@ -90,8 +93,10 @@ EDNS0-Client-Subnet is affecting your privacy, you can set `no_ecs = true` in
`/etc/dns-over-https/doh-client.conf`, with the cost of slower video streaming `/etc/dns-over-https/doh-client.conf`, with the cost of slower video streaming
or software downloading speed. or software downloading speed.
If your server is backed by `unbound` or `bind`, you probably want to enable To ultilize ECS, `X-Forwarded-For` or `X-Real-IP` should be enabled on your
the EDNS0-Client-Subnet feature in their configuration files as well. HTTP service muxer. If your server is backed by `unbound` or `bind`, you
probably want to configure it to enable the EDNS0-Client-Subnet feature as
well.
## Protocol compatibility ## Protocol compatibility

View File

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

View File

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

View File

@@ -1,5 +1,8 @@
# DNS listen port # DNS listen port
listen = "127.0.0.1:53" listen = [
"127.0.0.1:53",
"[::1]:53",
]
# HTTP path for upstream resolver # HTTP path for upstream resolver
# If multiple servers are specified, a random one will be chosen each time. # If multiple servers are specified, a random one will be chosen each time.
@@ -10,6 +13,8 @@ upstream_google = [
# CloudFlare's resolver, bad ECS, good DNSSEC # CloudFlare's resolver, bad ECS, good DNSSEC
#"https://cloudflare-dns.com/dns-query", #"https://cloudflare-dns.com/dns-query",
#"https://1.1.1.1/dns-query",
#"https://1.0.0.1/dns-query",
] ]
upstream_ietf = [ upstream_ietf = [
@@ -19,12 +24,16 @@ upstream_ietf = [
# CloudFlare's resolver, bad ECS, good DNSSEC # CloudFlare's resolver, bad ECS, good DNSSEC
#"https://cloudflare-dns.com/dns-query", #"https://cloudflare-dns.com/dns-query",
#"https://1.1.1.1/dns-query",
#"https://1.0.0.1/dns-query",
] ]
# Bootstrap DNS server to resolve the address of the upstream resolver # Bootstrap DNS server to resolve the address of the upstream resolver
# If multiple servers are specified, a random one will be chosen each time. # If multiple servers are specified, a random one will be chosen each time.
# If empty, use the system DNS settings. # If empty, use the system DNS settings.
# If you want to preload IP addresses in /etc/hosts instead of using a
# bootstrap server, please make this list empty.
bootstrap = [ bootstrap = [
# Google's resolver, bad ECS, good DNSSEC # Google's resolver, bad ECS, good DNSSEC
@@ -38,7 +47,7 @@ bootstrap = [
] ]
# Timeout for upstream request # Timeout for upstream request
timeout = 10 timeout = 30
# Disable HTTP Cookies # Disable HTTP Cookies
# #

View File

@@ -35,7 +35,7 @@ import (
"strings" "strings"
"time" "time"
"../json-dns" "github.com/m13253/dns-over-https/json-dns"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -91,8 +91,8 @@ func (c *Client) generateRequestGoogle(w dns.ResponseWriter, r *dns.Msg, isTCP b
err: err, err: err,
} }
} }
req.Header.Set("Accept", "application/json, 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)") req.Header.Set("User-Agent", USER_AGENT)
c.httpClientMux.RLock() c.httpClientMux.RLock()
resp, err := c.httpClient.Do(req) resp, err := c.httpClient.Do(req)
c.httpClientMux.RUnlock() c.httpClientMux.RUnlock()

View File

@@ -36,7 +36,7 @@ import (
"strings" "strings"
"time" "time"
"../json-dns" "github.com/m13253/dns-over-https/json-dns"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -73,9 +73,9 @@ func (c *Client) generateRequestIETF(w dns.ResponseWriter, r *dns.Msg, isTCP boo
opt = new(dns.OPT) opt = new(dns.OPT)
opt.Hdr.Name = "." opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096) opt.SetUDPSize(dns.DefaultMsgSize)
opt.SetDo(false) opt.SetDo(false)
r.Extra = append(r.Extra, opt) r.Extra = append([]dns.RR{opt}, r.Extra...)
} else { } else {
udpSize = opt.UDPSize() udpSize = opt.UDPSize()
} }
@@ -128,14 +128,14 @@ func (c *Client) generateRequestIETF(w dns.ResponseWriter, r *dns.Msg, isTCP boo
numServers := len(c.conf.UpstreamIETF) numServers := len(c.conf.UpstreamIETF)
upstream := c.conf.UpstreamIETF[rand.Intn(numServers)] upstream := c.conf.UpstreamIETF[rand.Intn(numServers)]
requestURL := fmt.Sprintf("%s?ct=application/dns-udpwireformat&dns=%s", upstream, requestBase64) requestURL := fmt.Sprintf("%s?ct=application/dns-udpwireformat&dns=%s", upstream, requestBase64)
//requestURL := fmt.Sprintf("%s?ct=application/dns-message&dns=%s", upstream, requestBase64)
var req *http.Request var req *http.Request
if len(requestURL) < 2048 { if len(requestURL) < 2048 {
req, err = http.NewRequest("GET", requestURL, nil) req, err = http.NewRequest("GET", requestURL, nil)
if err != nil { if err != nil {
// Do not respond, silently fail to prevent caching of SERVFAIL
log.Println(err) log.Println(err)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return &DNSRequest{ return &DNSRequest{
err: err, err: err,
} }
@@ -143,17 +143,16 @@ func (c *Client) generateRequestIETF(w dns.ResponseWriter, r *dns.Msg, isTCP boo
} else { } else {
req, err = http.NewRequest("POST", upstream, bytes.NewReader(requestBinary)) req, err = http.NewRequest("POST", upstream, bytes.NewReader(requestBinary))
if err != nil { if err != nil {
// Do not respond, silently fail to prevent caching of SERVFAIL
log.Println(err) log.Println(err)
reply.Rcode = dns.RcodeServerFailure
w.WriteMsg(reply)
return &DNSRequest{ return &DNSRequest{
err: err, err: err,
} }
} }
req.Header.Set("Content-Type", "application/dns-udpwireformat") req.Header.Set("Content-Type", "application/dns-message")
} }
req.Header.Set("Accept", "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)") req.Header.Set("User-Agent", USER_AGENT)
c.httpClientMux.RLock() c.httpClientMux.RLock()
resp, err := c.httpClient.Do(req) resp, err := c.httpClient.Do(req)
c.httpClientMux.RUnlock() c.httpClientMux.RUnlock()
@@ -184,7 +183,7 @@ func (c *Client) parseResponseIETF(w dns.ResponseWriter, r *dns.Msg, isTCP bool,
log.Printf("HTTP error: %s\n", req.response.Status) log.Printf("HTTP error: %s\n", req.response.Status)
req.reply.Rcode = dns.RcodeServerFailure req.reply.Rcode = dns.RcodeServerFailure
contentType := req.response.Header.Get("Content-Type") contentType := req.response.Header.Get("Content-Type")
if contentType != "application/dns-udpwireformat" && !strings.HasPrefix(contentType, "application/dns-udpwireformat;") { if contentType != "application/dns-message" && !strings.HasPrefix(contentType, "application/dns-message;") {
w.WriteMsg(req.reply) w.WriteMsg(req.reply)
return return
} }

29
doh-client/version.go Normal file
View File

@@ -0,0 +1,29 @@
/*
DNS-over-HTTPS
Copyright (C) 2017-2018 Star Brilliant <m13253@hotmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
package main
const (
VERSION = "1.3.4"
USER_AGENT = "DNS-over-HTTPS/" + VERSION + " (+https://github.com/m13253/dns-over-https)"
)

View File

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

View File

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

View File

@@ -33,7 +33,7 @@ import (
"strings" "strings"
"time" "time"
"../json-dns" "github.com/m13253/dns-over-https/json-dns"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.org/x/net/idna" "golang.org/x/net/idna"
) )
@@ -150,7 +150,7 @@ func (s *Server) parseRequestGoogle(w http.ResponseWriter, r *http.Request) *DNS
opt := new(dns.OPT) opt := new(dns.OPT)
opt.Hdr.Name = "." opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096) opt.SetUDPSize(dns.DefaultMsgSize)
opt.SetDo(true) opt.SetDo(true)
if ednsClientAddress != nil { if ednsClientAddress != nil {
edns0Subnet := new(dns.EDNS0_SUBNET) edns0Subnet := new(dns.EDNS0_SUBNET)

View File

@@ -32,7 +32,7 @@ import (
"strconv" "strconv"
"time" "time"
"../json-dns" "github.com/m13253/dns-over-https/json-dns"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -45,7 +45,7 @@ func (s *Server) parseRequestIETF(w http.ResponseWriter, r *http.Request) *DNSRe
errtext: fmt.Sprintf("Invalid argument value: \"dns\" = %q", requestBase64), errtext: fmt.Sprintf("Invalid argument value: \"dns\" = %q", requestBase64),
} }
} }
if len(requestBinary) == 0 && 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) requestBinary, err = ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
return &DNSRequest{ return &DNSRequest{
@@ -93,9 +93,9 @@ func (s *Server) parseRequestIETF(w http.ResponseWriter, r *http.Request) *DNSRe
opt = new(dns.OPT) opt = new(dns.OPT)
opt.Hdr.Name = "." opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT opt.Hdr.Rrtype = dns.TypeOPT
opt.SetUDPSize(4096) opt.SetUDPSize(dns.DefaultMsgSize)
opt.SetDo(false) opt.SetDo(false)
msg.Extra = append(msg.Extra, opt) msg.Extra = append([]dns.RR{opt}, msg.Extra...)
} }
var edns0Subnet *dns.EDNS0_SUBNET var edns0Subnet *dns.EDNS0_SUBNET
for _, option := range opt.Option { for _, option := range opt.Option {
@@ -144,7 +144,7 @@ func (s *Server) generateResponseIETF(w http.ResponseWriter, r *http.Request, re
return return
} }
w.Header().Set("Content-Type", "application/dns-udpwireformat") w.Header().Set("Content-Type", "application/dns-message")
now := time.Now().UTC().Format(http.TimeFormat) now := time.Now().UTC().Format(http.TimeFormat)
w.Header().Set("Date", now) w.Header().Set("Date", now)
w.Header().Set("Last-Modified", now) w.Header().Set("Last-Modified", now)

View File

@@ -43,8 +43,5 @@ func main() {
} }
server := NewServer(conf) server := NewServer(conf)
err = server.Start() _ = server.Start()
if err != nil {
log.Fatalln(err)
}
} }

View File

@@ -33,8 +33,8 @@ import (
"strings" "strings"
"time" "time"
"../json-dns"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/m13253/dns-over-https/json-dns"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
@@ -58,6 +58,7 @@ func NewServer(conf *config) (s *Server) {
conf: conf, conf: conf,
udpClient: &dns.Client{ udpClient: &dns.Client{
Net: "udp", Net: "udp",
UDPSize: dns.DefaultMsgSize,
Timeout: time.Duration(conf.Timeout) * time.Second, Timeout: time.Duration(conf.Timeout) * time.Second,
}, },
tcpClient: &dns.Client{ tcpClient: &dns.Client{
@@ -75,15 +76,35 @@ func (s *Server) Start() error {
if s.conf.Verbose { if s.conf.Verbose {
servemux = handlers.CombinedLoggingHandler(os.Stdout, servemux) servemux = handlers.CombinedLoggingHandler(os.Stdout, servemux)
} }
if s.conf.Cert != "" || s.conf.Key != "" { results := make(chan error, len(s.conf.Listen))
return http.ListenAndServeTLS(s.conf.Listen, s.conf.Cert, s.conf.Key, servemux) for _, addr := range s.conf.Listen {
go func(addr string) {
var err error
if s.conf.Cert != "" || s.conf.Key != "" {
err = http.ListenAndServeTLS(addr, s.conf.Cert, s.conf.Key, servemux)
} else {
err = http.ListenAndServe(addr, servemux)
}
if err != nil {
log.Println(err)
}
results <- err
}(addr)
} }
return http.ListenAndServe(s.conf.Listen, servemux) // wait for all handlers
for i := 0; i < cap(results); i++ {
err := <-results
if err != nil {
return err
}
}
close(results)
return nil
} }
func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) { func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "DNS-over-HTTPS/1.1 (+https://github.com/m13253/dns-over-https)") w.Header().Set("Server", USER_AGENT)
w.Header().Set("X-Powered-By", "DNS-over-HTTPS/1.1 (+https://github.com/m13253/dns-over-https)") w.Header().Set("X-Powered-By", USER_AGENT)
if r.Form == nil { if r.Form == nil {
const maxMemory = 32 << 20 // 32 MB const maxMemory = 32 << 20 // 32 MB
@@ -98,17 +119,20 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
if r.FormValue("name") != "" { if r.FormValue("name") != "" {
contentType = "application/dns-json" contentType = "application/dns-json"
} else if r.FormValue("dns") != "" { } else if r.FormValue("dns") != "" {
contentType = "application/dns-udpwireformat" contentType = "application/dns-message"
} }
} }
var responseType string var responseType string
for _, responseCandidate := range strings.Split(r.Header.Get("Accept"), ",") { for _, responseCandidate := range strings.Split(r.Header.Get("Accept"), ",") {
responseCandidate = strings.ToLower(strings.SplitN(responseCandidate, ";", 2)[0]) responseCandidate = strings.SplitN(responseCandidate, ";", 2)[0]
if responseCandidate == "application/json" { if responseCandidate == "application/json" {
responseType = "application/json" responseType = "application/json"
break break
} else if responseCandidate == "application/dns-udpwireformat" { } else if responseCandidate == "application/dns-udpwireformat" {
responseType = "application/dns-udpwireformat" responseType = "application/dns-message"
break
} else if responseCandidate == "application/dns-message" {
responseType = "application/dns-message"
break break
} }
} }
@@ -116,14 +140,18 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
// Guess response Content-Type based on request Content-Type // Guess response Content-Type based on request Content-Type
if contentType == "application/dns-json" { if contentType == "application/dns-json" {
responseType = "application/json" responseType = "application/json"
} else if contentType == "application/dns-message" {
responseType = "application/dns-message"
} else if contentType == "application/dns-udpwireformat" { } else if contentType == "application/dns-udpwireformat" {
responseType = "application/dns-udpwireformat" responseType = "application/dns-message"
} }
} }
var req *DNSRequest var req *DNSRequest
if contentType == "application/dns-json" { if contentType == "application/dns-json" {
req = s.parseRequestGoogle(w, r) req = s.parseRequestGoogle(w, r)
} else if contentType == "application/dns-message" {
req = s.parseRequestIETF(w, r)
} else if contentType == "application/dns-udpwireformat" { } else if contentType == "application/dns-udpwireformat" {
req = s.parseRequestIETF(w, r) req = s.parseRequestIETF(w, r)
} else { } else {
@@ -144,7 +172,7 @@ func (s *Server) handlerFunc(w http.ResponseWriter, r *http.Request) {
if responseType == "application/json" { if responseType == "application/json" {
s.generateResponseGoogle(w, r, req) s.generateResponseGoogle(w, r, req)
} else if responseType == "application/dns-udpwireformat" { } else if responseType == "application/dns-message" {
s.generateResponseIETF(w, r, req) s.generateResponseIETF(w, r, req)
} else { } else {
panic("Unknown response Content-Type") panic("Unknown response Content-Type")

29
doh-server/version.go Normal file
View File

@@ -0,0 +1,29 @@
/*
DNS-over-HTTPS
Copyright (C) 2017-2018 Star Brilliant <m13253@hotmail.com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
package main
const (
VERSION = "1.3.4"
USER_AGENT = "DNS-over-HTTPS/" + VERSION + " (+https://github.com/m13253/dns-over-https)"
)

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