Move a few DNS things to dnsutils
This commit is contained in:
parent
a24b009667
commit
85e7dddc9b
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -45,7 +44,7 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
|
|||
if knownBugs.fragmentsBlocked {
|
||||
tryFragmentsSupport = false
|
||||
}
|
||||
in, rtt, fragmentsBlocked, err := dnsExchange(proxy, proto, &query, serverAddress, relay, serverName, tryFragmentsSupport)
|
||||
in, rtt, fragmentsBlocked, err := DNSExchange(proxy, proto, &query, serverAddress, relay, serverName, tryFragmentsSupport)
|
||||
if err != nil {
|
||||
dlog.Noticef("[%s] TIMEOUT", *serverName)
|
||||
return CertInfo{}, 0, fragmentsBlocked, err
|
||||
|
@ -62,7 +61,7 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
|
|||
} else {
|
||||
txt = strings.Join(t.Txt, "")
|
||||
}
|
||||
binCert := packTxtString(txt)
|
||||
binCert := PackTXTRR(txt)
|
||||
if len(binCert) < 124 {
|
||||
dlog.Warnf("[%v] Certificate too short", *serverName)
|
||||
continue
|
||||
|
@ -153,220 +152,3 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
|
|||
}
|
||||
return certInfo, int(rtt.Nanoseconds() / 1000000), fragmentsBlocked, nil
|
||||
}
|
||||
|
||||
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
|
||||
|
||||
func dddToByte(s []byte) byte {
|
||||
return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
|
||||
}
|
||||
|
||||
func packTxtString(s string) []byte {
|
||||
bs := make([]byte, len(s))
|
||||
msg := make([]byte, 0)
|
||||
copy(bs, s)
|
||||
for i := 0; i < len(bs); i++ {
|
||||
if bs[i] == '\\' {
|
||||
i++
|
||||
if i == len(bs) {
|
||||
break
|
||||
}
|
||||
if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
|
||||
msg = append(msg, dddToByte(bs[i:]))
|
||||
i += 2
|
||||
} else if bs[i] == 't' {
|
||||
msg = append(msg, '\t')
|
||||
} else if bs[i] == 'r' {
|
||||
msg = append(msg, '\r')
|
||||
} else if bs[i] == 'n' {
|
||||
msg = append(msg, '\n')
|
||||
} else {
|
||||
msg = append(msg, bs[i])
|
||||
}
|
||||
} else {
|
||||
msg = append(msg, bs[i])
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
type dnsExchangeResponse struct {
|
||||
response *dns.Msg
|
||||
rtt time.Duration
|
||||
priority int
|
||||
fragmentsBlocked bool
|
||||
err error
|
||||
}
|
||||
|
||||
func dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relay *DNSCryptRelay, serverName *string, tryFragmentsSupport bool) (*dns.Msg, time.Duration, bool, error) {
|
||||
for {
|
||||
cancelChannel := make(chan struct{})
|
||||
channel := make(chan dnsExchangeResponse)
|
||||
var err error
|
||||
options := 0
|
||||
|
||||
for tries := 0; tries < 3; tries++ {
|
||||
if tryFragmentsSupport {
|
||||
queryCopy := query.Copy()
|
||||
queryCopy.Id += uint16(options)
|
||||
go func(query *dns.Msg, delay time.Duration) {
|
||||
option := _dnsExchange(proxy, proto, query, serverAddress, relay, 1500)
|
||||
option.fragmentsBlocked = false
|
||||
option.priority = 0
|
||||
channel <- option
|
||||
time.Sleep(delay)
|
||||
select {
|
||||
case <-cancelChannel:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}(queryCopy, time.Duration(200*tries)*time.Millisecond)
|
||||
options++
|
||||
}
|
||||
queryCopy := query.Copy()
|
||||
queryCopy.Id += uint16(options)
|
||||
go func(query *dns.Msg, delay time.Duration) {
|
||||
option := _dnsExchange(proxy, proto, query, serverAddress, relay, 480)
|
||||
option.fragmentsBlocked = true
|
||||
option.priority = 1
|
||||
channel <- option
|
||||
time.Sleep(delay)
|
||||
select {
|
||||
case <-cancelChannel:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}(queryCopy, time.Duration(250*tries)*time.Millisecond)
|
||||
options++
|
||||
}
|
||||
var bestOption *dnsExchangeResponse
|
||||
for i := 0; i < options; i++ {
|
||||
if dnsExchangeResponse := <-channel; dnsExchangeResponse.err == nil {
|
||||
if bestOption == nil || dnsExchangeResponse.priority < bestOption.priority ||
|
||||
(dnsExchangeResponse.priority == bestOption.priority && dnsExchangeResponse.rtt < bestOption.rtt) {
|
||||
bestOption = &dnsExchangeResponse
|
||||
if bestOption.priority == 0 {
|
||||
close(cancelChannel)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = dnsExchangeResponse.err
|
||||
}
|
||||
}
|
||||
if bestOption != nil {
|
||||
if bestOption.fragmentsBlocked {
|
||||
dlog.Debugf("Certificate retrieval for [%v] succeeded but server is blocking fragments", *serverName)
|
||||
} else {
|
||||
dlog.Debugf("Certificate retrieval for [%v] succeeded", *serverName)
|
||||
}
|
||||
return bestOption.response, bestOption.rtt, bestOption.fragmentsBlocked, nil
|
||||
}
|
||||
|
||||
if relay == nil || !proxy.anonDirectCertFallback {
|
||||
if err == nil {
|
||||
err = errors.New("Unable to reach the server")
|
||||
}
|
||||
return nil, 0, false, err
|
||||
}
|
||||
dlog.Infof("Unable to get a certificate for [%v] via relay [%v], retrying over a direct connection", *serverName, relay.RelayUDPAddr.IP)
|
||||
relay = nil
|
||||
}
|
||||
}
|
||||
|
||||
func _dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relay *DNSCryptRelay, paddedLen int) dnsExchangeResponse {
|
||||
var packet []byte
|
||||
var rtt time.Duration
|
||||
|
||||
if proto == "udp" {
|
||||
qNameLen, padding := len(query.Question[0].Name), 0
|
||||
if qNameLen < paddedLen {
|
||||
padding = paddedLen - qNameLen
|
||||
}
|
||||
if padding > 0 {
|
||||
opt := new(dns.OPT)
|
||||
opt.Hdr.Name = "."
|
||||
ext := new(dns.EDNS0_PADDING)
|
||||
ext.Padding = make([]byte, padding)
|
||||
opt.Option = append(opt.Option, ext)
|
||||
query.Extra = []dns.RR{opt}
|
||||
}
|
||||
binQuery, err := query.Pack()
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", serverAddress)
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
upstreamAddr := udpAddr
|
||||
if relay != nil {
|
||||
proxy.prepareForRelay(udpAddr.IP, udpAddr.Port, &binQuery)
|
||||
upstreamAddr = relay.RelayUDPAddr
|
||||
}
|
||||
now := time.Now()
|
||||
pc, err := net.DialUDP("udp", nil, upstreamAddr)
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
defer pc.Close()
|
||||
if err := pc.SetDeadline(time.Now().Add(proxy.timeout)); err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
if _, err := pc.Write(binQuery); err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
packet = make([]byte, MaxDNSPacketSize)
|
||||
length, err := pc.Read(packet)
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
rtt = time.Since(now)
|
||||
packet = packet[:length]
|
||||
} else {
|
||||
binQuery, err := query.Pack()
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddress)
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
upstreamAddr := tcpAddr
|
||||
if relay != nil {
|
||||
proxy.prepareForRelay(tcpAddr.IP, tcpAddr.Port, &binQuery)
|
||||
upstreamAddr = relay.RelayTCPAddr
|
||||
}
|
||||
now := time.Now()
|
||||
var pc net.Conn
|
||||
proxyDialer := proxy.xTransport.proxyDialer
|
||||
if proxyDialer == nil {
|
||||
pc, err = net.DialTCP("tcp", nil, upstreamAddr)
|
||||
} else {
|
||||
pc, err = (*proxyDialer).Dial("tcp", tcpAddr.String())
|
||||
}
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
defer pc.Close()
|
||||
if err := pc.SetDeadline(time.Now().Add(proxy.timeout)); err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
binQuery, err = PrefixWithSize(binQuery)
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
if _, err := pc.Write(binQuery); err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
packet, err = ReadPrefixed(&pc)
|
||||
if err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
rtt = time.Since(now)
|
||||
}
|
||||
msg := dns.Msg{}
|
||||
if err := msg.Unpack(packet); err != nil {
|
||||
return dnsExchangeResponse{err: err}
|
||||
}
|
||||
return dnsExchangeResponse{response: &msg, rtt: rtt, err: nil}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/jedisct1/dlog"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -256,3 +257,220 @@ func removeEDNS0Options(msg *dns.Msg) bool {
|
|||
edns0.Option = []dns.EDNS0{}
|
||||
return true
|
||||
}
|
||||
|
||||
func isDigit(b byte) bool { return b >= '0' && b <= '9' }
|
||||
|
||||
func dddToByte(s []byte) byte {
|
||||
return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
|
||||
}
|
||||
|
||||
func PackTXTRR(s string) []byte {
|
||||
bs := make([]byte, len(s))
|
||||
msg := make([]byte, 0)
|
||||
copy(bs, s)
|
||||
for i := 0; i < len(bs); i++ {
|
||||
if bs[i] == '\\' {
|
||||
i++
|
||||
if i == len(bs) {
|
||||
break
|
||||
}
|
||||
if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
|
||||
msg = append(msg, dddToByte(bs[i:]))
|
||||
i += 2
|
||||
} else if bs[i] == 't' {
|
||||
msg = append(msg, '\t')
|
||||
} else if bs[i] == 'r' {
|
||||
msg = append(msg, '\r')
|
||||
} else if bs[i] == 'n' {
|
||||
msg = append(msg, '\n')
|
||||
} else {
|
||||
msg = append(msg, bs[i])
|
||||
}
|
||||
} else {
|
||||
msg = append(msg, bs[i])
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
type DNSExchangeResponse struct {
|
||||
response *dns.Msg
|
||||
rtt time.Duration
|
||||
priority int
|
||||
fragmentsBlocked bool
|
||||
err error
|
||||
}
|
||||
|
||||
func DNSExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relay *DNSCryptRelay, serverName *string, tryFragmentsSupport bool) (*dns.Msg, time.Duration, bool, error) {
|
||||
for {
|
||||
cancelChannel := make(chan struct{})
|
||||
channel := make(chan DNSExchangeResponse)
|
||||
var err error
|
||||
options := 0
|
||||
|
||||
for tries := 0; tries < 3; tries++ {
|
||||
if tryFragmentsSupport {
|
||||
queryCopy := query.Copy()
|
||||
queryCopy.Id += uint16(options)
|
||||
go func(query *dns.Msg, delay time.Duration) {
|
||||
option := _dnsExchange(proxy, proto, query, serverAddress, relay, 1500)
|
||||
option.fragmentsBlocked = false
|
||||
option.priority = 0
|
||||
channel <- option
|
||||
time.Sleep(delay)
|
||||
select {
|
||||
case <-cancelChannel:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}(queryCopy, time.Duration(200*tries)*time.Millisecond)
|
||||
options++
|
||||
}
|
||||
queryCopy := query.Copy()
|
||||
queryCopy.Id += uint16(options)
|
||||
go func(query *dns.Msg, delay time.Duration) {
|
||||
option := _dnsExchange(proxy, proto, query, serverAddress, relay, 480)
|
||||
option.fragmentsBlocked = true
|
||||
option.priority = 1
|
||||
channel <- option
|
||||
time.Sleep(delay)
|
||||
select {
|
||||
case <-cancelChannel:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}(queryCopy, time.Duration(250*tries)*time.Millisecond)
|
||||
options++
|
||||
}
|
||||
var bestOption *DNSExchangeResponse
|
||||
for i := 0; i < options; i++ {
|
||||
if dnsExchangeResponse := <-channel; dnsExchangeResponse.err == nil {
|
||||
if bestOption == nil || dnsExchangeResponse.priority < bestOption.priority ||
|
||||
(dnsExchangeResponse.priority == bestOption.priority && dnsExchangeResponse.rtt < bestOption.rtt) {
|
||||
bestOption = &dnsExchangeResponse
|
||||
if bestOption.priority == 0 {
|
||||
close(cancelChannel)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = dnsExchangeResponse.err
|
||||
}
|
||||
}
|
||||
if bestOption != nil {
|
||||
if bestOption.fragmentsBlocked {
|
||||
dlog.Debugf("[%v] public key retrieval succeeded but server is blocking fragments", *serverName)
|
||||
} else {
|
||||
dlog.Debugf("[%v] public key retrieval succeeded", *serverName)
|
||||
}
|
||||
return bestOption.response, bestOption.rtt, bestOption.fragmentsBlocked, nil
|
||||
}
|
||||
|
||||
if relay == nil || !proxy.anonDirectCertFallback {
|
||||
if err == nil {
|
||||
err = errors.New("Unable to reach the server")
|
||||
}
|
||||
return nil, 0, false, err
|
||||
}
|
||||
dlog.Infof("Unable to get the public key for [%v] via relay [%v], retrying over a direct connection", *serverName, relay.RelayUDPAddr.IP)
|
||||
relay = nil
|
||||
}
|
||||
}
|
||||
|
||||
func _dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress string, relay *DNSCryptRelay, paddedLen int) DNSExchangeResponse {
|
||||
var packet []byte
|
||||
var rtt time.Duration
|
||||
|
||||
if proto == "udp" {
|
||||
qNameLen, padding := len(query.Question[0].Name), 0
|
||||
if qNameLen < paddedLen {
|
||||
padding = paddedLen - qNameLen
|
||||
}
|
||||
if padding > 0 {
|
||||
opt := new(dns.OPT)
|
||||
opt.Hdr.Name = "."
|
||||
ext := new(dns.EDNS0_PADDING)
|
||||
ext.Padding = make([]byte, padding)
|
||||
opt.Option = append(opt.Option, ext)
|
||||
query.Extra = []dns.RR{opt}
|
||||
}
|
||||
binQuery, err := query.Pack()
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", serverAddress)
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
upstreamAddr := udpAddr
|
||||
if relay != nil {
|
||||
proxy.prepareForRelay(udpAddr.IP, udpAddr.Port, &binQuery)
|
||||
upstreamAddr = relay.RelayUDPAddr
|
||||
}
|
||||
now := time.Now()
|
||||
pc, err := net.DialUDP("udp", nil, upstreamAddr)
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
defer pc.Close()
|
||||
if err := pc.SetDeadline(time.Now().Add(proxy.timeout)); err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
if _, err := pc.Write(binQuery); err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
packet = make([]byte, MaxDNSPacketSize)
|
||||
length, err := pc.Read(packet)
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
rtt = time.Since(now)
|
||||
packet = packet[:length]
|
||||
} else {
|
||||
binQuery, err := query.Pack()
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp", serverAddress)
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
upstreamAddr := tcpAddr
|
||||
if relay != nil {
|
||||
proxy.prepareForRelay(tcpAddr.IP, tcpAddr.Port, &binQuery)
|
||||
upstreamAddr = relay.RelayTCPAddr
|
||||
}
|
||||
now := time.Now()
|
||||
var pc net.Conn
|
||||
proxyDialer := proxy.xTransport.proxyDialer
|
||||
if proxyDialer == nil {
|
||||
pc, err = net.DialTCP("tcp", nil, upstreamAddr)
|
||||
} else {
|
||||
pc, err = (*proxyDialer).Dial("tcp", tcpAddr.String())
|
||||
}
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
defer pc.Close()
|
||||
if err := pc.SetDeadline(time.Now().Add(proxy.timeout)); err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
binQuery, err = PrefixWithSize(binQuery)
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
if _, err := pc.Write(binQuery); err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
packet, err = ReadPrefixed(&pc)
|
||||
if err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
rtt = time.Since(now)
|
||||
}
|
||||
msg := dns.Msg{}
|
||||
if err := msg.Unpack(packet); err != nil {
|
||||
return DNSExchangeResponse{err: err}
|
||||
}
|
||||
return DNSExchangeResponse{response: &msg, rtt: rtt, err: nil}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue