Handle TCP, padding, etc.
This commit is contained in:
parent
6a39b0afdb
commit
553f6afb00
17
common.go
17
common.go
|
@ -18,7 +18,8 @@ var (
|
||||||
ServerMagic = [8]byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38}
|
ServerMagic = [8]byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38}
|
||||||
MinDNSPacketSize = 12
|
MinDNSPacketSize = 12
|
||||||
MaxDNSPacketSize = 4096
|
MaxDNSPacketSize = 4096
|
||||||
InitialMinQuestionSize = 128
|
MaxDNSUDPPacketSize = 1252
|
||||||
|
InitialMinQuestionSize = 256
|
||||||
TimeoutMin = 1 * time.Second
|
TimeoutMin = 1 * time.Second
|
||||||
TimeoutMax = 5 * time.Second
|
TimeoutMax = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
@ -36,3 +37,17 @@ type ServerInfo struct {
|
||||||
func HasTCFlag(packet []byte) bool {
|
func HasTCFlag(packet []byte) bool {
|
||||||
return packet[2]&2 == 2
|
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
|
||||||
|
}
|
||||||
|
|
18
crypto.go
18
crypto.go
|
@ -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)
|
nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize)
|
||||||
rand.Read(clientNonce)
|
rand.Read(clientNonce)
|
||||||
copy(nonce, 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(serverInfo.MagicQuery[:], proxy.proxyPublicKey[:]...)
|
||||||
encrypted = append(encrypted, nonce[:HalfNonceSize]...)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
@ -16,69 +17,111 @@ import (
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
proxyPublicKey [32]byte
|
proxyPublicKey [32]byte
|
||||||
proxySecretKey [32]byte
|
proxySecretKey [32]byte
|
||||||
minQuestionSize int
|
questionSizeEstimator QuestionSizeEstimator
|
||||||
serversInfo []ServerInfo
|
serversInfo []ServerInfo
|
||||||
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(0)
|
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 {
|
func NewProxy(listenAddrStr string, serverAddrStr string, serverPkStr string, providerName string) {
|
||||||
proxy := Proxy{minQuestionSize: InitialMinQuestionSize}
|
proxy := Proxy{questionSizeEstimator: NewQuestionSizeEstimator(), timeout: TimeoutMax}
|
||||||
if _, err := rand.Read(proxy.proxySecretKey[:]); err != nil {
|
if _, err := rand.Read(proxy.proxySecretKey[:]); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
curve25519.ScalarBaseMult(&proxy.proxyPublicKey, &proxy.proxySecretKey)
|
curve25519.ScalarBaseMult(&proxy.proxyPublicKey, &proxy.proxySecretKey)
|
||||||
proxy.fetchServerInfo(serverAddrStr, serverPkStr, providerName)
|
proxy.fetchServerInfo(serverAddrStr, serverPkStr, providerName)
|
||||||
proxy.udpListener(listenAddrStr)
|
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
|
||||||
return proxy
|
if err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
|
}
|
||||||
func (proxy *Proxy) adjustMinQuestionSize() {
|
listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr)
|
||||||
if MaxDNSPacketSize-proxy.minQuestionSize < proxy.minQuestionSize {
|
if err != nil {
|
||||||
proxy.minQuestionSize = MaxDNSPacketSize
|
log.Fatal(err)
|
||||||
} else {
|
}
|
||||||
proxy.minQuestionSize *= 2
|
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 {
|
func (proxy *Proxy) udpListener(listenAddr *net.UDPAddr) error {
|
||||||
clientPc, err := net.ListenPacket("udp", listenAddrStr)
|
clientPc, err := net.ListenUDP("udp", listenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer clientPc.Close()
|
defer clientPc.Close()
|
||||||
fmt.Printf("Now listening to %v [UDP]\n", listenAddrStr)
|
fmt.Printf("Now listening to %v [UDP]\n", listenAddr)
|
||||||
for {
|
for {
|
||||||
buffer := make([]byte, MaxDNSPacketSize)
|
buffer := make([]byte, MaxDNSPacketSize-1)
|
||||||
length, clientAddr, err := clientPc.ReadFrom(buffer)
|
length, clientAddr, err := clientPc.ReadFrom(buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
packet := buffer[:length]
|
packet := buffer[:length]
|
||||||
go func() {
|
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 {
|
if len(packet) < MinDNSPacketSize {
|
||||||
return
|
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)
|
pc, err := net.DialUDP("udp", nil, serverInfo.UDPAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer pc.Close()
|
|
||||||
pc.SetDeadline(time.Now().Add(serverInfo.Timeout))
|
pc.SetDeadline(time.Now().Add(serverInfo.Timeout))
|
||||||
pc.Write(encrypted)
|
pc.Write(encrypted)
|
||||||
|
|
||||||
encrypted = make([]byte, MaxDNSPacketSize)
|
encrypted = make([]byte, MaxDNSPacketSize)
|
||||||
length, err := pc.Read(encrypted)
|
length, err := pc.Read(encrypted)
|
||||||
|
pc.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -87,9 +130,16 @@ func (proxy *Proxy) processIncomingUDPQuery(serverInfo *ServerInfo, packet []byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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) {
|
if HasTCFlag(packet) {
|
||||||
proxy.adjustMinQuestionSize()
|
proxy.questionSizeEstimator.blindAdjust()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in New Issue