mirror of
https://github.com/SamTherapy/dnscrypt.git
synced 2024-10-02 16:32:51 +00:00
125 lines
3.2 KiB
Go
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
|
|
}
|