Synthesize a truncated response if the response wouldn't fit the local MSS

This commit is contained in:
Frank Denis 2018-01-10 02:52:09 +01:00
parent ab9006e74c
commit d8f8d561c8
5 changed files with 47 additions and 20 deletions

View File

@ -27,10 +27,6 @@ var (
CertRefreshDelay = 30 * time.Minute CertRefreshDelay = 30 * time.Minute
) )
func HasTCFlag(packet []byte) bool {
return packet[2]&2 == 2
}
func PrefixWithSize(packet []byte) ([]byte, error) { func PrefixWithSize(packet []byte) ([]byte, error) {
packet_len := len(packet) packet_len := len(packet)
if packet_len > 0xffff { if packet_len > 0xffff {

View File

@ -9,9 +9,10 @@ import (
) )
const ( const (
NonceSize = xsecretbox.NonceSize NonceSize = xsecretbox.NonceSize
HalfNonceSize = xsecretbox.NonceSize / 2 HalfNonceSize = xsecretbox.NonceSize / 2
TagSize = xsecretbox.TagSize TagSize = xsecretbox.TagSize
ResponseOverhead = len(ServerMagic) + NonceSize + TagSize
) )
func pad(packet []byte, minSize int) []byte { 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) nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize)
rand.Read(clientNonce) rand.Read(clientNonce)
copy(nonce, clientNonce) copy(nonce, clientNonce)
minQuestionSize := len(packet) minQuestionSize := ResponseOverhead + len(packet)
if proto == "udp" { if proto == "udp" {
minQuestionSize = proxy.questionSizeEstimator.MinQuestionSize() minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize)
} else { } else {
var xpad [1]byte var xpad [1]byte
rand.Read(xpad[:]) rand.Read(xpad[:])
minQuestionSize += int(xpad[0]) minQuestionSize += int(xpad[0])
} }
paddedLength := Min((minQuestionSize+63)&^63, MaxDNSUDPPacketSize-1) paddedLength := Min(MaxDNSUDPPacketSize, (Max(minQuestionSize, ResponseOverhead)+63) & ^63)
if paddedLength <= 0 || len(packet) >= paddedLength { if ResponseOverhead+len(packet)+1 > paddedLength {
err = errors.New("Question too large; cannot be padded") err = errors.New("Question too large; cannot be padded")
return return
} }
encrypted = append(serverInfo.MagicQuery[:], proxy.proxyPublicKey[:]...) encrypted = append(serverInfo.MagicQuery[:], proxy.proxyPublicKey[:]...)
encrypted = append(encrypted, nonce[:HalfNonceSize]...) 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 return
} }
@ -63,8 +64,9 @@ func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, encrypted []byte, nonce []by
serverMagicLen := len(ServerMagic) serverMagicLen := len(ServerMagic)
responseHeaderLen := serverMagicLen + NonceSize responseHeaderLen := serverMagicLen + NonceSize
if len(encrypted) < responseHeaderLen+TagSize+int(MinDNSPacketSize) || if len(encrypted) < responseHeaderLen+TagSize+int(MinDNSPacketSize) ||
len(encrypted) > responseHeaderLen+TagSize+int(MaxDNSPacketSize) ||
!bytes.Equal(encrypted[:serverMagicLen], ServerMagic[:]) { !bytes.Equal(encrypted[:serverMagicLen], ServerMagic[:]) {
return encrypted, errors.New("Short message") return encrypted, errors.New("Invalid message size or prefix")
} }
serverNonce := encrypted[serverMagicLen:responseHeaderLen] serverNonce := encrypted[serverMagicLen:responseHeaderLen]
if !bytes.Equal(nonce[:HalfNonceSize], serverNonce[:HalfNonceSize]) { if !bytes.Equal(nonce[:HalfNonceSize], serverNonce[:HalfNonceSize]) {

View File

@ -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
}

View File

@ -20,7 +20,7 @@ func (questionSizeEstimator *QuestionSizeEstimator) MinQuestionSize() int {
func (questionSizeEstimator *QuestionSizeEstimator) blindAdjust() { func (questionSizeEstimator *QuestionSizeEstimator) blindAdjust() {
questionSizeEstimator.Lock() questionSizeEstimator.Lock()
if MaxDNSPacketSize-questionSizeEstimator.minQuestionSize < questionSizeEstimator.minQuestionSize { if MaxDNSUDPPacketSize-questionSizeEstimator.minQuestionSize < questionSizeEstimator.minQuestionSize {
questionSizeEstimator.minQuestionSize = MaxDNSUDPPacketSize questionSizeEstimator.minQuestionSize = MaxDNSUDPPacketSize
} else { } else {
questionSizeEstimator.minQuestionSize *= 2 questionSizeEstimator.minQuestionSize *= 2

View File

@ -142,22 +142,28 @@ func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, serverProto str
if err != nil { if err != nil {
return return
} }
var encryptedResponse []byte var response []byte
if serverProto == "udp" { if serverProto == "udp" {
encryptedResponse, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce) response, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce)
} else { } else {
encryptedResponse, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce) response, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce)
} }
if err != nil { if err != nil {
return return
} }
if clientAddr != nil { if clientAddr != nil {
clientPc.(net.PacketConn).WriteTo(encryptedResponse, *clientAddr) if len(response) > MaxDNSUDPPacketSize {
if HasTCFlag(encryptedResponse) { response, err = TruncatedResponse(response)
if err != nil {
return
}
}
clientPc.(net.PacketConn).WriteTo(response, *clientAddr)
if HasTCFlag(response) {
proxy.questionSizeEstimator.blindAdjust() proxy.questionSizeEstimator.blindAdjust()
} }
} else { } else {
response, err := PrefixWithSize(encryptedResponse) response, err = PrefixWithSize(response)
if err != nil { if err != nil {
return return
} }