diff --git a/dnscrypt-proxy/common.go b/dnscrypt-proxy/common.go index 8658a945..97a95f90 100644 --- a/dnscrypt-proxy/common.go +++ b/dnscrypt-proxy/common.go @@ -27,10 +27,6 @@ var ( CertRefreshDelay = 30 * time.Minute ) -func HasTCFlag(packet []byte) bool { - return packet[2]&2 == 2 -} - func PrefixWithSize(packet []byte) ([]byte, error) { packet_len := len(packet) if packet_len > 0xffff { diff --git a/dnscrypt-proxy/crypto.go b/dnscrypt-proxy/crypto.go index 7d761404..92688e87 100644 --- a/dnscrypt-proxy/crypto.go +++ b/dnscrypt-proxy/crypto.go @@ -9,9 +9,10 @@ import ( ) const ( - NonceSize = xsecretbox.NonceSize - HalfNonceSize = xsecretbox.NonceSize / 2 - TagSize = xsecretbox.TagSize + NonceSize = xsecretbox.NonceSize + HalfNonceSize = xsecretbox.NonceSize / 2 + TagSize = xsecretbox.TagSize + ResponseOverhead = len(ServerMagic) + NonceSize + TagSize ) func pad(packet []byte, minSize int) []byte { @@ -40,22 +41,22 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string) nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize) rand.Read(clientNonce) copy(nonce, clientNonce) - minQuestionSize := len(packet) + minQuestionSize := ResponseOverhead + len(packet) if proto == "udp" { - minQuestionSize = proxy.questionSizeEstimator.MinQuestionSize() + minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize) } else { var xpad [1]byte rand.Read(xpad[:]) minQuestionSize += int(xpad[0]) } - paddedLength := Min((minQuestionSize+63)&^63, MaxDNSUDPPacketSize-1) - if paddedLength <= 0 || len(packet) >= paddedLength { + paddedLength := Min(MaxDNSUDPPacketSize, (Max(minQuestionSize, ResponseOverhead)+63) & ^63) + if ResponseOverhead+len(packet)+1 > paddedLength { err = errors.New("Question too large; cannot be padded") return } encrypted = append(serverInfo.MagicQuery[:], proxy.proxyPublicKey[:]...) encrypted = append(encrypted, nonce[:HalfNonceSize]...) - encrypted = xsecretbox.Seal(encrypted, nonce, pad(packet, paddedLength), serverInfo.SharedKey[:]) + encrypted = xsecretbox.Seal(encrypted, nonce, pad(packet, paddedLength-ResponseOverhead), serverInfo.SharedKey[:]) return } @@ -63,8 +64,9 @@ func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, encrypted []byte, nonce []by serverMagicLen := len(ServerMagic) responseHeaderLen := serverMagicLen + NonceSize if len(encrypted) < responseHeaderLen+TagSize+int(MinDNSPacketSize) || + len(encrypted) > responseHeaderLen+TagSize+int(MaxDNSPacketSize) || !bytes.Equal(encrypted[:serverMagicLen], ServerMagic[:]) { - return encrypted, errors.New("Short message") + return encrypted, errors.New("Invalid message size or prefix") } serverNonce := encrypted[serverMagicLen:responseHeaderLen] if !bytes.Equal(nonce[:HalfNonceSize], serverNonce[:HalfNonceSize]) { diff --git a/dnscrypt-proxy/dnsutils.go b/dnscrypt-proxy/dnsutils.go new file mode 100644 index 00000000..d1f5ae90 --- /dev/null +++ b/dnscrypt-proxy/dnsutils.go @@ -0,0 +1,23 @@ +package main + +import ( + "github.com/miekg/dns" +) + +func TruncatedResponse(packet []byte) ([]byte, error) { + srcMsg := new(dns.Msg) + if err := srcMsg.Unpack(packet); err != nil { + return nil, err + } + dstMsg := srcMsg + dstMsg.Response = true + dstMsg.Answer = make([]dns.RR, 0) + dstMsg.Ns = make([]dns.RR, 0) + dstMsg.Extra = make([]dns.RR, 0) + dstMsg.Truncated = true + return dstMsg.Pack() +} + +func HasTCFlag(packet []byte) bool { + return packet[2]&2 == 2 +} diff --git a/dnscrypt-proxy/estimators.go b/dnscrypt-proxy/estimators.go index bfb732ef..0c591168 100644 --- a/dnscrypt-proxy/estimators.go +++ b/dnscrypt-proxy/estimators.go @@ -20,7 +20,7 @@ func (questionSizeEstimator *QuestionSizeEstimator) MinQuestionSize() int { func (questionSizeEstimator *QuestionSizeEstimator) blindAdjust() { questionSizeEstimator.Lock() - if MaxDNSPacketSize-questionSizeEstimator.minQuestionSize < questionSizeEstimator.minQuestionSize { + if MaxDNSUDPPacketSize-questionSizeEstimator.minQuestionSize < questionSizeEstimator.minQuestionSize { questionSizeEstimator.minQuestionSize = MaxDNSUDPPacketSize } else { questionSizeEstimator.minQuestionSize *= 2 diff --git a/dnscrypt-proxy/main.go b/dnscrypt-proxy/main.go index 012e063d..ebc8ebc2 100644 --- a/dnscrypt-proxy/main.go +++ b/dnscrypt-proxy/main.go @@ -142,22 +142,28 @@ func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, serverProto str if err != nil { return } - var encryptedResponse []byte + var response []byte if serverProto == "udp" { - encryptedResponse, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce) + response, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce) } else { - encryptedResponse, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce) + response, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce) } if err != nil { return } if clientAddr != nil { - clientPc.(net.PacketConn).WriteTo(encryptedResponse, *clientAddr) - if HasTCFlag(encryptedResponse) { + if len(response) > MaxDNSUDPPacketSize { + response, err = TruncatedResponse(response) + if err != nil { + return + } + } + clientPc.(net.PacketConn).WriteTo(response, *clientAddr) + if HasTCFlag(response) { proxy.questionSizeEstimator.blindAdjust() } } else { - response, err := PrefixWithSize(encryptedResponse) + response, err = PrefixWithSize(response) if err != nil { return }