1
0
Fork 0
mirror of https://github.com/SamTherapy/dnscrypt.git synced 2024-12-22 08:50:43 +00:00

Fix golint issues

This commit is contained in:
Andrey Meshkov 2018-12-17 02:31:36 +03:00
parent 97b46cf0ce
commit 0f4b899065
3 changed files with 84 additions and 53 deletions

View file

@ -1,5 +1,7 @@
[![Build Status](https://travis-ci.org/ameshkov/dnscrypt.svg?branch=master)](https://travis-ci.org/ameshkov/dnscrypt)
[![Code Coverage](https://img.shields.io/codecov/c/github/ameshkov/dnscrypt/master.svg)](https://codecov.io/github/ameshkov/dnscrypt?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/ameshkov/dnscrypt)](https://goreportcard.com/report/ameshkov/dnscrypt)
[![Go Doc](https://godoc.org/github.com/ameshkov/dnscrypt?status.svg)](https://godoc.org/github.com/ameshkov/dnscrypt)
# DNSCrypt Client

View file

@ -20,54 +20,53 @@ import (
"golang.org/x/crypto/nacl/secretbox"
)
// Encryption algorithm (either XSalsa20Poly1305 or XChacha20Poly1305)
type CryptoConstruction uint16
const (
UndefinedConstruction CryptoConstruction = iota
XSalsa20Poly1305
XChacha20Poly1305
UndefinedConstruction CryptoConstruction = iota // Default value for empty CertInfo only
XSalsa20Poly1305 // Salsa20Poly1305 encryption
XChacha20Poly1305 // Chacha20Poly1305 encryption
)
var (
CertMagic = [4]byte{0x44, 0x4e, 0x53, 0x43}
ServerMagic = [8]byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38}
MinDNSPacketSize = 12 + 5
MaxDNSPacketSize = 4096
MaxDNSUDPPacketSize = 1252
certMagic = [4]byte{0x44, 0x4e, 0x53, 0x43}
serverMagic = [8]byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38}
minDNSPacketSize = 12 + 5
maxDNSPacketSize = 4096
maxDNSUDPPacketSize = 1252
)
const (
ClientMagicLen = 8
)
const (
NonceSize = xsecretbox.NonceSize
HalfNonceSize = xsecretbox.NonceSize / 2
TagSize = xsecretbox.TagSize
PublicKeySize = 32
QueryOverhead = ClientMagicLen + PublicKeySize + HalfNonceSize + TagSize
ResponseOverhead = len(ServerMagic) + NonceSize + TagSize
clientMagicLen = 8
nonceSize = xsecretbox.NonceSize
halfNonceSize = xsecretbox.NonceSize / 2
tagSize = xsecretbox.TagSize
publicKeySize = 32
queryOverhead = clientMagicLen + publicKeySize + halfNonceSize + tagSize
responseOverhead = len(serverMagic) + nonceSize + tagSize
// Some servers do not work if padded length is less than 256. Example: Quad9
MinQuestionSize = 256
minUdpQuestionSize = 256
)
// Client contains parameters for a DNSCrypt client
type Client struct {
Proto string // Protocol ("udp" or "tcp")
Timeout time.Duration // Timeout for read/write operations
}
// DnsCrypt server certificate data
// CertInfo contains DnsCrypt server certificate data retrieved from the server
type CertInfo struct {
Serial uint32
ServerPk [32]byte
SharedKey [32]byte
MagicQuery [ClientMagicLen]byte
MagicQuery [clientMagicLen]byte
CryptoConstruction CryptoConstruction
ForwardSecurity bool
}
// DNSCrypt server information necessary for decryption/encryption
// ServerInfo contains DNSCrypt server information necessary for decryption/encryption
type ServerInfo struct {
SecretKey [32]byte // Client secret key
PublicKey [32]byte // Client public key
@ -79,7 +78,7 @@ type ServerInfo struct {
ServerCert *CertInfo // Certificate info (obtained with the first unencrypted DNS request)
}
// Fetches and validates DNSCrypt certificate from the given server
// Dial fetches and validates DNSCrypt certificate from the given server
// Data received during this call is then used for DNS requests encryption/decryption
// stampStr is an sdns:// address which is parsed using go-dnsstamps package
func (c *Client) Dial(stampStr string) (*ServerInfo, time.Duration, error) {
@ -97,7 +96,7 @@ func (c *Client) Dial(stampStr string) (*ServerInfo, time.Duration, error) {
return c.DialStamp(stamp)
}
// Fetches and validates DNSCrypt certificate from the given server
// DialStamp fetches and validates DNSCrypt certificate from the given server
// Data received during this call is then used for DNS requests encryption/decryption
func (c *Client) DialStamp(stamp dnsstamps.ServerStamp) (*ServerInfo, time.Duration, error) {
@ -129,7 +128,7 @@ func (c *Client) DialStamp(stamp dnsstamps.ServerStamp) (*ServerInfo, time.Durat
return &serverInfo, rtt, nil
}
// Performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response.
// Exchange performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response.
// This method creates a new network connection for every call so avoid using it for TCP.
// DNSCrypt server information needs to be fetched and validated prior to this call using the c.DialStamp method.
func (c *Client) Exchange(m *dns.Msg, s *ServerInfo) (*dns.Msg, time.Duration, error) {
@ -150,7 +149,7 @@ func (c *Client) Exchange(m *dns.Msg, s *ServerInfo) (*dns.Msg, time.Duration, e
return r, rtt, nil
}
// Performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response.
// ExchangeConn performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response.
// DNSCrypt server information needs to be fetched and validated prior to this call using the c.DialStamp method
func (c *Client) ExchangeConn(m *dns.Msg, s *ServerInfo, conn net.Conn) (*dns.Msg, time.Duration, error) {
now := time.Now()
@ -175,7 +174,7 @@ func (c *Client) ExchangeConn(m *dns.Msg, s *ServerInfo, conn net.Conn) (*dns.Ms
conn.SetDeadline(time.Now().Add(c.Timeout))
conn.Write(encryptedQuery)
encryptedResponse := make([]byte, MaxDNSPacketSize)
encryptedResponse := make([]byte, maxDNSPacketSize)
if c.Proto == "tcp" {
encryptedResponse, err = readPrefixed(conn)
@ -210,17 +209,17 @@ func (c *Client) ExchangeConn(m *dns.Msg, s *ServerInfo, conn net.Conn) (*dns.Ms
// See https://github.com/jedisct1/dnscrypt-proxy/blob/master/dnscrypt-proxy/plugin_get_set_payload_size.go
// TODO: I don't really understand why it is required :)
func (c *Client) adjustPayloadSize(msg *dns.Msg) {
originalMaxPayloadSize := 512 - ResponseOverhead
originalMaxPayloadSize := 512 - responseOverhead
edns0 := msg.IsEdns0()
dnssec := false
if edns0 != nil {
originalMaxPayloadSize = min(int(edns0.UDPSize())-ResponseOverhead, originalMaxPayloadSize)
originalMaxPayloadSize = min(int(edns0.UDPSize())-responseOverhead, originalMaxPayloadSize)
dnssec = edns0.Do()
}
var options *[]dns.EDNS0
maxPayloadSize := MaxDNSUDPPacketSize - ResponseOverhead
maxPayloadSize = min(MaxDNSUDPPacketSize-ResponseOverhead, max(originalMaxPayloadSize, maxPayloadSize))
maxPayloadSize := maxDNSUDPPacketSize - responseOverhead
maxPayloadSize = min(maxDNSUDPPacketSize-responseOverhead, max(originalMaxPayloadSize, maxPayloadSize))
if maxPayloadSize > 512 {
var extra2 []dns.RR
@ -251,7 +250,7 @@ func (s *ServerInfo) fetchCurrentDNSCryptCert(timeout time.Duration) (CertInfo,
query := new(dns.Msg)
query.SetQuestion(s.ProviderName, dns.TypeTXT)
client := dns.Client{Net: s.Proto, UDPSize: uint16(MaxDNSUDPPacketSize), Timeout: timeout}
client := dns.Client{Net: s.Proto, UDPSize: uint16(maxDNSUDPPacketSize), Timeout: timeout}
in, rtt, err := client.Exchange(query, s.ServerAddress)
if err != nil {
return CertInfo{}, 0, err
@ -292,31 +291,31 @@ func (s *ServerInfo) fetchCurrentDNSCryptCert(timeout time.Duration) (CertInfo,
}
func (s *ServerInfo) encrypt(packet []byte) (encrypted []byte, clientNonce []byte, err error) {
nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize)
nonce, clientNonce := make([]byte, nonceSize), make([]byte, halfNonceSize)
rand.Read(clientNonce)
copy(nonce, clientNonce)
var publicKey *[PublicKeySize]byte
var publicKey *[publicKeySize]byte
sharedKey := &s.ServerCert.SharedKey
publicKey = &s.PublicKey
minQuestionSize := QueryOverhead + len(packet)
minQuestionSize := queryOverhead + len(packet)
if s.Proto == "udp" {
minQuestionSize = max(MinQuestionSize, minQuestionSize)
minQuestionSize = max(minUdpQuestionSize, minQuestionSize)
} else {
var xpad [1]byte
rand.Read(xpad[:])
minQuestionSize += int(xpad[0])
}
paddedLength := min(MaxDNSUDPPacketSize, (max(minQuestionSize, QueryOverhead)+63) & ^63)
paddedLength := min(maxDNSUDPPacketSize, (max(minQuestionSize, queryOverhead)+63) & ^63)
if QueryOverhead+len(packet)+1 > paddedLength {
if queryOverhead+len(packet)+1 > paddedLength {
err = errors.New("question too large; cannot be padded")
return
}
encrypted = append(s.ServerCert.MagicQuery[:], publicKey[:]...)
encrypted = append(encrypted, nonce[:HalfNonceSize]...)
padded := pad(packet, paddedLength-QueryOverhead)
encrypted = append(encrypted, nonce[:halfNonceSize]...)
padded := pad(packet, paddedLength-queryOverhead)
if s.ServerCert.CryptoConstruction == XChacha20Poly1305 {
encrypted = xsecretbox.Seal(encrypted, nonce, padded, sharedKey[:])
} else {
@ -330,15 +329,15 @@ func (s *ServerInfo) encrypt(packet []byte) (encrypted []byte, clientNonce []byt
func (s *ServerInfo) decrypt(encrypted []byte, nonce []byte) ([]byte, error) {
sharedKey := &s.ServerCert.SharedKey
serverMagicLen := len(ServerMagic)
responseHeaderLen := serverMagicLen + NonceSize
if len(encrypted) < responseHeaderLen+TagSize+int(MinDNSPacketSize) ||
len(encrypted) > responseHeaderLen+TagSize+int(MaxDNSPacketSize) ||
!bytes.Equal(encrypted[:serverMagicLen], ServerMagic[:]) {
serverMagicLen := len(serverMagic)
responseHeaderLen := serverMagicLen + nonceSize
if len(encrypted) < responseHeaderLen+tagSize+int(minDNSPacketSize) ||
len(encrypted) > responseHeaderLen+tagSize+int(maxDNSPacketSize) ||
!bytes.Equal(encrypted[:serverMagicLen], serverMagic[:]) {
return encrypted, errors.New("invalid message size or prefix")
}
serverNonce := encrypted[serverMagicLen:responseHeaderLen]
if !bytes.Equal(nonce[:HalfNonceSize], serverNonce[:HalfNonceSize]) {
if !bytes.Equal(nonce[:halfNonceSize], serverNonce[:halfNonceSize]) {
return encrypted, errors.New("unexpected nonce")
}
var packet []byte
@ -358,7 +357,7 @@ func (s *ServerInfo) decrypt(encrypted []byte, nonce []byte) ([]byte, error) {
return encrypted, err
}
packet, err = unpad(packet)
if err != nil || len(packet) < MinDNSPacketSize {
if err != nil || len(packet) < minDNSPacketSize {
return encrypted, errors.New("incorrect padding")
}
return packet, nil
@ -378,7 +377,7 @@ func txtToCertInfo(answerRr dns.RR, serverInfo *ServerInfo) (CertInfo, error) {
if len(binCert) < 124 {
return certInfo, errors.New("certificate is too short")
}
if !bytes.Equal(binCert[:4], CertMagic[:4]) {
if !bytes.Equal(binCert[:4], certMagic[:4]) {
return certInfo, errors.New("invalid cert magic")
}
@ -388,7 +387,7 @@ func txtToCertInfo(answerRr dns.RR, serverInfo *ServerInfo) (CertInfo, error) {
case 0x0002:
certInfo.CryptoConstruction = XChacha20Poly1305
default:
return certInfo, errors.New(fmt.Sprintf("unsupported crypto construction: %v", esVersion))
return certInfo, fmt.Errorf("unsupported crypto construction: %v", esVersion)
}
// Verify the server public key
@ -404,7 +403,7 @@ func txtToCertInfo(answerRr dns.RR, serverInfo *ServerInfo) (CertInfo, error) {
tsBegin := binary.BigEndian.Uint32(binCert[116:120])
tsEnd := binary.BigEndian.Uint32(binCert[120:124])
if tsBegin >= tsEnd {
return certInfo, errors.New(fmt.Sprintf("certificate ends before it starts (%v >= %v)", tsBegin, tsEnd))
return certInfo, fmt.Errorf("certificate ends before it starts (%v >= %v)", tsBegin, tsEnd)
}
if now > tsEnd || now < tsBegin {
return certInfo, errors.New("certificate not valid at the current date")
@ -523,7 +522,7 @@ func prefixWithSize(packet []byte) ([]byte, error) {
}
func readPrefixed(conn net.Conn) ([]byte, error) {
buf := make([]byte, 2+MaxDNSPacketSize)
buf := make([]byte, 2+maxDNSPacketSize)
packetLength, pos := -1, 0
for {
readnb, err := conn.Read(buf[pos:])
@ -533,10 +532,10 @@ func readPrefixed(conn net.Conn) ([]byte, error) {
pos += readnb
if pos >= 2 && packetLength < 0 {
packetLength = int(binary.BigEndian.Uint16(buf[0:2]))
if packetLength > MaxDNSPacketSize-1 {
if packetLength > maxDNSPacketSize-1 {
return buf, errors.New("packet too large")
}
if packetLength < MinDNSPacketSize {
if packetLength < minDNSPacketSize {
return buf, errors.New("packet too short")
}
}

30
doc.go Normal file
View file

@ -0,0 +1,30 @@
/*
Package dnscrypt implements a very simple DNSCrypt client library.
The idea is to let others use DNSCrypt resolvers in the same manner as we can use regular and DoT resolvers with miekg's DNS library.
Here is a simple usage example:
// AdGuard DNS stamp
stampStr := "sdns://AQIAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20"
// Initializing the DNSCrypt client
c := dnscrypt.Client{Proto: "udp", Timeout: 10 * time.Second}
// Fetching and validating the server certificate
serverInfo, rtt, err := client.Dial(stampStr)
// Create a DNS request
req := dns.Msg{}
req.Id = dns.Id()
req.RecursionDesired = true
req.Question = []dns.Question{
{Name: "google-public-dns-a.google.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET},
}
// Get the DNS response
reply, rtt, err := client.Exchange(&req, serverInfo)
Unfortunately, I have not found an easy way to use dnscrypt-proxy as a dependency so here's why this library was created.
*/
package dnscrypt