Use a fallback resolver if the local DNS configuration doesn't work

This should fix all chicken-and-egg issues
This commit is contained in:
Frank Denis 2018-01-30 15:46:21 +01:00
parent 24c21d5eb2
commit ecaf18f614
6 changed files with 46 additions and 76 deletions

View File

@ -41,6 +41,7 @@ type Config struct {
SourceIPv4 bool `toml:"ipv4_servers"`
SourceIPv6 bool `toml:"ipv6_servers"`
MaxClients uint32 `toml:"max_clients"`
FallbackResolver string `toml:"fallback_resolver"`
}
func newConfig() Config {
@ -59,6 +60,7 @@ func newConfig() Config {
SourceIPv4: true,
SourceIPv6: false,
MaxClients: 100,
FallbackResolver: DefaultFallbackResolver,
}
}
@ -129,6 +131,7 @@ func ConfigLoad(proxy *Proxy, svcFlag *string, config_file string) error {
} else if config.LogFile != nil {
dlog.UseLogFile(*config.LogFile)
}
proxy.xTransport.fallbackResolver = config.FallbackResolver
proxy.timeout = time.Duration(config.Timeout) * time.Millisecond
proxy.maxClients = config.MaxClients
proxy.mainProto = "udp"
@ -223,7 +226,7 @@ func ConfigLoad(proxy *Proxy, svcFlag *string, config_file string) error {
if cfgSource.RefreshDelay <= 0 {
cfgSource.RefreshDelay = 24
}
source, sourceUrlsToPrefetch, err := NewSource(cfgSource.URL, cfgSource.MinisignKeyStr, cfgSource.CacheFile, cfgSource.FormatStr, time.Duration(cfgSource.RefreshDelay)*time.Hour)
source, sourceUrlsToPrefetch, err := NewSource(proxy.xTransport, cfgSource.URL, cfgSource.MinisignKeyStr, cfgSource.CacheFile, cfgSource.FormatStr, time.Duration(cfgSource.RefreshDelay)*time.Hour)
proxy.urlsToPrefetch = append(proxy.urlsToPrefetch, sourceUrlsToPrefetch...)
if err != nil {
dlog.Criticalf("Unable use source [%s]: [%s]", cfgSourceName, err)

View File

@ -15,7 +15,6 @@
## If this line is commented, all registered servers will be used
# server_names = ['scaleway-fr', 'google', 'yandex']
server_names =['google']
## List of local addresses and ports to listen to. Can be IPv4 and/or IPv6.
## To only use systemd activation sockets, use an empty set: []
@ -65,17 +64,17 @@ force_tcp = false
timeout = 2500
# Log level (0-6, default: 2 - 0 is very verbose, 6 only contains fatal errors)
## Log level (0-6, default: 2 - 0 is very verbose, 6 only contains fatal errors)
# log_level = 2
# log file for the application
## log file for the application
# log_file = 'dnscrypt-proxy.log'
# Use the system logger (syslog on Unix, Event Log on Windows)
## Use the system logger (syslog on Unix, Event Log on Windows)
# use_syslog = true
@ -85,6 +84,18 @@ timeout = 2500
cert_refresh_delay = 30
## Fallback resolver
## This is a normal, non-encrypted resolver, that will be only used
## for one-shot queries when retrieving the initial resolvers list, and
## only if the system DNS configuration doesn't work.
## No user application queries will ever be leaked through this resolver,
## and it will not be used after IP addresses of resolvers URLs have been found.
## It will never be used at all if lists have already been cached, and if
## stamps don't include host names without IP addresses.
## A resolver supporting DNSSEC is recommended. This may become mandatory.
fallback_resolver = "9.9.9.9:53"
#########################
# Filters #

View File

@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"sync"
"time"
"github.com/jedisct1/dlog"
"github.com/kardianos/service"
@ -40,12 +41,12 @@ func main() {
dlog.Debug(err)
}
app.proxy = Proxy{}
app.proxy.xTransport = NewXTransport(30 * time.Second)
cdFileDir(ConfigFileName)
if err := ConfigLoad(&app.proxy, svcFlag, ConfigFileName); err != nil {
dlog.Fatal(err)
}
app.proxy.xTransport = NewXTransport(app.proxy.timeout)
dlog.Noticef("Starting dnscrypt-proxy %s", AppVersion)
if len(*svcFlag) != 0 {

View File

@ -1,11 +1,9 @@
package main
import (
"bytes"
"io/ioutil"
"math/rand"
"net"
"net/http"
"sync/atomic"
"time"
@ -108,8 +106,8 @@ func (proxy *Proxy) prefetcher(urlsToPrefetch *[]URLToPrefetch) {
urlToPrefetch := &(*urlsToPrefetch)[i]
if now.After(urlToPrefetch.when) {
dlog.Debugf("Prefetching [%s]", urlToPrefetch.url)
if err := PrefetchSourceURL(urlToPrefetch); err != nil {
dlog.Debugf("Prefetching [%s] failed: %s", err)
if err := PrefetchSourceURL(proxy.xTransport, urlToPrefetch); err != nil {
dlog.Debugf("Prefetching [%s] failed: %s", urlToPrefetch.url, err)
} else {
dlog.Debugf("Prefetching [%s] succeeded. Next refresh scheduled for %v", urlToPrefetch.url, urlToPrefetch.when)
}
@ -278,25 +276,8 @@ func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, clientProto str
return
}
} else if serverInfo.Proto == StampProtoTypeDoH {
req := &http.Request{
Method: "POST",
URL: serverInfo.URL,
Host: serverInfo.HostName,
Header: map[string][]string{
"Accept": {"application/dns-udpwireformat"},
"Content-Type": {"application/dns-udpwireformat"},
"User-Agent": {"dnscrypt-proxy"},
},
Close: false,
Body: ioutil.NopCloser(bytes.NewReader(query)),
}
client := http.Client{
Transport: proxy.xTransport.transport,
Timeout: proxy.timeout,
}
resp, err := client.Do(req)
if (err == nil && resp != nil && (resp.StatusCode < 200 || resp.StatusCode > 299)) ||
err != nil || resp == nil {
resp, _, err := proxy.xTransport.Post(serverInfo.URL, "application/dns-udpwireformat", "application/dns-udpwireformat", query, proxy.timeout)
if err != nil {
return
}
response, err = ioutil.ReadAll(resp.Body)

View File

@ -1,7 +1,6 @@
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"errors"
@ -9,7 +8,6 @@ import (
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"strings"
"sync"
@ -209,46 +207,16 @@ func (serversInfo *ServersInfo) fetchDoHServerInfo(proxy *Proxy, name string, st
Host: stamp.providerName,
Path: stamp.path,
}
client := http.Client{
Transport: proxy.xTransport.transport,
Timeout: proxy.timeout,
}
preReq := &http.Request{
Method: "HEAD",
URL: url,
Header: map[string][]string{
"User-Agent": {"dnscrypt-proxy"},
},
Close: false,
Host: stamp.providerName,
}
if _, err := client.Do(preReq); err != nil {
return ServerInfo{}, err
}
body := ioutil.NopCloser(bytes.NewReader([]byte{
body := []byte{
0xca, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
}))
req := &http.Request{
Method: "POST",
URL: url,
Header: map[string][]string{
"Accept": {"application/dns-udpwireformat"},
"Content-Type": {"application/dns-udpwireformat"},
"User-Agent": {"dnscrypt-proxy"},
},
Close: false,
Host: stamp.providerName,
Body: body,
}
start := time.Now()
resp, err := client.Do(req)
rtt := time.Since(start)
if err == nil && resp != nil && (resp.StatusCode < 200 || resp.StatusCode > 299) {
return ServerInfo{}, fmt.Errorf("Webserver returned code %d", resp.StatusCode)
} else if err != nil {
_, _, err := proxy.xTransport.Post(url, "application/dns-udpwireformat", "application/dns-udpwireformat", body, proxy.timeout)
if err != nil {
return ServerInfo{}, err
}
resp, rtt, err := proxy.xTransport.Post(url, "application/dns-udpwireformat", "application/dns-udpwireformat", body, proxy.timeout)
if err != nil {
return ServerInfo{}, err
} else if resp == nil {
return ServerInfo{}, errors.New("Webserver returned an error")
}
tls := resp.TLS
if tls == nil || !tls.HandshakeComplete {

View File

@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
@ -58,7 +59,7 @@ func fetchFromCache(cacheFile string) (in string, delayTillNextUpdate time.Durat
return
}
func fetchWithCache(url string, cacheFile string) (in string, cached bool, delayTillNextUpdate time.Duration, err error) {
func fetchWithCache(xTransport *XTransport, urlStr string, cacheFile string) (in string, cached bool, delayTillNextUpdate time.Duration, err error) {
cached = false
in, delayTillNextUpdate, err = fetchFromCache(cacheFile)
if err == nil {
@ -67,8 +68,13 @@ func fetchWithCache(url string, cacheFile string) (in string, cached bool, delay
return
}
var resp *http.Response
dlog.Infof("Loading source information from URL [%s]", url)
resp, err = http.Get(url)
dlog.Infof("Loading source information from URL [%s]", urlStr)
url, err := url.Parse(urlStr)
if err != nil {
return
}
resp, _, err = xTransport.Get(url, 30*time.Second)
if err == nil && resp != nil && (resp.StatusCode < 200 || resp.StatusCode > 299) {
err = fmt.Errorf("Webserver returned code %d", resp.StatusCode)
return
@ -100,7 +106,7 @@ type URLToPrefetch struct {
when time.Time
}
func NewSource(url string, minisignKeyStr string, cacheFile string, formatStr string, refreshDelay time.Duration) (Source, []URLToPrefetch, error) {
func NewSource(xTransport *XTransport, url string, minisignKeyStr string, cacheFile string, formatStr string, refreshDelay time.Duration) (Source, []URLToPrefetch, error) {
_ = refreshDelay
source := Source{url: url}
if formatStr == "v1" {
@ -118,11 +124,11 @@ func NewSource(url string, minisignKeyStr string, cacheFile string, formatStr st
urlsToPrefetch := []URLToPrefetch{}
sigURL := url + ".minisig"
in, cached, delayTillNextUpdate, err := fetchWithCache(url, cacheFile)
in, cached, delayTillNextUpdate, err := fetchWithCache(xTransport, url, cacheFile)
urlsToPrefetch = append(urlsToPrefetch, URLToPrefetch{url: url, cacheFile: cacheFile, when: now.Add(delayTillNextUpdate)})
sigCacheFile := cacheFile + ".minisig"
sigStr, sigCached, sigDelayTillNextUpdate, sigErr := fetchWithCache(sigURL, sigCacheFile)
sigStr, sigCached, sigDelayTillNextUpdate, sigErr := fetchWithCache(xTransport, sigURL, sigCacheFile)
urlsToPrefetch = append(urlsToPrefetch, URLToPrefetch{url: sigURL, cacheFile: sigCacheFile, when: now.Add(sigDelayTillNextUpdate)})
if err != nil || sigErr != nil {
@ -253,8 +259,8 @@ func (source *Source) parseV2(prefix string) ([]RegisteredServer, error) {
return registeredServers, nil
}
func PrefetchSourceURL(urlToPrefetch *URLToPrefetch) error {
in, _, delayTillNextUpdate, err := fetchWithCache(urlToPrefetch.url, urlToPrefetch.cacheFile)
func PrefetchSourceURL(xTransport *XTransport, urlToPrefetch *URLToPrefetch) error {
in, _, delayTillNextUpdate, err := fetchWithCache(xTransport, urlToPrefetch.url, urlToPrefetch.cacheFile)
if err == nil {
AtomicFileWrite(urlToPrefetch.cacheFile, []byte(in))
}