dnscrypt-proxy/dnscrypt-proxy/main.go

281 lines
7.0 KiB
Go
Raw Normal View History

package main
import (
"crypto/rand"
"log"
"net"
2018-01-14 00:56:46 +01:00
"os"
"path/filepath"
"sync"
"time"
2018-01-11 11:50:54 +01:00
"github.com/jedisct1/dlog"
"github.com/judwhite/go-svc/svc"
"golang.org/x/crypto/curve25519"
)
type Proxy struct {
2018-01-09 16:40:37 +01:00
proxyPublicKey [32]byte
proxySecretKey [32]byte
questionSizeEstimator QuestionSizeEstimator
serversInfo ServersInfo
2018-01-09 16:40:37 +01:00
timeout time.Duration
2018-01-10 12:01:49 +01:00
certRefreshDelay time.Duration
mainProto string
2018-01-10 12:01:49 +01:00
listenAddresses []string
daemonize bool
registeredServers []RegisteredServer
2018-01-10 17:23:20 +01:00
pluginBlockIPv6 bool
2018-01-10 18:32:05 +01:00
cache bool
cacheSize int
cacheNegTTL uint32
cacheMinTTL uint32
cacheMaxTTL uint32
2018-01-16 00:23:16 +01:00
queryLogFile string
queryLogFormat string
blockNameFile string
2018-01-17 09:44:03 +01:00
forwardFile string
pluginsGlobals PluginsGlobals
}
type App struct {
wg sync.WaitGroup
quit chan struct{}
}
2018-01-09 13:35:10 +01:00
func main() {
2018-01-11 11:50:54 +01:00
dlog.Init("dnscrypt-proxy", dlog.SeverityNotice)
2018-01-14 00:56:46 +01:00
cdLocal()
app := &App{}
if err := svc.Run(app); err != nil {
dlog.Fatal(err)
}
}
func (app *App) Init(env svc.Environment) error {
log.Printf("is win service? %v\n", env.IsWindowsService())
return nil
}
func (app *App) Start() error {
2018-01-10 12:01:49 +01:00
proxy := Proxy{}
if err := ConfigLoad(&proxy, "dnscrypt-proxy.toml"); err != nil {
2018-01-11 11:50:54 +01:00
dlog.Fatal(err)
2018-01-10 12:01:49 +01:00
}
if err := InitPluginsGlobals(&proxy.pluginsGlobals, &proxy); err != nil {
dlog.Fatal(err)
}
2018-01-10 13:33:06 +01:00
if proxy.daemonize {
Daemonize()
2018-01-10 13:33:06 +01:00
}
app.quit = make(chan struct{})
app.wg.Add(1)
go func() {
proxy.StartProxy()
<-app.quit
dlog.Notice("Quit signal received...")
app.wg.Done()
}()
return nil
}
func (app *App) Stop() error {
dlog.Notice("Stopping...")
close(app.quit)
app.wg.Wait()
dlog.Notice("Stopped.")
return nil
2018-01-14 00:56:46 +01:00
}
2018-01-10 12:01:49 +01:00
func (proxy *Proxy) StartProxy() {
proxy.questionSizeEstimator = NewQuestionSizeEstimator()
if _, err := rand.Read(proxy.proxySecretKey[:]); err != nil {
2018-01-11 11:50:54 +01:00
dlog.Fatal(err)
}
curve25519.ScalarBaseMult(&proxy.proxyPublicKey, &proxy.proxySecretKey)
2018-01-10 12:01:49 +01:00
for _, registeredServer := range proxy.registeredServers {
proxy.serversInfo.registerServer(proxy, registeredServer.name, registeredServer.stamp)
2018-01-09 16:40:37 +01:00
}
2018-01-10 12:01:49 +01:00
for _, listenAddrStr := range proxy.listenAddresses {
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
if err != nil {
2018-01-11 11:50:54 +01:00
dlog.Fatal(err)
2018-01-10 12:01:49 +01:00
}
listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr)
if err != nil {
2018-01-11 11:50:54 +01:00
dlog.Fatal(err)
2018-01-10 12:01:49 +01:00
}
if err := proxy.udpListener(listenUDPAddr); err != nil {
2018-01-11 11:50:54 +01:00
dlog.Fatal(err)
2018-01-10 12:01:49 +01:00
}
if err := proxy.tcpListener(listenTCPAddr); err != nil {
2018-01-11 11:50:54 +01:00
dlog.Fatal(err)
2018-01-10 12:01:49 +01:00
}
2018-01-09 16:40:37 +01:00
}
2018-01-11 11:50:54 +01:00
dlog.Notice("dnscrypt-proxy is ready")
go func() {
for {
time.Sleep(proxy.certRefreshDelay)
proxy.serversInfo.refresh(proxy)
}
}()
2018-01-09 13:35:10 +01:00
}
2018-01-09 16:40:37 +01:00
func (proxy *Proxy) udpListener(listenAddr *net.UDPAddr) error {
clientPc, err := net.ListenUDP("udp", listenAddr)
if err != nil {
2018-01-09 13:35:10 +01:00
return err
}
2018-01-10 12:01:49 +01:00
go func() {
defer clientPc.Close()
2018-01-11 11:50:54 +01:00
dlog.Noticef("Now listening to %v [UDP]", listenAddr)
2018-01-10 12:01:49 +01:00
for {
buffer := make([]byte, MaxDNSPacketSize-1)
length, clientAddr, err := clientPc.ReadFrom(buffer)
if err != nil {
return
}
packet := buffer[:length]
go func() {
proxy.processIncomingQuery(proxy.serversInfo.getOne(), "udp", proxy.mainProto, packet, &clientAddr, clientPc)
2018-01-10 12:01:49 +01:00
}()
}
2018-01-10 12:01:49 +01:00
}()
return nil
2018-01-09 16:40:37 +01:00
}
func (proxy *Proxy) tcpListener(listenAddr *net.TCPAddr) error {
acceptPc, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
return err
}
2018-01-10 12:01:49 +01:00
go func() {
defer acceptPc.Close()
2018-01-11 11:50:54 +01:00
dlog.Noticef("Now listening to %v [TCP]", listenAddr)
2018-01-10 12:01:49 +01:00
for {
clientPc, err := acceptPc.Accept()
if err != nil {
continue
2018-01-09 16:40:37 +01:00
}
2018-01-10 12:01:49 +01:00
go func() {
defer clientPc.Close()
clientPc.SetDeadline(time.Now().Add(proxy.timeout))
packet, err := ReadPrefixed(clientPc.(*net.TCPConn))
if err != nil || len(packet) < MinDNSPacketSize {
return
}
clientAddr := clientPc.RemoteAddr()
proxy.processIncomingQuery(proxy.serversInfo.getOne(), "tcp", "tcp", packet, &clientAddr, clientPc)
2018-01-10 12:01:49 +01:00
}()
}
}()
return nil
2018-01-09 13:27:03 +01:00
}
2018-01-10 00:31:12 +01:00
func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
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, encryptedResponse, clientNonce)
}
func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
pc, err := net.DialTCP("tcp", nil, serverInfo.TCPAddr)
if err != nil {
return nil, err
}
pc.SetDeadline(time.Now().Add(serverInfo.Timeout))
encryptedQuery, err = PrefixWithSize(encryptedQuery)
if err != nil {
return nil, err
}
pc.Write(encryptedQuery)
encryptedResponse, err := ReadPrefixed(pc)
pc.Close()
if err != nil {
return nil, err
}
return proxy.Decrypt(serverInfo, encryptedResponse, clientNonce)
}
func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, clientProto string, serverProto string, query []byte, clientAddr *net.Addr, clientPc net.Conn) {
2018-01-10 16:01:29 +01:00
if len(query) < MinDNSPacketSize || serverInfo == nil {
2018-01-09 13:27:03 +01:00
return
}
pluginsState := NewPluginsState(proxy, clientProto, clientAddr)
query, _ = pluginsState.ApplyQueryPlugins(&proxy.pluginsGlobals, query)
var response []byte
2018-01-10 17:23:20 +01:00
var err error
if pluginsState.action != PluginsActionForward {
if pluginsState.synthResponse != nil {
response, err = pluginsState.synthResponse.PackBuffer(response)
if err != nil {
return
}
}
if pluginsState.action == PluginsActionDrop {
return
}
2018-01-10 00:32:16 +01:00
}
2018-01-10 17:23:20 +01:00
if len(response) == 0 {
encryptedQuery, clientNonce, err := proxy.Encrypt(serverInfo, query, serverProto)
if err != nil {
return
}
serverInfo.noticeBegin(proxy)
if serverProto == "udp" {
response, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce)
} else {
response, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce)
}
if err != nil {
serverInfo.noticeFailure(proxy)
return
}
response, _ = pluginsState.ApplyResponsePlugins(&proxy.pluginsGlobals, response)
}
if clientProto == "udp" {
if len(response) > MaxDNSUDPPacketSize {
response, err = TruncatedResponse(response)
if err != nil {
return
}
}
clientPc.(net.PacketConn).WriteTo(response, *clientAddr)
if HasTCFlag(response) {
2018-01-10 00:31:12 +01:00
proxy.questionSizeEstimator.blindAdjust()
} else {
2018-01-10 16:01:29 +01:00
proxy.questionSizeEstimator.adjust(ResponseOverhead + len(response))
2018-01-10 00:31:12 +01:00
}
2018-01-09 16:40:37 +01:00
} else {
response, err = PrefixWithSize(response)
2018-01-09 19:47:24 +01:00
if err != nil {
serverInfo.noticeFailure(proxy)
2018-01-09 19:47:24 +01:00
return
}
2018-01-10 00:31:12 +01:00
clientPc.Write(response)
}
serverInfo.noticeSuccess(proxy)
}
func cdLocal() {
ex, err := os.Executable()
if err != nil {
dlog.Critical(err)
return
}
exPath := filepath.Dir(ex)
os.Chdir(exPath)
}