2018-01-30 15:51:07 +01:00
package main
import (
"bytes"
"context"
2018-11-22 18:09:27 +01:00
"crypto/sha512"
2018-04-07 22:23:29 +02:00
"crypto/tls"
2020-06-08 18:01:40 +02:00
"crypto/x509"
2018-02-05 11:30:10 +01:00
"encoding/base64"
2018-11-22 18:09:27 +01:00
"encoding/hex"
2018-01-30 15:51:07 +01:00
"errors"
2020-02-21 22:33:34 +01:00
"io"
2018-01-30 15:51:07 +01:00
"io/ioutil"
2019-10-20 17:09:14 +02:00
"math/rand"
2018-01-30 15:51:07 +01:00
"net"
"net/http"
"net/url"
2018-03-28 12:38:17 +02:00
"strconv"
2018-01-30 15:51:07 +01:00
"strings"
"sync"
"time"
2018-04-18 18:47:10 +02:00
2018-01-30 15:51:07 +01:00
"github.com/jedisct1/dlog"
2018-04-18 18:58:39 +02:00
stamps "github.com/jedisct1/go-dnsstamps"
2018-01-30 15:51:07 +01:00
"github.com/miekg/dns"
2018-04-07 22:23:29 +02:00
"golang.org/x/net/http2"
2018-06-06 15:54:51 +02:00
netproxy "golang.org/x/net/proxy"
2018-01-30 15:51:07 +01:00
)
2019-10-20 16:24:02 +02:00
const (
2021-02-20 18:50:42 +01:00
DefaultBootstrapResolver = "9.9.9.9:53"
DefaultKeepAlive = 5 * time . Second
DefaultTimeout = 30 * time . Second
SystemResolverIPTTL = 24 * time . Hour
MinResolverIPTTL = 12 * time . Hour
ExpiredCachedIPGraceTTL = 15 * time . Minute
2019-10-20 16:24:02 +02:00
)
2018-01-30 15:51:07 +01:00
2019-10-20 17:09:14 +02:00
type CachedIPItem struct {
2019-10-21 18:40:47 +02:00
ip net . IP
expiration * time . Time
2019-10-20 17:09:14 +02:00
}
2018-01-30 15:51:07 +01:00
type CachedIPs struct {
sync . RWMutex
2019-10-20 17:09:14 +02:00
cache map [ string ] * CachedIPItem
2018-01-30 15:51:07 +01:00
}
type XTransport struct {
2018-04-07 22:23:29 +02:00
transport * http . Transport
keepAlive time . Duration
timeout time . Duration
cachedIPs CachedIPs
2021-02-20 18:50:42 +01:00
bootstrapResolvers [ ] string
2019-07-10 13:13:28 +02:00
mainProto string
2018-04-07 22:23:29 +02:00
ignoreSystemDNS bool
useIPv4 bool
useIPv6 bool
tlsDisableSessionTickets bool
tlsCipherSuite [ ] uint16
2018-06-06 15:54:51 +02:00
proxyDialer * netproxy . Dialer
2018-11-15 18:47:33 +01:00
httpProxyFunction func ( * http . Request ) ( * url . URL , error )
2020-03-09 22:11:53 +01:00
tlsClientCreds DOHClientCreds
2018-01-30 15:51:07 +01:00
}
2018-04-07 22:33:11 +02:00
func NewXTransport ( ) * XTransport {
2021-02-20 18:50:42 +01:00
if err := isIPAndPort ( DefaultBootstrapResolver ) ; err != nil {
panic ( "DefaultBootstrapResolver does not parse" )
2019-10-20 20:35:25 +02:00
}
2018-01-30 15:51:07 +01:00
xTransport := XTransport {
2019-10-20 17:09:14 +02:00
cachedIPs : CachedIPs { cache : make ( map [ string ] * CachedIPItem ) } ,
2018-04-07 22:23:29 +02:00
keepAlive : DefaultKeepAlive ,
2018-04-07 22:33:11 +02:00
timeout : DefaultTimeout ,
2021-02-20 18:50:42 +01:00
bootstrapResolvers : [ ] string { DefaultBootstrapResolver } ,
2019-07-10 13:13:28 +02:00
mainProto : "" ,
2019-11-17 20:30:00 +01:00
ignoreSystemDNS : true ,
2018-04-07 22:33:11 +02:00
useIPv4 : true ,
useIPv6 : false ,
2018-04-07 22:23:29 +02:00
tlsDisableSessionTickets : false ,
tlsCipherSuite : nil ,
2018-01-30 15:51:07 +01:00
}
2018-02-05 19:03:04 +01:00
return & xTransport
}
2019-10-20 21:30:16 +02:00
func ParseIP ( ipStr string ) net . IP {
return net . ParseIP ( strings . TrimRight ( strings . TrimLeft ( ipStr , "[" ) , "]" ) )
}
2019-10-20 17:09:14 +02:00
// If ttl < 0, never expire
2019-11-17 20:00:34 +01:00
// Otherwise, ttl is set to max(ttl, MinResolverIPTTL)
2019-10-20 17:09:14 +02:00
func ( xTransport * XTransport ) saveCachedIP ( host string , ip net . IP , ttl time . Duration ) {
2019-10-21 18:40:47 +02:00
item := & CachedIPItem { ip : ip , expiration : nil }
2019-10-20 17:09:14 +02:00
if ttl >= 0 {
2019-11-17 20:00:34 +01:00
if ttl < MinResolverIPTTL {
ttl = MinResolverIPTTL
2019-10-20 17:09:14 +02:00
}
2019-10-21 18:40:47 +02:00
expiration := time . Now ( ) . Add ( ttl )
item . expiration = & expiration
2019-10-20 17:09:14 +02:00
}
2019-10-21 18:36:47 +02:00
xTransport . cachedIPs . Lock ( )
2019-10-20 17:09:14 +02:00
xTransport . cachedIPs . cache [ host ] = item
2018-03-21 09:32:35 +01:00
xTransport . cachedIPs . Unlock ( )
2019-10-20 17:09:14 +02:00
}
2019-11-01 22:55:06 +01:00
func ( xTransport * XTransport ) loadCachedIP ( host string ) ( ip net . IP , expired bool ) {
2019-11-01 23:19:07 +01:00
ip , expired = nil , false
2019-10-21 18:36:47 +02:00
xTransport . cachedIPs . RLock ( )
2019-10-20 17:09:14 +02:00
item , ok := xTransport . cachedIPs . cache [ host ]
2019-10-21 18:36:47 +02:00
xTransport . cachedIPs . RUnlock ( )
2019-10-20 17:09:14 +02:00
if ! ok {
2019-11-01 22:55:06 +01:00
return
2019-10-20 17:09:14 +02:00
}
2019-11-01 22:55:06 +01:00
ip = item . ip
2019-10-21 18:40:47 +02:00
expiration := item . expiration
2019-11-01 22:55:06 +01:00
if expiration != nil && time . Until ( * expiration ) < 0 {
expired = true
2019-10-20 17:09:14 +02:00
}
2019-11-01 22:55:06 +01:00
return
2018-03-21 09:32:35 +01:00
}
2018-02-05 19:03:04 +01:00
func ( xTransport * XTransport ) rebuildTransport ( ) {
dlog . Debug ( "Rebuilding transport" )
if xTransport . transport != nil {
( * xTransport . transport ) . CloseIdleConnections ( )
}
timeout := xTransport . timeout
2018-01-30 15:51:07 +01:00
transport := & http . Transport {
DisableKeepAlives : false ,
DisableCompression : true ,
MaxIdleConns : 1 ,
2018-04-02 01:49:09 +02:00
IdleConnTimeout : xTransport . keepAlive ,
2018-01-30 15:51:07 +01:00
ResponseHeaderTimeout : timeout ,
ExpectContinueTimeout : timeout ,
MaxResponseHeaderBytes : 4096 ,
DialContext : func ( ctx context . Context , network , addrStr string ) ( net . Conn , error ) {
2018-04-14 15:03:21 +02:00
host , port := ExtractHostAndPort ( addrStr , stamps . DefaultPort )
2018-01-30 15:51:07 +01:00
ipOnly := host
2019-12-09 17:03:16 +01:00
// resolveAndUpdateCache() is always called in `Fetch()` before the `Dial()`
2019-11-01 23:19:07 +01:00
// method is used, so that a cached entry must be present at this point.
2019-11-01 22:55:06 +01:00
cachedIP , _ := xTransport . loadCachedIP ( host )
2019-11-02 01:20:28 +01:00
if cachedIP != nil {
2019-10-31 16:38:43 +01:00
if ipv4 := cachedIP . To4 ( ) ; ipv4 != nil {
ipOnly = ipv4 . String ( )
} else {
ipOnly = "[" + cachedIP . String ( ) + "]"
}
2018-01-30 15:51:07 +01:00
} else {
dlog . Debugf ( "[%s] IP address was not cached" , host )
}
2018-03-28 12:38:17 +02:00
addrStr = ipOnly + ":" + strconv . Itoa ( port )
2018-06-06 15:54:51 +02:00
if xTransport . proxyDialer == nil {
dialer := & net . Dialer { Timeout : timeout , KeepAlive : timeout , DualStack : true }
return dialer . DialContext ( ctx , network , addrStr )
}
2019-06-26 19:51:57 +02:00
return ( * xTransport . proxyDialer ) . Dial ( network , addrStr )
2018-01-30 15:51:07 +01:00
} ,
}
2018-11-15 18:47:33 +01:00
if xTransport . httpProxyFunction != nil {
transport . Proxy = xTransport . httpProxyFunction
}
2021-06-03 12:48:33 +02:00
2020-03-09 22:11:53 +01:00
clientCreds := xTransport . tlsClientCreds
2021-06-03 20:59:05 +02:00
tlsClientConfig := tls . Config { }
certPool , certPoolErr := x509 . SystemCertPool ( )
2021-06-03 12:48:33 +02:00
if clientCreds . rootCA != "" {
2021-06-03 20:59:05 +02:00
if certPool == nil {
dlog . Fatalf ( "Additional CAs not supported on this platform: %v" , certPoolErr )
}
2021-06-03 12:48:33 +02:00
additionalCaCert , err := ioutil . ReadFile ( clientCreds . rootCA )
if err != nil {
dlog . Fatal ( err )
}
certPool . AppendCertsFromPEM ( additionalCaCert )
}
2021-06-03 20:59:05 +02:00
if certPool != nil {
// Some operating systems don't include Let's Encrypt ISRG Root X1 certificate yet
var letsEncryptX1Cert = [ ] byte ( ` -- -- - BEGIN CERTIFICATE -- -- -
2021-06-03 12:48:33 +02:00
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygch77ct984kIxuPOZXoHj3dcKi / vVqbvYATyjb3miGbESTtrFj / RQSa78f0uoxmyF + 0 TM8ukj13Xnfs7j / EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4 + 3 mX6UA5 / TR5d8mUgjU + g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8 + o + u3dpjq + sWT8KOEUt + zwvo / 7 V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm / ELNKjD + Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x + UCB5iPNgiV5 + I3lg02dZ77DnKxHZu8A / lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1 / ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFuhjuefXKnEgV4We0 + UXgVCwOPjdAvBbI + e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY / rOPNk3sgrDQoo //fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZLubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-- -- - END CERTIFICATE -- -- - ` )
2021-06-03 20:59:05 +02:00
certPool . AppendCertsFromPEM ( letsEncryptX1Cert )
tlsClientConfig . RootCAs = certPool
}
2021-06-03 12:48:33 +02:00
if clientCreds . clientCert != "" {
2020-03-09 22:11:53 +01:00
cert , err := tls . LoadX509KeyPair ( clientCreds . clientCert , clientCreds . clientKey )
if err != nil {
2020-03-24 14:31:43 +01:00
dlog . Fatalf ( "Unable to use certificate [%v] (key: [%v]): %v" , clientCreds . clientCert , clientCreds . clientKey , err )
2018-04-07 22:23:29 +02:00
}
2020-03-09 22:11:53 +01:00
tlsClientConfig . Certificates = [ ] tls . Certificate { cert }
}
2021-06-03 12:48:33 +02:00
2020-03-09 22:11:53 +01:00
if xTransport . tlsDisableSessionTickets || xTransport . tlsCipherSuite != nil {
tlsClientConfig . SessionTicketsDisabled = xTransport . tlsDisableSessionTickets
2018-04-10 00:36:55 +02:00
if ! xTransport . tlsDisableSessionTickets {
tlsClientConfig . ClientSessionCache = tls . NewLRUClientSessionCache ( 10 )
}
2018-04-07 22:23:29 +02:00
if xTransport . tlsCipherSuite != nil {
tlsClientConfig . PreferServerCipherSuites = false
tlsClientConfig . CipherSuites = xTransport . tlsCipherSuite
}
}
2020-03-09 22:11:53 +01:00
transport . TLSClientConfig = & tlsClientConfig
2018-04-07 22:23:29 +02:00
http2 . ConfigureTransport ( transport )
2018-01-30 15:51:07 +01:00
xTransport . transport = transport
}
2019-10-20 17:09:14 +02:00
func ( xTransport * XTransport ) resolveUsingSystem ( host string ) ( ip net . IP , ttl time . Duration , err error ) {
2019-11-17 20:30:00 +01:00
ttl = SystemResolverIPTTL
2019-10-20 17:09:14 +02:00
var foundIPs [ ] string
foundIPs , err = net . LookupHost ( host )
2019-04-14 13:46:07 +02:00
if err != nil {
2019-10-20 17:09:14 +02:00
return
2019-04-14 13:46:07 +02:00
}
2019-10-20 17:09:14 +02:00
ips := make ( [ ] net . IP , 0 )
2019-04-14 13:46:07 +02:00
for _ , ip := range foundIPs {
2019-10-20 17:09:14 +02:00
if foundIP := net . ParseIP ( ip ) ; foundIP != nil {
if xTransport . useIPv4 {
if ipv4 := foundIP . To4 ( ) ; ipv4 != nil {
ips = append ( ips , foundIP )
}
2019-04-14 13:46:07 +02:00
}
2019-10-20 17:09:14 +02:00
if xTransport . useIPv6 {
if ipv6 := foundIP . To16 ( ) ; ipv6 != nil {
ips = append ( ips , foundIP )
}
2019-04-14 13:46:07 +02:00
}
}
}
2019-10-20 17:09:14 +02:00
if len ( ips ) > 0 {
ip = ips [ rand . Intn ( len ( ips ) ) ]
}
return
2019-04-14 13:46:07 +02:00
}
2019-10-20 17:09:14 +02:00
func ( xTransport * XTransport ) resolveUsingResolver ( proto , host string , resolver string ) ( ip net . IP , ttl time . Duration , err error ) {
dnsClient := dns . Client { Net : proto }
2018-03-21 10:03:05 +01:00
if xTransport . useIPv4 {
2019-12-11 14:02:56 +01:00
msg := dns . Msg { }
2018-03-21 10:03:05 +01:00
msg . SetQuestion ( dns . Fqdn ( host ) , dns . TypeA )
2019-10-16 11:47:43 +02:00
msg . SetEdns0 ( uint16 ( MaxDNSPacketSize ) , true )
2018-03-21 10:03:05 +01:00
var in * dns . Msg
2019-12-11 14:02:56 +01:00
if in , _ , err = dnsClient . Exchange ( & msg , resolver ) ; err == nil {
2019-10-20 17:09:14 +02:00
answers := make ( [ ] dns . RR , 0 )
2018-03-21 10:03:05 +01:00
for _ , answer := range in . Answer {
if answer . Header ( ) . Rrtype == dns . TypeA {
2019-10-20 17:09:14 +02:00
answers = append ( answers , answer )
2018-03-21 10:03:05 +01:00
}
}
2019-10-20 17:09:14 +02:00
if len ( answers ) > 0 {
answer := answers [ rand . Intn ( len ( answers ) ) ]
ip = answer . ( * dns . A ) . A
ttl = time . Duration ( answer . Header ( ) . Ttl ) * time . Second
return
}
2018-03-21 10:03:05 +01:00
}
}
2019-10-03 18:43:27 +02:00
if xTransport . useIPv6 {
2019-12-11 14:02:56 +01:00
msg := dns . Msg { }
2018-03-21 10:03:05 +01:00
msg . SetQuestion ( dns . Fqdn ( host ) , dns . TypeAAAA )
2019-10-16 11:47:43 +02:00
msg . SetEdns0 ( uint16 ( MaxDNSPacketSize ) , true )
2018-03-21 10:03:05 +01:00
var in * dns . Msg
2019-12-11 14:02:56 +01:00
if in , _ , err = dnsClient . Exchange ( & msg , resolver ) ; err == nil {
2019-10-20 17:09:14 +02:00
answers := make ( [ ] dns . RR , 0 )
2018-03-21 10:03:05 +01:00
for _ , answer := range in . Answer {
if answer . Header ( ) . Rrtype == dns . TypeAAAA {
2019-10-20 17:09:14 +02:00
answers = append ( answers , answer )
2018-03-21 10:03:05 +01:00
}
}
2019-10-20 17:09:14 +02:00
if len ( answers ) > 0 {
answer := answers [ rand . Intn ( len ( answers ) ) ]
ip = answer . ( * dns . AAAA ) . AAAA
ttl = time . Duration ( answer . Header ( ) . Ttl ) * time . Second
return
}
}
}
return
}
2020-01-15 19:58:14 +01:00
func ( xTransport * XTransport ) resolveUsingResolvers ( proto , host string , resolvers [ ] string ) ( ip net . IP , ttl time . Duration , err error ) {
for i , resolver := range resolvers {
ip , ttl , err = xTransport . resolveUsingResolver ( proto , host , resolver )
if err == nil {
if i > 0 {
2021-02-20 18:50:42 +01:00
dlog . Infof ( "Resolution succeeded with bootstrap resolver %s[%s]" , proto , resolver )
2020-01-15 19:58:14 +01:00
resolvers [ 0 ] , resolvers [ i ] = resolvers [ i ] , resolvers [ 0 ]
}
break
}
2021-02-20 18:50:42 +01:00
dlog . Infof ( "Unable to resolve [%s] using bootstrap resolver %s[%s]: %v" , host , proto , resolver , err )
2020-01-15 19:58:14 +01:00
}
return
}
2019-12-09 17:03:16 +01:00
// If a name is not present in the cache, resolve the name and update the cache
func ( xTransport * XTransport ) resolveAndUpdateCache ( host string ) error {
2019-10-20 17:09:14 +02:00
if xTransport . proxyDialer != nil || xTransport . httpProxyFunction != nil {
2019-12-09 16:59:02 +01:00
return nil
2019-10-20 17:09:14 +02:00
}
if ParseIP ( host ) != nil {
2019-12-09 16:59:02 +01:00
return nil
2019-10-20 17:09:14 +02:00
}
2019-11-01 22:55:06 +01:00
cachedIP , expired := xTransport . loadCachedIP ( host )
2019-11-02 01:50:35 +01:00
if cachedIP != nil && ! expired {
2019-12-09 16:59:02 +01:00
return nil
2019-10-20 17:09:14 +02:00
}
var foundIP net . IP
var ttl time . Duration
2019-12-09 16:59:02 +01:00
var err error
2019-10-20 17:09:14 +02:00
if ! xTransport . ignoreSystemDNS {
foundIP , ttl , err = xTransport . resolveUsingSystem ( host )
}
if xTransport . ignoreSystemDNS || err != nil {
protos := [ ] string { "udp" , "tcp" }
if xTransport . mainProto == "tcp" {
protos = [ ] string { "tcp" , "udp" }
}
for _ , proto := range protos {
if err != nil {
2021-02-20 18:50:42 +01:00
dlog . Noticef ( "System DNS configuration not usable yet, exceptionally resolving [%s] using bootstrap resolvers over %s" , host , proto )
2019-10-20 17:09:14 +02:00
} else {
2021-02-20 18:50:42 +01:00
dlog . Debugf ( "Resolving [%s] using bootstrap resolvers over %s" , host , proto )
2019-10-20 17:09:14 +02:00
}
2021-02-20 18:50:42 +01:00
foundIP , ttl , err = xTransport . resolveUsingResolvers ( proto , host , xTransport . bootstrapResolvers )
2019-10-20 17:09:14 +02:00
if err == nil {
break
}
2018-03-21 10:03:05 +01:00
}
}
2019-11-17 22:00:08 +01:00
if err != nil && xTransport . ignoreSystemDNS {
2021-02-20 18:50:42 +01:00
dlog . Noticef ( "Bootstrap resolvers didn't respond - Trying with the system resolver as a last resort" )
2019-11-17 22:00:08 +01:00
foundIP , ttl , err = xTransport . resolveUsingSystem ( host )
}
2019-11-17 20:00:34 +01:00
if ttl < MinResolverIPTTL {
ttl = MinResolverIPTTL
2019-11-02 02:01:03 +01:00
}
2019-10-20 17:09:14 +02:00
if err != nil {
2019-11-01 22:55:06 +01:00
if cachedIP != nil {
2019-12-09 17:08:59 +01:00
dlog . Noticef ( "Using stale [%v] cached address for a grace period" , host )
2019-11-01 22:55:06 +01:00
foundIP = cachedIP
2019-11-01 23:19:07 +01:00
ttl = ExpiredCachedIPGraceTTL
2019-11-01 22:55:06 +01:00
} else {
2019-12-09 16:59:02 +01:00
return err
2019-11-01 22:55:06 +01:00
}
2019-10-20 17:09:14 +02:00
}
xTransport . saveCachedIP ( host , foundIP , ttl )
2019-11-02 02:01:03 +01:00
dlog . Debugf ( "[%s] IP address [%s] added to the cache, valid for %v" , host , foundIP , ttl )
2019-12-09 16:59:02 +01:00
return nil
2018-03-21 10:03:05 +01:00
}
2021-03-30 11:03:25 +02:00
func ( xTransport * XTransport ) Fetch ( method string , url * url . URL , accept string , contentType string , body * [ ] byte , timeout time . Duration ) ( [ ] byte , int , * tls . ConnectionState , time . Duration , error ) {
2018-01-30 15:51:07 +01:00
if timeout <= 0 {
timeout = xTransport . timeout
}
client := http . Client { Transport : xTransport . transport , Timeout : timeout }
header := map [ string ] [ ] string { "User-Agent" : { "dnscrypt-proxy" } }
if len ( accept ) > 0 {
header [ "Accept" ] = [ ] string { accept }
}
if len ( contentType ) > 0 {
header [ "Content-Type" ] = [ ] string { contentType }
}
2020-07-09 21:42:19 +02:00
header [ "Cache-Control" ] = [ ] string { "max-stale" }
2018-11-22 18:09:27 +01:00
if body != nil {
h := sha512 . Sum512 ( * body )
qs := url . Query ( )
qs . Add ( "body_hash" , hex . EncodeToString ( h [ : 32 ] ) )
url2 := * url
url2 . RawQuery = qs . Encode ( )
url = & url2
}
2019-10-16 12:17:57 +02:00
host , _ := ExtractHostAndPort ( url . Host , 0 )
if xTransport . proxyDialer == nil && strings . HasSuffix ( host , ".onion" ) {
2021-03-30 11:03:25 +02:00
return nil , 0 , nil , 0 , errors . New ( "Onion service is not reachable without Tor" )
2019-10-16 12:17:57 +02:00
}
2019-12-09 17:03:16 +01:00
if err := xTransport . resolveAndUpdateCache ( host ) ; err != nil {
2021-02-20 18:50:42 +01:00
dlog . Errorf ( "Unable to resolve [%v] - Make sure that the system resolver works, or that `bootstrap_resolvers` has been set to resolvers that can be reached" , host )
2021-03-30 11:03:25 +02:00
return nil , 0 , nil , 0 , err
2019-10-20 17:09:14 +02:00
}
2018-01-30 15:51:07 +01:00
req := & http . Request {
Method : method ,
URL : url ,
Header : header ,
Close : false ,
}
if body != nil {
2018-11-22 17:23:22 +01:00
req . ContentLength = int64 ( len ( * body ) )
2019-10-16 12:20:40 +02:00
req . Body = ioutil . NopCloser ( bytes . NewReader ( * body ) )
2018-01-30 15:51:07 +01:00
}
2018-01-30 17:37:35 +01:00
start := time . Now ( )
resp , err := client . Do ( req )
rtt := time . Since ( start )
2018-01-30 15:51:07 +01:00
if err == nil {
if resp == nil {
err = errors . New ( "Webserver returned an error" )
} else if resp . StatusCode < 200 || resp . StatusCode > 299 {
2019-10-16 11:49:18 +02:00
err = errors . New ( resp . Status )
2018-01-30 15:51:07 +01:00
}
2018-02-05 19:03:04 +01:00
} else {
( * xTransport . transport ) . CloseIdleConnections ( )
2018-01-30 15:51:07 +01:00
}
2018-01-30 17:37:35 +01:00
if err != nil {
dlog . Debugf ( "[%s]: [%s]" , req . URL , err )
2018-04-11 11:42:30 +02:00
if xTransport . tlsCipherSuite != nil && strings . Contains ( err . Error ( ) , "handshake failure" ) {
dlog . Warnf ( "TLS handshake failure - Try changing or deleting the tls_cipher_suite value in the configuration file" )
xTransport . tlsCipherSuite = nil
xTransport . rebuildTransport ( )
}
2021-03-30 11:03:25 +02:00
return nil , 0 , nil , 0 , err
2018-01-30 17:37:35 +01:00
}
2020-02-21 22:33:34 +01:00
tls := resp . TLS
bin , err := ioutil . ReadAll ( io . LimitReader ( resp . Body , MaxHTTPBodyLength ) )
if err != nil {
2021-03-30 11:03:25 +02:00
return nil , resp . StatusCode , tls , 0 , err
2020-02-21 22:33:34 +01:00
}
resp . Body . Close ( )
2021-03-30 11:03:25 +02:00
return bin , resp . StatusCode , tls , rtt , err
2018-01-30 15:51:07 +01:00
}
2021-03-30 11:03:25 +02:00
func ( xTransport * XTransport ) Get ( url * url . URL , accept string , timeout time . Duration ) ( [ ] byte , int , * tls . ConnectionState , time . Duration , error ) {
2019-12-22 17:34:16 +01:00
return xTransport . Fetch ( "GET" , url , accept , "" , nil , timeout )
2018-01-30 15:51:07 +01:00
}
2021-03-30 11:03:25 +02:00
func ( xTransport * XTransport ) Post ( url * url . URL , accept string , contentType string , body * [ ] byte , timeout time . Duration ) ( [ ] byte , int , * tls . ConnectionState , time . Duration , error ) {
2019-12-22 17:34:16 +01:00
return xTransport . Fetch ( "POST" , url , accept , contentType , body , timeout )
2018-01-30 15:51:07 +01:00
}
2018-02-27 09:30:09 +01:00
2021-03-30 11:03:25 +02:00
func ( xTransport * XTransport ) DoHQuery ( useGet bool , url * url . URL , body [ ] byte , timeout time . Duration ) ( [ ] byte , int , * tls . ConnectionState , time . Duration , error ) {
2018-05-19 02:39:32 +02:00
dataType := "application/dns-message"
2018-02-05 11:30:10 +01:00
if useGet {
qs := url . Query ( )
2019-12-23 11:37:45 +01:00
encBody := base64 . RawURLEncoding . EncodeToString ( body )
2018-02-05 11:36:15 +01:00
qs . Add ( "dns" , encBody )
2018-02-05 11:30:10 +01:00
url2 := * url
url2 . RawQuery = qs . Encode ( )
return xTransport . Get ( & url2 , dataType , timeout )
}
2019-12-23 11:37:45 +01:00
return xTransport . Post ( url , dataType , dataType , & body , timeout )
2018-02-05 11:30:10 +01:00
}
2021-03-30 11:03:25 +02:00
func ( xTransport * XTransport ) ObliviousDoHQuery ( url * url . URL , body [ ] byte , timeout time . Duration ) ( [ ] byte , int , * tls . ConnectionState , time . Duration , error ) {
dataType := "application/oblivious-dns-message"
return xTransport . Post ( url , dataType , dataType , & body , timeout )
}