diff --git a/dnscrypt-proxy/config.go b/dnscrypt-proxy/config.go index 61d919df..eac1f579 100644 --- a/dnscrypt-proxy/config.go +++ b/dnscrypt-proxy/config.go @@ -83,6 +83,7 @@ type Config struct { RefusedCodeInResponses bool `toml:"refused_code_in_responses"` BlockedQueryResponse string `toml:"blocked_query_response"` QueryMeta []string `toml:"query_meta"` + AnonymizedDNS AnonymizedDNSConfig `toml:"anonymized_dns"` } func newConfig() Config { @@ -166,6 +167,15 @@ type BlockIPConfig struct { Format string `toml:"log_format"` } +type AnonymizedDNSRouteConfig struct { + ServerName string `toml:"server_name"` + RelayName string `toml:"via"` +} + +type AnonymizedDNSConfig struct { + Routes []AnonymizedDNSRouteConfig `toml:"routes"` +} + type ServerSummary struct { Name string `json:"name"` Proto string `json:"proto"` @@ -422,6 +432,15 @@ func ConfigLoad(proxy *Proxy, svcFlag *string) error { } proxy.allWeeklyRanges = allWeeklyRanges + if configRoutes := config.AnonymizedDNS.Routes; configRoutes != nil { + routes := make(map[string]string) + for _, configRoute := range configRoutes { + routes[configRoute.ServerName] = configRoute.RelayName + dlog.Debugf("Routing server [%s] via [%s]", configRoute.ServerName, configRoute.RelayName) + } + proxy.routes = &routes + } + if *listAll { config.ServerNames = nil config.DisabledServerNames = nil diff --git a/dnscrypt-proxy/proxy.go b/dnscrypt-proxy/proxy.go index af610b2f..aa3a9749 100644 --- a/dnscrypt-proxy/proxy.go +++ b/dnscrypt-proxy/proxy.go @@ -2,6 +2,7 @@ package main import ( crypto_rand "crypto/rand" + "encoding/binary" "io" "io/ioutil" "net" @@ -65,6 +66,7 @@ type Proxy struct { logMaxBackups int blockedQueryResponse string queryMeta []string + routes *map[string]string showCerts bool } @@ -268,12 +270,29 @@ func (proxy *Proxy) tcpListenerFromAddr(listenAddr *net.TCPAddr) error { return nil } +func (proxy *Proxy) prepareForRelay(ip net.IP, port int, encryptedQuery *[]byte) { + anonymizedDNSHeader := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00} + relayedQuery := append(anonymizedDNSHeader, ip.To16()...) + var tmp [2]byte + binary.BigEndian.PutUint16(tmp[0:2], uint16(port)) + relayedQuery = append(relayedQuery, tmp[:]...) + relayedQuery = append(relayedQuery, *encryptedQuery...) + *encryptedQuery = relayedQuery +} + func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) { - pc, err := net.DialUDP("udp", nil, serverInfo.UDPAddr) + upstreamAddr := serverInfo.UDPAddr + if serverInfo.RelayUDPAddr != nil { + upstreamAddr = serverInfo.RelayUDPAddr + } + pc, err := net.DialUDP("udp", nil, upstreamAddr) if err != nil { return nil, err } pc.SetDeadline(time.Now().Add(serverInfo.Timeout)) + if serverInfo.RelayUDPAddr != nil { + proxy.prepareForRelay(serverInfo.UDPAddr.IP, serverInfo.UDPAddr.Port, &encryptedQuery) + } pc.Write(encryptedQuery) encryptedResponse := make([]byte, MaxDNSPacketSize) length, err := pc.Read(encryptedResponse) @@ -286,18 +305,25 @@ func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32 } func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) { + upstreamAddr := serverInfo.TCPAddr + if serverInfo.RelayUDPAddr != nil { + upstreamAddr = serverInfo.RelayTCPAddr + } var err error var pc net.Conn proxyDialer := proxy.xTransport.proxyDialer if proxyDialer == nil { - pc, err = net.Dial("tcp", serverInfo.TCPAddr.String()) + pc, err = net.DialTCP("tcp", nil, upstreamAddr) } else { - pc, err = (*proxyDialer).Dial("tcp", serverInfo.TCPAddr.String()) + pc, err = (*proxyDialer).Dial("tcp", upstreamAddr.String()) } if err != nil { return nil, err } pc.SetDeadline(time.Now().Add(serverInfo.Timeout)) + if serverInfo.RelayTCPAddr != nil { + proxy.prepareForRelay(serverInfo.TCPAddr.IP, serverInfo.TCPAddr.Port, &encryptedQuery) + } encryptedQuery, err = PrefixWithSize(encryptedQuery) if err != nil { return nil, err diff --git a/dnscrypt-proxy/serversInfo.go b/dnscrypt-proxy/serversInfo.go index 3e4d914e..a9eb9073 100644 --- a/dnscrypt-proxy/serversInfo.go +++ b/dnscrypt-proxy/serversInfo.go @@ -43,6 +43,8 @@ type ServerInfo struct { HostName string UDPAddr *net.UDPAddr TCPAddr *net.TCPAddr + RelayUDPAddr *net.UDPAddr + RelayTCPAddr *net.TCPAddr lastActionTS time.Time rtt ewma.MovingAverage initialRtt int @@ -258,6 +260,40 @@ func (serversInfo *ServersInfo) fetchDNSCryptServerInfo(proxy *Proxy, name strin if err != nil { return ServerInfo{}, err } + var relayUDPAddr *net.UDPAddr + var relayTCPAddr *net.TCPAddr + routes := proxy.routes + if routes != nil { + if relayName, ok := (*routes)[name]; ok { + var relayCandidateStamp *stamps.ServerStamp + if stamp, err = stamps.NewServerStampFromString(relayName); err == nil { + relayCandidateStamp = &stamp + } else if _, err := net.ResolveUDPAddr("udp", relayName); err == nil { + relayCandidateStamp = &stamps.ServerStamp{ + ServerAddrStr: relayName, + Proto: stamps.StampProtoTypeDNSCrypt, + } + } else { + for _, registeredServer := range proxy.registeredServers { + if registeredServer.name == relayName { + relayCandidateStamp = ®isteredServer.stamp + } + } + } + if relayCandidateStamp != nil && relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCrypt { + relayUDPAddr, err = net.ResolveUDPAddr("udp", relayCandidateStamp.ServerAddrStr) + if err != nil { + return ServerInfo{}, err + } + relayTCPAddr, err = net.ResolveTCPAddr("tcp", relayCandidateStamp.ServerAddrStr) + if err != nil { + return ServerInfo{}, err + } + } else { + dlog.Errorf("Invalid relay [%v] for server [%v]", relayName, name) + } + } + } return ServerInfo{ Proto: stamps.StampProtoTypeDNSCrypt, MagicQuery: certInfo.MagicQuery, @@ -268,6 +304,8 @@ func (serversInfo *ServersInfo) fetchDNSCryptServerInfo(proxy *Proxy, name strin Timeout: proxy.timeout, UDPAddr: remoteUDPAddr, TCPAddr: remoteTCPAddr, + RelayUDPAddr: relayUDPAddr, + RelayTCPAddr: relayTCPAddr, initialRtt: rtt, }, nil }