Handle TCP, padding, etc.

This commit is contained in:
Frank Denis 2018-01-09 16:40:37 +01:00
parent 6a39b0afdb
commit 553f6afb00
4 changed files with 137 additions and 29 deletions

View File

@ -18,7 +18,8 @@ var (
ServerMagic = [8]byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38}
MinDNSPacketSize = 12
MaxDNSPacketSize = 4096
InitialMinQuestionSize = 128
MaxDNSUDPPacketSize = 1252
InitialMinQuestionSize = 256
TimeoutMin = 1 * time.Second
TimeoutMax = 5 * time.Second
)
@ -36,3 +37,17 @@ type ServerInfo struct {
func HasTCFlag(packet []byte) bool {
return packet[2]&2 == 2
}
func Min(a, b int) int {
if a < b {
return a
}
return b
}
func Max(a, b int) int {
if a > b {
return a
}
return b
}

View File

@ -35,13 +35,27 @@ func unpad(packet []byte) ([]byte, error) {
}
}
}
func (proxy *Proxy) Crypt(serverInfo *ServerInfo, packet []byte) (encrypted []byte, clientNonce []byte) {
func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string) (encrypted []byte, clientNonce []byte, err error) {
nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize)
rand.Read(clientNonce)
copy(nonce, clientNonce)
minQuestionSize := len(packet)
if proto == "udp" {
minQuestionSize = proxy.questionSizeEstimator.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 {
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, proxy.minQuestionSize), serverInfo.SharedKey[:])
encrypted = xsecretbox.Seal(encrypted, nonce, pad(packet, paddedLength), serverInfo.SharedKey[:])
return
}

View File

