1
0
Fork 0
mirror of https://github.com/SamTherapy/dnscrypt.git synced 2024-07-05 06:46:07 +00:00
dnscrypt/server_udp.go
2020-10-19 17:20:49 +03:00

125 lines
3.2 KiB
Go

package dnscrypt
import (
"bytes"
"net"
"github.com/AdguardTeam/golibs/log"
"github.com/miekg/dns"
)
type encryptionFunc func(m *dns.Msg, q EncryptedQuery) ([]byte, error)
// UDPResponseWriter - ResponseWriter implementation for UDP
type UDPResponseWriter struct {
udpConn *net.UDPConn // UDP connection
remoteAddr *net.UDPAddr // Remote peer address
localIP net.IP // Local IP (that was used to accept the remote connection)
encrypt encryptionFunc // DNSCRypt encryption function
req *dns.Msg // DNS query that was processed
query EncryptedQuery // DNSCrypt query properties
}
// type check
var _ ResponseWriter = &UDPResponseWriter{}
// LocalAddr - server socket local address
func (w *UDPResponseWriter) LocalAddr() net.Addr {
return w.udpConn.LocalAddr()
}
// RemoteAddr - client's address
func (w *UDPResponseWriter) RemoteAddr() net.Addr {
return w.remoteAddr
}
// WriteMsg - writes DNS message to the client
func (w *UDPResponseWriter) WriteMsg(m *dns.Msg) error {
m.Truncate(dnsSize("udp", w.req))
res, err := w.encrypt(m, w.query)
if err != nil {
log.Tracef("Failed to encrypt the DNS query: %v", err)
return err
}
_, _ = udpWrite(res, w.udpConn, w.remoteAddr, w.localIP)
return nil
}
// ServeUDP - listens to UDP connections, queries are then processed by Server.Handler.
// It blocks the calling goroutine and to stop it you need to close the listener.
func (s *Server) ServeUDP(l *net.UDPConn) error {
// Check that server is properly configured
if !s.validate() {
return ErrServerConfig
}
// set UDP options to allow receiving OOB data
err := udpSetOptions(l)
if err != nil {
return err
}
// Buffer to read incoming messages
b := make([]byte, dns.MaxMsgSize)
// Serialize the cert right away and prepare it to be sent to the client
certBuf, err := s.ResolverCert.Serialize()
if err != nil {
return err
}
certTxt := packTxtString(certBuf)
// Init oobSize - it will be used later when reading and writing UDP messages
oobSize := udpGetOOBSize()
log.Info("Entering DNSCrypt UDP listening loop on udp://%s", l.LocalAddr().String())
for {
n, localIP, addr, err := udpRead(l, b, oobSize)
if n < minDNSPacketSize {
// Ignore the packets that are too short
continue
}
if bytes.Equal(b[:clientMagicSize], s.ResolverCert.ClientMagic[:]) {
// This is an encrypted message, we should decrypt it
m, q, err := s.decrypt(b[:n])
if err == nil {
rw := &UDPResponseWriter{
udpConn: l,
remoteAddr: addr,
localIP: localIP,
encrypt: s.encrypt,
req: m,
query: q,
}
go s.serveDNS(rw, m)
} else {
log.Tracef("Failed to decrypt incoming message len=%d: %v", n, err)
}
} else {
// Most likely this a DNS message requesting the certificate
reply, err := s.handleHandshake(b, certTxt)
if err != nil {
log.Tracef("Failed to process a plain DNS query: %v", err)
}
if err == nil {
_, _ = l.WriteTo(reply, addr)
}
}
if err != nil {
if isConnClosed(err) {
log.Info("udpListen.ReadFrom() returned because we're reading from a closed connection, exiting loop")
} else {
log.Info("got error when reading from UDP listen: %s", err)
}
break
}
}
return nil
}