dnscrypt-proxy/dnscrypt-proxy/proxy.go

467 lines
14 KiB
Go
Raw Normal View History

2018-01-29 23:47:04 +01:00
package main
import (
2018-02-04 11:31:54 +01:00
"io"
2018-01-29 23:47:04 +01:00
"io/ioutil"
"math/rand"
"net"
2018-07-05 18:05:24 +02:00
"os"
2018-01-29 23:47:04 +01:00
"sync/atomic"
"time"
"github.com/jedisct1/dlog"
clocksmith "github.com/jedisct1/go-clocksmith"
stamps "github.com/jedisct1/go-dnsstamps"
2018-01-29 23:47:04 +01:00
"golang.org/x/crypto/curve25519"
)
type Proxy struct {
userName string
child bool
2018-01-29 23:47:04 +01:00
proxyPublicKey [32]byte
proxySecretKey [32]byte
ephemeralKeys bool
2018-01-29 23:47:04 +01:00
questionSizeEstimator QuestionSizeEstimator
serversInfo ServersInfo
timeout time.Duration
certRefreshDelay time.Duration
certRefreshDelayAfterFailure time.Duration
certIgnoreTimestamp bool
mainProto string
listenAddresses []string
daemonize bool
registeredServers []RegisteredServer
pluginBlockIPv6 bool
cache bool
cacheSize int
cacheNegMinTTL uint32
cacheNegMaxTTL uint32
2018-01-29 23:47:04 +01:00
cacheMinTTL uint32
cacheMaxTTL uint32
queryLogFile string
queryLogFormat string
queryLogIgnoredQtypes []string
nxLogFile string
nxLogFormat string
blockNameFile string
2018-04-07 23:02:40 +02:00
whitelistNameFile string
2018-01-29 23:47:04 +01:00
blockNameLogFile string
2018-04-07 23:02:40 +02:00
whitelistNameLogFile string
2018-01-29 23:47:04 +01:00
blockNameFormat string
2018-04-07 23:02:40 +02:00
whitelistNameFormat string
2018-01-29 23:47:04 +01:00
blockIPFile string
blockIPLogFile string
blockIPFormat string
forwardFile string
cloakFile string
2018-01-29 23:47:04 +01:00
pluginsGlobals PluginsGlobals
urlsToPrefetch []URLToPrefetch
clientsCount uint32
maxClients uint32
xTransport *XTransport
allWeeklyRanges *map[string]WeeklyRanges
logMaxSize int
logMaxAge int
logMaxBackups int
refusedCodeInResponses bool
2018-01-29 23:47:04 +01:00
}
func (proxy *Proxy) StartProxy() {
proxy.questionSizeEstimator = NewQuestionSizeEstimator()
if _, err := rand.Read(proxy.proxySecretKey[:]); err != nil {
dlog.Fatal(err)
}
curve25519.ScalarBaseMult(&proxy.proxyPublicKey, &proxy.proxySecretKey)
for _, registeredServer := range proxy.registeredServers {
proxy.serversInfo.registerServer(proxy, registeredServer.name, registeredServer.stamp)
}
2018-01-29 23:47:04 +01:00
for _, listenAddrStr := range proxy.listenAddresses {
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
if err != nil {
dlog.Fatal(err)
}
listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr)
if err != nil {
dlog.Fatal(err)
}
// if 'userName' is not set, continue as before
if !(len(proxy.userName) > 0) {
if err := proxy.udpListenerFromAddr(listenUDPAddr); err != nil {
dlog.Fatal(err)
}
if err := proxy.tcpListenerFromAddr(listenTCPAddr); err != nil {
dlog.Fatal(err)
}
} else {
// if 'userName' is set and we are the parent process
if !proxy.child {
// parent
listenerUDP, err := net.ListenUDP("udp", listenUDPAddr)
if err != nil {
dlog.Fatal(err)
}
listenerTCP, err := net.ListenTCP("tcp", listenTCPAddr)
if err != nil {
dlog.Fatal(err)
}
fdUDP, err := listenerUDP.File() // On Windows, the File method of UDPConn is not implemented.
if err != nil {
dlog.Fatal(err)
}
fdTCP, err := listenerTCP.File() // On Windows, the File method of TCPListener is not implemented.
if err != nil {
dlog.Fatal(err)
}
defer listenerUDP.Close()
defer listenerTCP.Close()
FileDescriptors = append(FileDescriptors, fdUDP)
FileDescriptors = append(FileDescriptors, fdTCP)
// if 'userName' is set and we are the child process
} else {
// child
listenerUDP, err := net.FilePacketConn(os.NewFile(uintptr(3+FileDescriptorNum), "listenerUDP"))
if err != nil {
dlog.Fatal(err)
}
FileDescriptorNum++
listenerTCP, err := net.FileListener(os.NewFile(uintptr(3+FileDescriptorNum), "listenerTCP"))
if err != nil {
dlog.Fatal(err)
}
FileDescriptorNum++
dlog.Noticef("Now listening to %v [UDP]", listenUDPAddr)
go proxy.udpListener(listenerUDP.(*net.UDPConn))
dlog.Noticef("Now listening to %v [TCP]", listenAddrStr)
go proxy.tcpListener(listenerTCP.(*net.TCPListener))
}
2018-01-29 23:47:04 +01:00
}
}
// if 'userName' is set and we are the parent process drop privilege and exit
if len(proxy.userName) > 0 && !proxy.child {
proxy.dropPrivilege(proxy.userName, FileDescriptors)
}
2018-01-29 23:47:04 +01:00
if err := proxy.SystemDListeners(); err != nil {
dlog.Fatal(err)
}
liveServers, err := proxy.serversInfo.refresh(proxy)
if liveServers > 0 {
dlog.Noticef("dnscrypt-proxy is ready - live servers: %d", liveServers)
if !proxy.child {
ServiceManagerReadyNotify()
}
2018-01-29 23:47:04 +01:00
} else if err != nil {
dlog.Error(err)
dlog.Notice("dnscrypt-proxy is waiting for at least one server to be reachable")
}
proxy.prefetcher(&proxy.urlsToPrefetch)
2018-07-05 18:05:24 +02:00
if len(proxy.serversInfo.registeredServers) > 0 {
go func() {
for {
delay := proxy.certRefreshDelay
if proxy.serversInfo.liveServers() == 0 {
delay = proxy.certRefreshDelayAfterFailure
}
clocksmith.Sleep(delay)
proxy.serversInfo.refresh(proxy)
2018-01-29 23:47:04 +01:00
}
2018-07-05 18:05:24 +02:00
}()
}
2018-01-29 23:47:04 +01:00
}
func (proxy *Proxy) prefetcher(urlsToPrefetch *[]URLToPrefetch) {
go func() {
for {
now := time.Now()
for i := range *urlsToPrefetch {
urlToPrefetch := &(*urlsToPrefetch)[i]
if now.After(urlToPrefetch.when) {
dlog.Debugf("Prefetching [%s]", urlToPrefetch.url)
if err := PrefetchSourceURL(proxy.xTransport, urlToPrefetch); err != nil {
dlog.Debugf("Prefetching [%s] failed: %s", urlToPrefetch.url, err)
2018-01-29 23:47:04 +01:00
} else {
dlog.Debugf("Prefetching [%s] succeeded. Next refresh scheduled for %v", urlToPrefetch.url, urlToPrefetch.when)
}
}
}
clocksmith.Sleep(60 * time.Second)
2018-01-29 23:47:04 +01:00
}
}()
}
func (proxy *Proxy) udpListener(clientPc *net.UDPConn) {
defer clientPc.Close()
for {
buffer := make([]byte, MaxDNSPacketSize-1)
length, clientAddr, err := clientPc.ReadFrom(buffer)
if err != nil {
return
}
packet := buffer[:length]
go func() {
start := time.Now()
2018-01-29 23:47:04 +01:00
if !proxy.clientsCountInc() {
dlog.Warnf("Too many connections (max=%d)", proxy.maxClients)
return
}
defer proxy.clientsCountDec()
proxy.processIncomingQuery(proxy.serversInfo.getOne(), "udp", proxy.mainProto, packet, &clientAddr, clientPc, start)
2018-01-29 23:47:04 +01:00
}()
}
}
func (proxy *Proxy) udpListenerFromAddr(listenAddr *net.UDPAddr) error {
clientPc, err := net.ListenUDP("udp", listenAddr)
if err != nil {
return err
}
dlog.Noticef("Now listening to %v [UDP]", listenAddr)
go proxy.udpListener(clientPc)
return nil
}
func (proxy *Proxy) tcpListener(acceptPc *net.TCPListener) {
defer acceptPc.Close()
for {
clientPc, err := acceptPc.Accept()
if err != nil {
continue
}
go func() {
start := time.Now()
2018-01-29 23:47:04 +01:00
defer clientPc.Close()
if !proxy.clientsCountInc() {
dlog.Warnf("Too many connections (max=%d)", proxy.maxClients)
return
}
defer proxy.clientsCountDec()
clientPc.SetDeadline(time.Now().Add(proxy.timeout))
2018-06-06 15:54:51 +02:00
packet, err := ReadPrefixed(&clientPc)
2018-01-29 23:47:04 +01:00
if err != nil || len(packet) < MinDNSPacketSize {
return
}
clientAddr := clientPc.RemoteAddr()
proxy.processIncomingQuery(proxy.serversInfo.getOne(), "tcp", "tcp", packet, &clientAddr, clientPc, start)
2018-01-29 23:47:04 +01:00
}()
}
}
func (proxy *Proxy) tcpListenerFromAddr(listenAddr *net.TCPAddr) error {
acceptPc, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
return err
}
dlog.Noticef("Now listening to %v [TCP]", listenAddr)
go proxy.tcpListener(acceptPc)
return nil
}
func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
2018-01-29 23:47:04 +01:00
pc, err := net.DialUDP("udp", nil, serverInfo.UDPAddr)
if err != nil {
return nil, err
}
pc.SetDeadline(time.Now().Add(serverInfo.Timeout))
pc.Write(encryptedQuery)
encryptedResponse := make([]byte, MaxDNSPacketSize)
length, err := pc.Read(encryptedResponse)
pc.Close()
if err != nil {
return nil, err
}
encryptedResponse = encryptedResponse[:length]
return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
2018-01-29 23:47:04 +01:00
}
func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
var err error
var pc net.Conn
2018-06-06 15:54:51 +02:00
proxyDialer := proxy.xTransport.proxyDialer
if proxyDialer == nil {
pc, err = net.Dial("tcp", serverInfo.TCPAddr.String())
2018-06-06 15:54:51 +02:00
} else {
pc, err = (*proxyDialer).Dial("tcp", serverInfo.TCPAddr.String())
2018-01-29 23:47:04 +01:00
}
if err != nil {
return nil, err
}
pc.SetDeadline(time.Now().Add(serverInfo.Timeout))
encryptedQuery, err = PrefixWithSize(encryptedQuery)
2018-01-29 23:47:04 +01:00
if err != nil {
return nil, err
}
pc.Write(encryptedQuery)
encryptedResponse, err := ReadPrefixed(&pc)
pc.Close()
2018-07-09 15:49:36 +02:00
if err != nil {
return nil, err
}
return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
2018-01-29 23:47:04 +01:00
}
func (proxy *Proxy) clientsCountInc() bool {
for {
count := atomic.LoadUint32(&proxy.clientsCount)
2018-01-29 23:47:04 +01:00
if count >= proxy.maxClients {
return false
}
if atomic.CompareAndSwapUint32(&proxy.clientsCount, count, count+1) {
dlog.Debugf("clients count: %d", count+1)
2018-01-29 23:47:04 +01:00
return true
}
}
}
func (proxy *Proxy) clientsCountDec() {
for {
if count := atomic.LoadUint32(&proxy.clientsCount); count == 0 || atomic.CompareAndSwapUint32(&proxy.clientsCount, count, count-1) {
2018-01-29 23:47:04 +01:00
break
}
}
}
func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, clientProto string, serverProto string, query []byte, clientAddr *net.Addr, clientPc net.Conn, start time.Time) {
2018-07-05 18:05:24 +02:00
if len(query) < MinDNSPacketSize {
2018-01-29 23:47:04 +01:00
return
}
pluginsState := NewPluginsState(proxy, clientProto, clientAddr, start)
2018-01-29 23:47:04 +01:00
query, _ = pluginsState.ApplyQueryPlugins(&proxy.pluginsGlobals, query)
if len(query) < MinDNSPacketSize || len(query) > MaxDNSPacketSize {
return
}
2018-01-29 23:47:04 +01:00
var response []byte
var err error
if pluginsState.action != PluginsActionForward {
if pluginsState.synthResponse != nil {
response, err = pluginsState.synthResponse.PackBuffer(response)
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
2018-01-29 23:47:04 +01:00
return
}
}
if pluginsState.action == PluginsActionDrop {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeDrop
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
2018-01-29 23:47:04 +01:00
return
}
2018-06-04 23:18:28 +02:00
} else {
pluginsState.returnCode = PluginsReturnCodeForward
2018-01-29 23:47:04 +01:00
}
2018-07-05 18:05:24 +02:00
if len(response) == 0 && serverInfo != nil {
var ttl *uint32
2018-04-14 15:03:21 +02:00
if serverInfo.Proto == stamps.StampProtoTypeDNSCrypt {
sharedKey, encryptedQuery, clientNonce, err := proxy.Encrypt(serverInfo, query, serverProto)
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
return
}
serverInfo.noticeBegin(proxy)
if serverProto == "udp" {
response, err = proxy.exchangeWithUDPServer(serverInfo, sharedKey, encryptedQuery, clientNonce)
} else {
response, err = proxy.exchangeWithTCPServer(serverInfo, sharedKey, encryptedQuery, clientNonce)
}
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeServerError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
return
}
2018-04-14 15:03:21 +02:00
} else if serverInfo.Proto == stamps.StampProtoTypeDoH {
tid := TransactionID(query)
SetTransactionID(query, 0)
serverInfo.noticeBegin(proxy)
resp, _, err := proxy.xTransport.DoHQuery(serverInfo.useGet, serverInfo.URL, query, proxy.timeout)
SetTransactionID(query, tid)
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeServerError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
2018-01-29 23:47:04 +01:00
return
}
2018-02-04 11:31:54 +01:00
response, err = ioutil.ReadAll(io.LimitReader(resp.Body, int64(MaxDNSPacketSize)))
2018-01-29 23:47:04 +01:00
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeServerError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
2018-01-29 23:47:04 +01:00
return
}
if len(response) >= MinDNSPacketSize {
SetTransactionID(response, tid)
}
2018-01-29 23:47:04 +01:00
} else {
dlog.Fatal("Unsupported protocol")
}
if len(response) < MinDNSPacketSize || len(response) > MaxDNSPacketSize {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
serverInfo.noticeFailure(proxy)
return
}
response, err = pluginsState.ApplyResponsePlugins(&proxy.pluginsGlobals, response, ttl)
2018-01-29 23:47:04 +01:00
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
2018-01-29 23:47:04 +01:00
serverInfo.noticeFailure(proxy)
return
}
if rcode := Rcode(response); rcode == 2 { // SERVFAIL
2018-04-12 11:07:34 +02:00
dlog.Infof("Server [%v] returned temporary error code [%v] -- Upstream server may be experiencing connectivity issues", serverInfo.Name, rcode)
serverInfo.noticeFailure(proxy)
} else {
serverInfo.noticeSuccess(proxy)
}
2018-01-29 23:47:04 +01:00
}
2018-07-05 18:05:24 +02:00
if len(response) < MinDNSPacketSize || len(response) > MaxDNSPacketSize {
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
if serverInfo != nil {
serverInfo.noticeFailure(proxy)
}
return
}
2018-01-29 23:47:04 +01:00
if clientProto == "udp" {
if len(response) > MaxDNSUDPPacketSize {
response, err = TruncatedResponse(response)
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
2018-01-29 23:47:04 +01:00
return
}
}
clientPc.(net.PacketConn).WriteTo(response, *clientAddr)
if HasTCFlag(response) {
proxy.questionSizeEstimator.blindAdjust()
} else {
proxy.questionSizeEstimator.adjust(ResponseOverhead + len(response))
}
} else {
response, err = PrefixWithSize(response)
if err != nil {
2018-06-04 23:18:28 +02:00
pluginsState.returnCode = PluginsReturnCodeParseError
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
2018-07-05 18:05:24 +02:00
if serverInfo != nil {
serverInfo.noticeFailure(proxy)
}
2018-01-29 23:47:04 +01:00
return
}
clientPc.Write(response)
}
pluginsState.ApplyLoggingPlugins(&proxy.pluginsGlobals)
2018-01-29 23:47:04 +01:00
}
func NewProxy() Proxy {
return Proxy{
serversInfo: ServersInfo{lbStrategy: DefaultLBStrategy, lbEstimator: true},
}
}