@ -2,6 +2,7 @@ package main
import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"fmt"
"log"
@ -14,71 +15,113 @@ import (
)
type Proxy struct {
proxyPublicKey [32]byte
proxySecretKey [32]byte
minQuestionSize int
serversInfo []ServerInfo
proxyPublicKey [32]byte
proxySecretKey [32]byte
questionSizeEstimator QuestionSizeEstimator
serversInfo []ServerInfo
timeout time.Duration
}
func main() {
log.SetFlags(0)
_ = NewProxy("127.0.0.1:5399", "212.47.228.136:443", "E801:B84E:A606:BFB0:BAC0:CE43:445B:B15E:BA64:B02F:A3C4:AA31:AE10:636A:0790:324D", "2.dnscrypt-cert.fr.dnscrypt.org")
NewProxy("127.0.0.1:5399", "212.47.228.136:443", "E801:B84E:A606:BFB0:BAC0:CE43:445B:B15E:BA64:B02F:A3C4:AA31:AE10:636A:0790:324D", "2.dnscrypt-cert.fr.dnscrypt.org")
}
func NewProxy(listenAddrStr string, serverAddrStr string, serverPkStr string, providerName string) Proxy {
proxy := Proxy{minQuestionSize: InitialMinQuestionSize}
func NewProxy(listenAddrStr string, serverAddrStr string, serverPkStr string, providerName string) {
proxy := Proxy{questionSizeEstimator: NewQuestionSizeEstimator(), timeout: TimeoutMax}
if _, err := rand.Read(proxy.proxySecretKey[:]); err != nil {
log.Fatal(err)
}
curve25519.ScalarBaseMult(&proxy.proxyPublicKey, &proxy.proxySecretKey)
proxy.fetchServerInfo(serverAddrStr, serverPkStr, providerName)
proxy.udpListener(listenAddrStr)
return proxy
}
func (proxy *Proxy) adjustMinQuestionSize() {
if MaxDNSPacketSize-proxy.minQuestionSize < proxy.minQuestionSize {
proxy.minQuestionSize = MaxDNSPacketSize
} else {
proxy.minQuestionSize *= 2
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
if err != nil {
log.Fatal(err)
}
listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr)
if err != nil {
log.Fatal(err)
}
go func() {
proxy.udpListener(listenUDPAddr)
}()
go func() {
proxy.tcpListener(listenTCPAddr)
}()
for {
time.Sleep(30 * time.Minute)
// Refresh certificates
}
}
func (proxy *Proxy) udpListener(listenAddrStr string) error {
clientPc, err := net.ListenPacket("udp", listenAddrStr)
func (proxy *Proxy) udpListener(listenAddr *net.UDPAddr) error {
clientPc, err := net.ListenUDP("udp", listenAddr)
if err != nil {
return err
}
defer clientPc.Close()
fmt.Printf("Now listening to %v [UDP]\n", listenAddrStr)
fmt.Printf("Now listening to %v [UDP]\n", listenAddr)
for {
buffer := make([]byte, MaxDNSPacketSize)
buffer := make([]byte, MaxDNSPacketSize-1)
length, clientAddr, err := clientPc.ReadFrom(buffer)
if err != nil {
return err
}
packet := buffer[:length]
go func() {
proxy.processIncomingUDPQuery(&proxy.serversInfo[0], packet, clientAddr, clientPc)
proxy.processIncomingQuery(&proxy.serversInfo[0], packet, &clientAddr, clientPc)
}()
}
}
func (proxy *Proxy) processIncomingUDPQuery(serverInfo *ServerInfo, packet []byte, clientAddr net.Addr, clientPc net.PacketConn) {
func (proxy *Proxy) tcpListener(listenAddr *net.TCPAddr) error {
acceptPc, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
return err
}
defer acceptPc.Close()
fmt.Printf("Now listening to %v [TCP]\n", listenAddr)
for {
clientPc, err := acceptPc.Accept()
if err != nil {
continue
}
go func() {
defer clientPc.Close()
clientPc.SetDeadline(time.Now().Add(proxy.timeout))
buffer := make([]byte, 2+MaxDNSPacketSize-1)
length, err := clientPc.Read(buffer)
if err != nil {
return
}
innerLength := binary.BigEndian.Uint16(buffer[0:2])
if int(innerLength) > length-2 {
return
}
packet := buffer[2:length]
proxy.processIncomingQuery(&proxy.serversInfo[0], packet, nil, clientPc)
}()
}
}
func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, packet []byte, clientAddr *net.Addr, clientPc net.Conn) {
if len(packet) < MinDNSPacketSize {
return
}
encrypted, clientNonce := proxy.Crypt(serverInfo, packet)
encrypted, clientNonce, err := proxy.Encrypt(serverInfo, packet, "udp")
if err != nil {
return
}
pc, err := net.DialUDP("udp", nil, serverInfo.UDPAddr)
if err != nil {
return
}
defer pc.Close()
pc.SetDeadline(time.Now().Add(serverInfo.Timeout))
pc.Write(encrypted)
encrypted = make([]byte, MaxDNSPacketSize)
length, err := pc.Read(encrypted)
pc.Close()
if err != nil {
return
}
@ -87,9 +130,16 @@ func (proxy *Proxy) processIncomingUDPQuery(serverInfo *ServerInfo, packet []byt
if err != nil {
return
}
clientPc.WriteTo(packet, clientAddr)
if clientAddr != nil {
clientPc.(net.PacketConn).WriteTo(packet, *clientAddr)
} else {
packet = append(append(packet, 0), 0)
copy(packet[2:], packet[:len(packet)-2])
binary.BigEndian.PutUint16(packet[0:2], uint16(len(packet)-2))
clientPc.Write(packet)
}
if HasTCFlag(packet) {
proxy.adjustMinQuestionSize()
proxy.questionSizeEstimator.blindAdjust()
}
}

29
estimators.go Normal file
View File

@ -0,0 +1,29 @@
package main
import "sync"
type QuestionSizeEstimator struct {
sync.RWMutex
minQuestionSize int
}
func NewQuestionSizeEstimator() QuestionSizeEstimator {
return QuestionSizeEstimator{minQuestionSize: InitialMinQuestionSize}
}
func (questionSizeEstimator *QuestionSizeEstimator) MinQuestionSize() int {
questionSizeEstimator.RLock()
minQuestionSize := questionSizeEstimator.minQuestionSize
questionSizeEstimator.RUnlock()
return minQuestionSize
}
func (questionSizeEstimator *QuestionSizeEstimator) blindAdjust() {
questionSizeEstimator.Lock()
if MaxDNSPacketSize-questionSizeEstimator.minQuestionSize < questionSizeEstimator.minQuestionSize {
questionSizeEstimator.minQuestionSize = MaxDNSUDPPacketSize
} else {
questionSizeEstimator.minQuestionSize *= 2
}
questionSizeEstimator.Unlock()
}