Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
  • renovate/git.autistici.org-ai3-go-common-digest
  • renovate/github.com-miekg-dns-1.x
  • renovate/github.com-prometheus-client_golang-1.x
  • renovate/golang.org-x-crypto-0.x
  • renovate/golang.org-x-net-0.x
  • v2
  • v3
8 results

Target

Select target project
  • ai3/tools/acmeserver
  • godog/acmeserver
  • svp-bot/acmeserver
3 results
Select Git revision
  • lintian-fixes
  • master
  • renovate/github.com-miekg-dns-1.x
  • renovate/golang.org-x-crypto-digest
4 results
Show changes
Showing
with 379 additions and 798 deletions
// Copyright 2013 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package handlers
import (
"bufio"
"fmt"
"net"
"net/http"
"sort"
"strings"
)
// MethodHandler is an http.Handler that dispatches to a handler whose key in the
// MethodHandler's map matches the name of the HTTP request's method, eg: GET
//
// If the request's method is OPTIONS and OPTIONS is not a key in the map then
// the handler responds with a status of 200 and sets the Allow header to a
// comma-separated list of available methods.
//
// If the request's method doesn't match any of its keys the handler responds
// with a status of HTTP 405 "Method Not Allowed" and sets the Allow header to a
// comma-separated list of available methods.
type MethodHandler map[string]http.Handler
func (h MethodHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if handler, ok := h[req.Method]; ok {
handler.ServeHTTP(w, req)
} else {
allow := []string{}
for k := range h {
allow = append(allow, k)
}
sort.Strings(allow)
w.Header().Set("Allow", strings.Join(allow, ", "))
if req.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
} else {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
}
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP
// status code and body size
type responseLogger struct {
w http.ResponseWriter
status int
size int
}
func (l *responseLogger) Write(b []byte) (int, error) {
size, err := l.w.Write(b)
l.size += size
return size, err
}
func (l *responseLogger) WriteHeader(s int) {
l.w.WriteHeader(s)
l.status = s
}
func (l *responseLogger) Status() int {
return l.status
}
func (l *responseLogger) Size() int {
return l.size
}
func (l *responseLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
conn, rw, err := l.w.(http.Hijacker).Hijack()
if err == nil && l.status == 0 {
// The status will be StatusSwitchingProtocols if there was no error and
// WriteHeader has not been called yet
l.status = http.StatusSwitchingProtocols
}
return conn, rw, err
}
// isContentType validates the Content-Type header matches the supplied
// contentType. That is, its type and subtype match.
func isContentType(h http.Header, contentType string) bool {
ct := h.Get("Content-Type")
if i := strings.IndexRune(ct, ';'); i != -1 {
ct = ct[0:i]
}
return ct == contentType
}
// ContentTypeHandler wraps and returns a http.Handler, validating the request
// content type is compatible with the contentTypes list. It writes a HTTP 415
// error if that fails.
//
// Only PUT, POST, and PATCH requests are considered.
func ContentTypeHandler(h http.Handler, contentTypes ...string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !(r.Method == "PUT" || r.Method == "POST" || r.Method == "PATCH") {
h.ServeHTTP(w, r)
return
}
for _, ct := range contentTypes {
if isContentType(r.Header, ct) {
h.ServeHTTP(w, r)
return
}
}
http.Error(w, fmt.Sprintf("Unsupported content type %q; expected one of %q", r.Header.Get("Content-Type"), contentTypes), http.StatusUnsupportedMediaType)
})
}
const (
// HTTPMethodOverrideHeader is a commonly used
// http header to override a request method.
HTTPMethodOverrideHeader = "X-HTTP-Method-Override"
// HTTPMethodOverrideFormKey is a commonly used
// HTML form key to override a request method.
HTTPMethodOverrideFormKey = "_method"
)
// HTTPMethodOverrideHandler wraps and returns a http.Handler which checks for
// the X-HTTP-Method-Override header or the _method form key, and overrides (if
// valid) request.Method with its value.
//
// This is especially useful for HTTP clients that don't support many http verbs.
// It isn't secure to override e.g a GET to a POST, so only POST requests are
// considered. Likewise, the override method can only be a "write" method: PUT,
// PATCH or DELETE.
//
// Form method takes precedence over header method.
func HTTPMethodOverrideHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
om := r.FormValue(HTTPMethodOverrideFormKey)
if om == "" {
om = r.Header.Get(HTTPMethodOverrideHeader)
}
if om == "PUT" || om == "PATCH" || om == "DELETE" {
r.Method = om
}
}
h.ServeHTTP(w, r)
})
}
// Copyright 2013 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package handlers
import (
"io"
"net"
"net/http"
"net/url"
"strconv"
"time"
"unicode/utf8"
"github.com/felixge/httpsnoop"
)
// Logging
// LogFormatterParams is the structure any formatter will be handed when time to log comes
type LogFormatterParams struct {
Request *http.Request
URL url.URL
TimeStamp time.Time
StatusCode int
Size int
}
// LogFormatter gives the signature of the formatter function passed to CustomLoggingHandler
type LogFormatter func(writer io.Writer, params LogFormatterParams)
// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its
// friends
type loggingHandler struct {
writer io.Writer
handler http.Handler
formatter LogFormatter
}
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
t := time.Now()
logger, w := makeLogger(w)
url := *req.URL
h.handler.ServeHTTP(w, req)
if req.MultipartForm != nil {
req.MultipartForm.RemoveAll()
}
params := LogFormatterParams{
Request: req,
URL: url,
TimeStamp: t,
StatusCode: logger.Status(),
Size: logger.Size(),
}
h.formatter(h.writer, params)
}
func makeLogger(w http.ResponseWriter) (*responseLogger, http.ResponseWriter) {
logger := &responseLogger{w: w, status: http.StatusOK}
return logger, httpsnoop.Wrap(w, httpsnoop.Hooks{
Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc {
return logger.Write
},
WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc {
return logger.WriteHeader
},
})
}
const lowerhex = "0123456789abcdef"
func appendQuoted(buf []byte, s string) []byte {
var runeTmp [utf8.UTFMax]byte
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0])
width = 1
if r >= utf8.RuneSelf {
r, width = utf8.DecodeRuneInString(s)
}
if width == 1 && r == utf8.RuneError {
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
continue
}
if r == rune('"') || r == '\\' { // always backslashed
buf = append(buf, '\\')
buf = append(buf, byte(r))
continue
}
if strconv.IsPrint(r) {
n := utf8.EncodeRune(runeTmp[:], r)
buf = append(buf, runeTmp[:n]...)
continue
}
switch r {
case '\a':
buf = append(buf, `\a`...)
case '\b':
buf = append(buf, `\b`...)
case '\f':
buf = append(buf, `\f`...)
case '\n':
buf = append(buf, `\n`...)
case '\r':
buf = append(buf, `\r`...)
case '\t':
buf = append(buf, `\t`...)
case '\v':
buf = append(buf, `\v`...)
default:
switch {
case r < ' ':
buf = append(buf, `\x`...)
buf = append(buf, lowerhex[s[0]>>4])
buf = append(buf, lowerhex[s[0]&0xF])
case r > utf8.MaxRune:
r = 0xFFFD
fallthrough
case r < 0x10000:
buf = append(buf, `\u`...)
for s := 12; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
default:
buf = append(buf, `\U`...)
for s := 28; s >= 0; s -= 4 {
buf = append(buf, lowerhex[r>>uint(s)&0xF])
}
}
}
}
return buf
}
// buildCommonLogLine builds a log entry for req in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
username := "-"
if url.User != nil {
if name := url.User.Username(); name != "" {
username = name
}
}
host, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
host = req.RemoteAddr
}
uri := req.RequestURI
// Requests using the CONNECT method over HTTP/2.0 must use
// the authority field (aka r.Host) to identify the target.
// Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT
if req.ProtoMajor == 2 && req.Method == "CONNECT" {
uri = req.Host
}
if uri == "" {
uri = url.RequestURI()
}
buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
buf = append(buf, host...)
buf = append(buf, " - "...)
buf = append(buf, username...)
buf = append(buf, " ["...)
buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
buf = append(buf, `] "`...)
buf = append(buf, req.Method...)
buf = append(buf, " "...)
buf = appendQuoted(buf, uri)
buf = append(buf, " "...)
buf = append(buf, req.Proto...)
buf = append(buf, `" `...)
buf = append(buf, strconv.Itoa(status)...)
buf = append(buf, " "...)
buf = append(buf, strconv.Itoa(size)...)
return buf
}
// writeLog writes a log entry for req to w in Apache Common Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeLog(writer io.Writer, params LogFormatterParams) {
buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)
buf = append(buf, '\n')
writer.Write(buf)
}
// writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
// ts is the timestamp with which the entry should be logged.
// status and size are used to provide the response HTTP status and size.
func writeCombinedLog(writer io.Writer, params LogFormatterParams) {
buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)
buf = append(buf, ` "`...)
buf = appendQuoted(buf, params.Request.Referer())
buf = append(buf, `" "`...)
buf = appendQuoted(buf, params.Request.UserAgent())
buf = append(buf, '"', '\n')
writer.Write(buf)
}
// CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Combined Log Format.
//
// See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
return loggingHandler{out, h, writeCombinedLog}
}
// LoggingHandler return a http.Handler that wraps h and logs requests to out in
// Apache Common Log Format (CLF).
//
// See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
//
// LoggingHandler always sets the ident field of the log to -
//
// Example:
//
// r := mux.NewRouter()
// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// w.Write([]byte("This is a catch-all route"))
// })
// loggedRouter := handlers.LoggingHandler(os.Stdout, r)
// http.ListenAndServe(":1123", loggedRouter)
//
func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
return loggingHandler{out, h, writeLog}
}
// CustomLoggingHandler provides a way to supply a custom log formatter
// while taking advantage of the mechanisms in this package
func CustomLoggingHandler(out io.Writer, h http.Handler, f LogFormatter) http.Handler {
return loggingHandler{out, h, f}
}
package handlers
import (
"net/http"
"regexp"
"strings"
)
var (
// De-facto standard header keys.
xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
xForwardedHost = http.CanonicalHeaderKey("X-Forwarded-Host")
xForwardedProto = http.CanonicalHeaderKey("X-Forwarded-Proto")
xForwardedScheme = http.CanonicalHeaderKey("X-Forwarded-Scheme")
xRealIP = http.CanonicalHeaderKey("X-Real-IP")
)
var (
// RFC7239 defines a new "Forwarded: " header designed to replace the
// existing use of X-Forwarded-* headers.
// e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43
forwarded = http.CanonicalHeaderKey("Forwarded")
// Allows for a sub-match of the first value after 'for=' to the next
// comma, semi-colon or space. The match is case-insensitive.
forRegex = regexp.MustCompile(`(?i)(?:for=)([^(;|,| )]+)`)
// Allows for a sub-match for the first instance of scheme (http|https)
// prefixed by 'proto='. The match is case-insensitive.
protoRegex = regexp.MustCompile(`(?i)(?:proto=)(https|http)`)
)
// ProxyHeaders inspects common reverse proxy headers and sets the corresponding
// fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP
// for the remote (client) IP address, X-Forwarded-Proto or X-Forwarded-Scheme
// for the scheme (http|https), X-Forwarded-Host for the host and the RFC7239
// Forwarded header, which may include both client IPs and schemes.
//
// NOTE: This middleware should only be used when behind a reverse
// proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are
// configured not to) strip these headers from client requests, or where these
// headers are accepted "as is" from a remote client (e.g. when Go is not behind
// a proxy), can manifest as a vulnerability if your application uses these
// headers for validating the 'trustworthiness' of a request.
func ProxyHeaders(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// Set the remote IP with the value passed from the proxy.
if fwd := getIP(r); fwd != "" {
r.RemoteAddr = fwd
}
// Set the scheme (proto) with the value passed from the proxy.
if scheme := getScheme(r); scheme != "" {
r.URL.Scheme = scheme
}
// Set the host with the value passed by the proxy
if r.Header.Get(xForwardedHost) != "" {
r.Host = r.Header.Get(xForwardedHost)
}
// Call the next handler in the chain.
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
// getIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239
// Forwarded headers (in that order).
func getIP(r *http.Request) string {
var addr string
if fwd := r.Header.Get(xForwardedFor); fwd != "" {
// Only grab the first (client) address. Note that '192.168.0.1,
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
// the first may represent forwarding proxies earlier in the chain.
s := strings.Index(fwd, ", ")
if s == -1 {
s = len(fwd)
}
addr = fwd[:s]
} else if fwd := r.Header.Get(xRealIP); fwd != "" {
// X-Real-IP should only contain one IP address (the client making the
// request).
addr = fwd
} else if fwd := r.Header.Get(forwarded); fwd != "" {
// match should contain at least two elements if the protocol was
// specified in the Forwarded header. The first element will always be
// the 'for=' capture, which we ignore. In the case of multiple IP
// addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only
// extract the first, which should be the client IP.
if match := forRegex.FindStringSubmatch(fwd); len(match) > 1 {
// IPv6 addresses in Forwarded headers are quoted-strings. We strip
// these quotes.
addr = strings.Trim(match[1], `"`)
}
}
return addr
}
// getScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239
// Forwarded headers (in that order).
func getScheme(r *http.Request) string {
var scheme string
// Retrieve the scheme from X-Forwarded-Proto.
if proto := r.Header.Get(xForwardedProto); proto != "" {
scheme = strings.ToLower(proto)
} else if proto = r.Header.Get(xForwardedScheme); proto != "" {
scheme = strings.ToLower(proto)
} else if proto = r.Header.Get(forwarded); proto != "" {
// match should contain at least two elements if the protocol was
// specified in the Forwarded header. The first element will always be
// the 'proto=' capture, which we ignore. In the case of multiple proto
// parameters (invalid) we only extract the first.
if match := protoRegex.FindStringSubmatch(proto); len(match) > 1 {
scheme = strings.ToLower(match[1])
}
}
return scheme
}
package handlers
import (
"log"
"net/http"
"runtime/debug"
)
// RecoveryHandlerLogger is an interface used by the recovering handler to print logs.
type RecoveryHandlerLogger interface {
Println(...interface{})
}
type recoveryHandler struct {
handler http.Handler
logger RecoveryHandlerLogger
printStack bool
}
// RecoveryOption provides a functional approach to define
// configuration for a handler; such as setting the logging
// whether or not to print stack traces on panic.
type RecoveryOption func(http.Handler)
func parseRecoveryOptions(h http.Handler, opts ...RecoveryOption) http.Handler {
for _, option := range opts {
option(h)
}
return h
}
// RecoveryHandler is HTTP middleware that recovers from a panic,
// logs the panic, writes http.StatusInternalServerError, and
// continues to the next handler.
//
// Example:
//
// r := mux.NewRouter()
// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// panic("Unexpected error!")
// })
//
// http.ListenAndServe(":1123", handlers.RecoveryHandler()(r))
func RecoveryHandler(opts ...RecoveryOption) func(h http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
r := &recoveryHandler{handler: h}
return parseRecoveryOptions(r, opts...)
}
}
// RecoveryLogger is a functional option to override
// the default logger
func RecoveryLogger(logger RecoveryHandlerLogger) RecoveryOption {
return func(h http.Handler) {
r := h.(*recoveryHandler)
r.logger = logger
}
}
// PrintRecoveryStack is a functional option to enable
// or disable printing stack traces on panic.
func PrintRecoveryStack(print bool) RecoveryOption {
return func(h http.Handler) {
r := h.(*recoveryHandler)
r.printStack = print
}
}
func (h recoveryHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
defer func() {
if err := recover(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
h.log(err)
}
}()
h.handler.ServeHTTP(w, req)
}
func (h recoveryHandler) log(v ...interface{}) {
if h.logger != nil {
h.logger.Println(v...)
} else {
log.Println(v...)
}
if h.printStack {
stack := string(debug.Stack())
if h.logger != nil {
h.logger.Println(stack)
} else {
log.Println(stack)
}
}
}
# Makefile for releasing. # Makefile for releasing.
# #
# The release is controlled from version.go. The version found there is # The release is controlled from version.go. The version found there is
# used to tag the git repo, we're not building any artifects so there is nothing # used to tag the git repo, we're not building any artifacts so there is nothing
# to upload to github. # to upload to github.
# #
# * Up the version in version.go # * Up the version in version.go
......
...@@ -73,6 +73,10 @@ A not-so-up-to-date-list-that-may-be-actually-current: ...@@ -73,6 +73,10 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/bodgit/tsig * https://github.com/bodgit/tsig
* https://github.com/v2fly/v2ray-core (test only) * https://github.com/v2fly/v2ray-core (test only)
* https://kuma.io/ * https://kuma.io/
* https://www.misaka.io/services/dns
* https://ping.sx/dig
* https://fleetdeck.io/
* https://github.com/markdingo/autoreverse
Send pull request if you want to be listed here. Send pull request if you want to be listed here.
...@@ -171,6 +175,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. ...@@ -171,6 +175,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 8080 - EdDSA for DNSSEC * 8080 - EdDSA for DNSSEC
* 8499 - DNS Terminology * 8499 - DNS Terminology
* 8659 - DNS Certification Authority Authorization (CAA) Resource Record * 8659 - DNS Certification Authority Authorization (CAA) Resource Record
* 8914 - Extended DNS Errors
* 8976 - Message Digest for DNS Zones (ZONEMD RR) * 8976 - Message Digest for DNS Zones (ZONEMD RR)
## Loosely Based Upon ## Loosely Based Upon
......
...@@ -12,7 +12,7 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction ...@@ -12,7 +12,7 @@ type MsgAcceptFunc func(dh Header) MsgAcceptAction
// //
// * Zero bit isn't zero // * Zero bit isn't zero
// //
// * has more than 1 question in the question section // * does not have exactly 1 question in the question section
// //
// * has more than 1 RR in the Answer section // * has more than 1 RR in the Answer section
// //
...@@ -25,6 +25,7 @@ var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc ...@@ -25,6 +25,7 @@ var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc
// MsgAcceptAction represents the action to be taken. // MsgAcceptAction represents the action to be taken.
type MsgAcceptAction int type MsgAcceptAction int
// Allowed returned values from a MsgAcceptFunc.
const ( const (
MsgAccept MsgAcceptAction = iota // Accept the message MsgAccept MsgAcceptAction = iota // Accept the message
MsgReject // Reject the message with a RcodeFormatError MsgReject // Reject the message with a RcodeFormatError
......
...@@ -18,6 +18,18 @@ const ( ...@@ -18,6 +18,18 @@ const (
tcpIdleTimeout time.Duration = 8 * time.Second tcpIdleTimeout time.Duration = 8 * time.Second
) )
func isPacketConn(c net.Conn) bool {
if _, ok := c.(net.PacketConn); !ok {
return false
}
if ua, ok := c.LocalAddr().(*net.UnixAddr); ok {
return ua.Net == "unixgram" || ua.Net == "unixpacket"
}
return true
}
// A Conn represents a connection to a DNS server. // A Conn represents a connection to a DNS server.
type Conn struct { type Conn struct {
net.Conn // a net.Conn holding the connection net.Conn // a net.Conn holding the connection
...@@ -27,6 +39,14 @@ type Conn struct { ...@@ -27,6 +39,14 @@ type Conn struct {
tsigRequestMAC string tsigRequestMAC string
} }
func (co *Conn) tsigProvider() TsigProvider {
if co.TsigProvider != nil {
return co.TsigProvider
}
// tsigSecretProvider will return ErrSecret if co.TsigSecret is nil.
return tsigSecretProvider(co.TsigSecret)
}
// A Client defines parameters for a DNS client. // A Client defines parameters for a DNS client.
type Client struct { type Client struct {
Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP)
...@@ -82,6 +102,12 @@ func (c *Client) writeTimeout() time.Duration { ...@@ -82,6 +102,12 @@ func (c *Client) writeTimeout() time.Duration {
// Dial connects to the address on the named network. // Dial connects to the address on the named network.
func (c *Client) Dial(address string) (conn *Conn, err error) { func (c *Client) Dial(address string) (conn *Conn, err error) {
return c.DialContext(context.Background(), address)
}
// DialContext connects to the address on the named network, with a context.Context.
// For TLS over TCP (DoT) the context isn't used yet. This will be enabled when Go 1.18 is released.
func (c *Client) DialContext(ctx context.Context, address string) (conn *Conn, err error) {
// create a new dialer with the appropriate timeout // create a new dialer with the appropriate timeout
var d net.Dialer var d net.Dialer
if c.Dialer == nil { if c.Dialer == nil {
...@@ -101,9 +127,17 @@ func (c *Client) Dial(address string) (conn *Conn, err error) { ...@@ -101,9 +127,17 @@ func (c *Client) Dial(address string) (conn *Conn, err error) {
if useTLS { if useTLS {
network = strings.TrimSuffix(network, "-tls") network = strings.TrimSuffix(network, "-tls")
// TODO(miekg): Enable after Go 1.18 is released, to be able to support two prev. releases.
/*
tlsDialer := tls.Dialer{
NetDialer: &d,
Config: c.TLSConfig,
}
conn.Conn, err = tlsDialer.DialContext(ctx, network, address)
*/
conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig) conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig)
} else { } else {
conn.Conn, err = d.Dial(network, address) conn.Conn, err = d.DialContext(ctx, network, address)
} }
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -139,6 +173,7 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er ...@@ -139,6 +173,7 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er
// ExchangeWithConn has the same behavior as Exchange, just with a predetermined connection // ExchangeWithConn has the same behavior as Exchange, just with a predetermined connection
// that will be used instead of creating a new one. // that will be used instead of creating a new one.
// Usage pattern with a *dns.Client: // Usage pattern with a *dns.Client:
//
// c := new(dns.Client) // c := new(dns.Client)
// // connection management logic goes here // // connection management logic goes here
// //
...@@ -148,15 +183,24 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er ...@@ -148,15 +183,24 @@ func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, er
// This allows users of the library to implement their own connection management, // This allows users of the library to implement their own connection management,
// as opposed to Exchange, which will always use new connections and incur the added overhead // as opposed to Exchange, which will always use new connections and incur the added overhead
// that entails when using "tcp" and especially "tcp-tls" clients. // that entails when using "tcp" and especially "tcp-tls" clients.
//
// When the singleflight is set for this client the context is _not_ forwarded to the (shared) exchange, to
// prevent one cancelation from canceling all outstanding requests.
func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) { func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) {
return c.exchangeWithConnContext(context.Background(), m, conn)
}
func (c *Client) exchangeWithConnContext(ctx context.Context, m *Msg, conn *Conn) (r *Msg, rtt time.Duration, err error) {
if !c.SingleInflight { if !c.SingleInflight {
return c.exchange(m, conn) return c.exchangeContext(ctx, m, conn)
} }
q := m.Question[0] q := m.Question[0]
key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass) key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass)
r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) { r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) {
return c.exchange(m, conn) // When we're doing singleflight we don't want one context cancelation, cancel _all_ outstanding queries.
// Hence we ignore the context and use Background().
return c.exchangeContext(context.Background(), m, conn)
}) })
if r != nil && shared { if r != nil && shared {
r = r.Copy() r = r.Copy()
...@@ -165,8 +209,7 @@ func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration ...@@ -165,8 +209,7 @@ func (c *Client) ExchangeWithConn(m *Msg, conn *Conn) (r *Msg, rtt time.Duration
return r, rtt, err return r, rtt, err
} }
func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) { func (c *Client) exchangeContext(ctx context.Context, m *Msg, co *Conn) (r *Msg, rtt time.Duration, err error) {
opt := m.IsEdns0() opt := m.IsEdns0()
// If EDNS0 is used use that for size. // If EDNS0 is used use that for size.
if opt != nil && opt.UDPSize() >= MinMsgSize { if opt != nil && opt.UDPSize() >= MinMsgSize {
...@@ -177,16 +220,28 @@ func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err erro ...@@ -177,16 +220,28 @@ func (c *Client) exchange(m *Msg, co *Conn) (r *Msg, rtt time.Duration, err erro
co.UDPSize = c.UDPSize co.UDPSize = c.UDPSize
} }
co.TsigSecret, co.TsigProvider = c.TsigSecret, c.TsigProvider
t := time.Now()
// write with the appropriate write timeout // write with the appropriate write timeout
co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout()))) t := time.Now()
writeDeadline := t.Add(c.getTimeoutForRequest(c.writeTimeout()))
readDeadline := t.Add(c.getTimeoutForRequest(c.readTimeout()))
if deadline, ok := ctx.Deadline(); ok {
if deadline.Before(writeDeadline) {
writeDeadline = deadline
}
if deadline.Before(readDeadline) {
readDeadline = deadline
}
}
co.SetWriteDeadline(writeDeadline)
co.SetReadDeadline(readDeadline)
co.TsigSecret, co.TsigProvider = c.TsigSecret, c.TsigProvider
if err = co.WriteMsg(m); err != nil { if err = co.WriteMsg(m); err != nil {
return nil, 0, err return nil, 0, err
} }
co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout()))) if isPacketConn(co.Conn) {
if _, ok := co.Conn.(net.PacketConn); ok {
for { for {
r, err = co.ReadMsg() r, err = co.ReadMsg()
// Ignore replies with mismatched IDs because they might be // Ignore replies with mismatched IDs because they might be
...@@ -224,15 +279,8 @@ func (co *Conn) ReadMsg() (*Msg, error) { ...@@ -224,15 +279,8 @@ func (co *Conn) ReadMsg() (*Msg, error) {
return m, err return m, err
} }
if t := m.IsTsig(); t != nil { if t := m.IsTsig(); t != nil {
if co.TsigProvider != nil {
err = tsigVerifyProvider(p, co.TsigProvider, co.tsigRequestMAC, false)
} else {
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
return m, ErrSecret
}
// Need to work on the original message p, as that was used to calculate the tsig. // Need to work on the original message p, as that was used to calculate the tsig.
err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) err = TsigVerifyWithProvider(p, co.tsigProvider(), co.tsigRequestMAC, false)
}
} }
return m, err return m, err
} }
...@@ -247,7 +295,7 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { ...@@ -247,7 +295,7 @@ func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
err error err error
) )
if _, ok := co.Conn.(net.PacketConn); ok { if isPacketConn(co.Conn) {
if co.UDPSize > MinMsgSize { if co.UDPSize > MinMsgSize {
p = make([]byte, co.UDPSize) p = make([]byte, co.UDPSize)
} else { } else {
...@@ -287,7 +335,7 @@ func (co *Conn) Read(p []byte) (n int, err error) { ...@@ -287,7 +335,7 @@ func (co *Conn) Read(p []byte) (n int, err error) {
return 0, ErrConnEmpty return 0, ErrConnEmpty
} }
if _, ok := co.Conn.(net.PacketConn); ok { if isPacketConn(co.Conn) {
// UDP connection // UDP connection
return co.Conn.Read(p) return co.Conn.Read(p)
} }
...@@ -309,17 +357,8 @@ func (co *Conn) Read(p []byte) (n int, err error) { ...@@ -309,17 +357,8 @@ func (co *Conn) Read(p []byte) (n int, err error) {
func (co *Conn) WriteMsg(m *Msg) (err error) { func (co *Conn) WriteMsg(m *Msg) (err error) {
var out []byte var out []byte
if t := m.IsTsig(); t != nil { if t := m.IsTsig(); t != nil {
mac := "" // Set tsigRequestMAC for the next read, although only used in zone transfers.
if co.TsigProvider != nil { out, co.tsigRequestMAC, err = TsigGenerateWithProvider(m, co.tsigProvider(), co.tsigRequestMAC, false)
out, mac, err = tsigGenerateProvider(m, co.TsigProvider, co.tsigRequestMAC, false)
} else {
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
return ErrSecret
}
out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false)
}
// Set for the next read, although only used in zone transfers
co.tsigRequestMAC = mac
} else { } else {
out, err = m.Pack() out, err = m.Pack()
} }
...@@ -336,7 +375,7 @@ func (co *Conn) Write(p []byte) (int, error) { ...@@ -336,7 +375,7 @@ func (co *Conn) Write(p []byte) (int, error) {
return 0, &Error{err: "message too large"} return 0, &Error{err: "message too large"}
} }
if _, ok := co.Conn.(net.PacketConn); ok { if isPacketConn(co.Conn) {
return co.Conn.Write(p) return co.Conn.Write(p)
} }
...@@ -379,7 +418,7 @@ func Dial(network, address string) (conn *Conn, err error) { ...@@ -379,7 +418,7 @@ func Dial(network, address string) (conn *Conn, err error) {
func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) { func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {
client := Client{Net: "udp"} client := Client{Net: "udp"}
r, _, err = client.ExchangeContext(ctx, m, a) r, _, err = client.ExchangeContext(ctx, m, a)
// ignorint rtt to leave the original ExchangeContext API unchanged, but // ignoring rtt to leave the original ExchangeContext API unchanged, but
// this function will go away // this function will go away
return r, err return r, err
} }
...@@ -435,15 +474,11 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout ...@@ -435,15 +474,11 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout
// context, if present. If there is both a context deadline and a configured // context, if present. If there is both a context deadline and a configured
// timeout on the client, the earliest of the two takes effect. // timeout on the client, the earliest of the two takes effect.
func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) { func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) {
var timeout time.Duration conn, err := c.DialContext(ctx, a)
if deadline, ok := ctx.Deadline(); !ok { if err != nil {
timeout = 0 return nil, 0, err
} else {
timeout = time.Until(deadline)
} }
// not passing the context to the underlying calls, as the API does not support defer conn.Close()
// context. For timeouts you should set up Client.Dialer and call Client.Exchange.
// TODO(tmthrgd,miekg): this is a race condition. return c.exchangeWithConnContext(ctx, m, conn)
c.Dialer = &net.Dialer{Timeout: timeout}
return c.Exchange(m, a)
} }
...@@ -218,6 +218,11 @@ func IsDomainName(s string) (labels int, ok bool) { ...@@ -218,6 +218,11 @@ func IsDomainName(s string) (labels int, ok bool) {
wasDot = false wasDot = false
case '.': case '.':
if i == 0 && len(s) > 1 {
// leading dots are not legal except for the root zone
return labels, false
}
if wasDot { if wasDot {
// two dots back to back is not legal // two dots back to back is not legal
return labels, false return labels, false
...@@ -349,10 +354,7 @@ func ReverseAddr(addr string) (arpa string, err error) { ...@@ -349,10 +354,7 @@ func ReverseAddr(addr string) (arpa string, err error) {
// Add it, in reverse, to the buffer // Add it, in reverse, to the buffer
for i := len(ip) - 1; i >= 0; i-- { for i := len(ip) - 1; i >= 0; i-- {
v := ip[i] v := ip[i]
buf = append(buf, hexDigit[v&0xF]) buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.')
buf = append(buf, '.')
buf = append(buf, hexDigit[v>>4])
buf = append(buf, '.')
} }
// Append "ip6.arpa." and return (buf already has the final .) // Append "ip6.arpa." and return (buf already has the final .)
buf = append(buf, "ip6.arpa."...) buf = append(buf, "ip6.arpa."...)
......
...@@ -4,12 +4,13 @@ import ( ...@@ -4,12 +4,13 @@ import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
_ "crypto/sha1" _ "crypto/sha1" // need its init function
_ "crypto/sha256" _ "crypto/sha256" // need its init function
_ "crypto/sha512" _ "crypto/sha512" // need its init function
"encoding/asn1" "encoding/asn1"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
...@@ -17,8 +18,6 @@ import ( ...@@ -17,8 +18,6 @@ import (
"sort" "sort"
"strings" "strings"
"time" "time"
"golang.org/x/crypto/ed25519"
) )
// DNSSEC encryption algorithm codes. // DNSSEC encryption algorithm codes.
...@@ -66,6 +65,9 @@ var AlgorithmToString = map[uint8]string{ ...@@ -66,6 +65,9 @@ var AlgorithmToString = map[uint8]string{
} }
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's. // AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
// For newer algorithm that do their own hashing (i.e. ED25519) the returned value
// is 0, implying no (external) hashing should occur. The non-exported identityHash is then
// used.
var AlgorithmToHash = map[uint8]crypto.Hash{ var AlgorithmToHash = map[uint8]crypto.Hash{
RSAMD5: crypto.MD5, // Deprecated in RFC 6725 RSAMD5: crypto.MD5, // Deprecated in RFC 6725
DSA: crypto.SHA1, DSA: crypto.SHA1,
...@@ -75,7 +77,7 @@ var AlgorithmToHash = map[uint8]crypto.Hash{ ...@@ -75,7 +77,7 @@ var AlgorithmToHash = map[uint8]crypto.Hash{
ECDSAP256SHA256: crypto.SHA256, ECDSAP256SHA256: crypto.SHA256,
ECDSAP384SHA384: crypto.SHA384, ECDSAP384SHA384: crypto.SHA384,
RSASHA512: crypto.SHA512, RSASHA512: crypto.SHA512,
ED25519: crypto.Hash(0), ED25519: 0,
} }
// DNSSEC hashing algorithm codes. // DNSSEC hashing algorithm codes.
...@@ -138,12 +140,12 @@ func (k *DNSKEY) KeyTag() uint16 { ...@@ -138,12 +140,12 @@ func (k *DNSKEY) KeyTag() uint16 {
var keytag int var keytag int
switch k.Algorithm { switch k.Algorithm {
case RSAMD5: case RSAMD5:
// Look at the bottom two bytes of the modules, which the last
// item in the pubkey.
// This algorithm has been deprecated, but keep this key-tag calculation. // This algorithm has been deprecated, but keep this key-tag calculation.
// Look at the bottom two bytes of the modules, which the last item in the pubkey.
// See https://www.rfc-editor.org/errata/eid193 .
modulus, _ := fromBase64([]byte(k.PublicKey)) modulus, _ := fromBase64([]byte(k.PublicKey))
if len(modulus) > 1 { if len(modulus) > 1 {
x := binary.BigEndian.Uint16(modulus[len(modulus)-2:]) x := binary.BigEndian.Uint16(modulus[len(modulus)-3:])
keytag = int(x) keytag = int(x)
} }
default: default:
...@@ -297,35 +299,20 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error { ...@@ -297,35 +299,20 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
return err return err
} }
hash, ok := AlgorithmToHash[rr.Algorithm] h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
if !ok {
return ErrAlg
}
switch rr.Algorithm {
case ED25519:
// ed25519 signs the raw message and performs hashing internally.
// All other supported signature schemes operate over the pre-hashed
// message, and thus ed25519 must be handled separately here.
//
// The raw message is passed directly into sign and crypto.Hash(0) is
// used to signal to the crypto.Signer that the data has not been hashed.
signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm)
if err != nil { if err != nil {
return err return err
} }
rr.Signature = toBase64(signature) switch rr.Algorithm {
return nil
case RSAMD5, DSA, DSANSEC3SHA1: case RSAMD5, DSA, DSANSEC3SHA1:
// See RFC 6944. // See RFC 6944.
return ErrAlg return ErrAlg
default: default:
h := hash.New()
h.Write(signdata) h.Write(signdata)
h.Write(wire) h.Write(wire)
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm) signature, err := sign(k, h.Sum(nil), cryptohash, rr.Algorithm)
if err != nil { if err != nil {
return err return err
} }
...@@ -342,7 +329,7 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, ...@@ -342,7 +329,7 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
} }
switch alg { switch alg {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512: case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, ED25519:
return signature, nil return signature, nil
case ECDSAP256SHA256, ECDSAP384SHA384: case ECDSAP256SHA256, ECDSAP384SHA384:
ecdsaSignature := &struct { ecdsaSignature := &struct {
...@@ -363,8 +350,6 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, ...@@ -363,8 +350,6 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
signature := intToBytes(ecdsaSignature.R, intlen) signature := intToBytes(ecdsaSignature.R, intlen)
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...) signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
return signature, nil return signature, nil
case ED25519:
return signature, nil
default: default:
return nil, ErrAlg return nil, ErrAlg
} }
...@@ -373,6 +358,8 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, ...@@ -373,6 +358,8 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
// Verify validates an RRSet with the signature and key. This is only the // Verify validates an RRSet with the signature and key. This is only the
// cryptographic test, the signature validity period must be checked separately. // cryptographic test, the signature validity period must be checked separately.
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work. // This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
// It also checks that the Zone Key bit (RFC 4034 2.1.1) is set on the DNSKEY
// and that the Protocol field is set to 3 (RFC 4034 2.1.2).
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// First the easy checks // First the easy checks
if !IsRRset(rrset) { if !IsRRset(rrset) {
...@@ -393,6 +380,12 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { ...@@ -393,6 +380,12 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
if k.Protocol != 3 { if k.Protocol != 3 {
return ErrKey return ErrKey
} }
// RFC 4034 2.1.1 If bit 7 has value 0, then the DNSKEY record holds some
// other type of DNS public key and MUST NOT be used to verify RRSIGs that
// cover RRsets.
if k.Flags&ZONE == 0 {
return ErrKey
}
// IsRRset checked that we have at least one RR and that the RRs in // IsRRset checked that we have at least one RR and that the RRs in
// the set have consistent type, class, and name. Also check that type and // the set have consistent type, class, and name. Also check that type and
...@@ -430,9 +423,9 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { ...@@ -430,9 +423,9 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// remove the domain name and assume its ours? // remove the domain name and assume its ours?
} }
hash, ok := AlgorithmToHash[rr.Algorithm] h, cryptohash, err := hashFromAlgorithm(rr.Algorithm)
if !ok { if err != nil {
return ErrAlg return err
} }
switch rr.Algorithm { switch rr.Algorithm {
...@@ -443,10 +436,9 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { ...@@ -443,10 +436,9 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
return ErrKey return ErrKey
} }
h := hash.New()
h.Write(signeddata) h.Write(signeddata)
h.Write(wire) h.Write(wire)
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf) return rsa.VerifyPKCS1v15(pubkey, cryptohash, h.Sum(nil), sigbuf)
case ECDSAP256SHA256, ECDSAP384SHA384: case ECDSAP256SHA256, ECDSAP384SHA384:
pubkey := k.publicKeyECDSA() pubkey := k.publicKeyECDSA()
...@@ -458,7 +450,6 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { ...@@ -458,7 +450,6 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2]) r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:]) s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
h := hash.New()
h.Write(signeddata) h.Write(signeddata)
h.Write(wire) h.Write(wire)
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) { if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
...@@ -500,7 +491,7 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool { ...@@ -500,7 +491,7 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
return ti <= utc && utc <= te return ti <= utc && utc <= te
} }
// Return the signatures base64 encodedig sigdata as a byte slice. // Return the signatures base64 encoding sigdata as a byte slice.
func (rr *RRSIG) sigBuf() []byte { func (rr *RRSIG) sigBuf() []byte {
sigbuf, err := fromBase64([]byte(rr.Signature)) sigbuf, err := fromBase64([]byte(rr.Signature))
if err != nil { if err != nil {
......
...@@ -3,12 +3,11 @@ package dns ...@@ -3,12 +3,11 @@ package dns
import ( import (
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"math/big" "math/big"
"golang.org/x/crypto/ed25519"
) )
// Generate generates a DNSKEY of the given bit size. // Generate generates a DNSKEY of the given bit size.
......
...@@ -4,13 +4,12 @@ import ( ...@@ -4,13 +4,12 @@ import (
"bufio" "bufio"
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"io" "io"
"math/big" "math/big"
"strconv" "strconv"
"strings" "strings"
"golang.org/x/crypto/ed25519"
) )
// NewPrivateKey returns a PrivateKey by parsing the string s. // NewPrivateKey returns a PrivateKey by parsing the string s.
......
...@@ -3,11 +3,10 @@ package dns ...@@ -3,11 +3,10 @@ package dns
import ( import (
"crypto" "crypto"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"math/big" "math/big"
"strconv" "strconv"
"golang.org/x/crypto/ed25519"
) )
const format = "Private-key-format: v1.3\n" const format = "Private-key-format: v1.3\n"
......
...@@ -159,7 +159,7 @@ shows the options you have and what functions to call. ...@@ -159,7 +159,7 @@ shows the options you have and what functions to call.
TRANSACTION SIGNATURE TRANSACTION SIGNATURE
An TSIG or transaction signature adds a HMAC TSIG record to each message sent. An TSIG or transaction signature adds a HMAC TSIG record to each message sent.
The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512. The supported algorithms include: HmacSHA1, HmacSHA256 and HmacSHA512.
Basic use pattern when querying with a TSIG name "axfr." (note that these key names Basic use pattern when querying with a TSIG name "axfr." (note that these key names
must be fully qualified - as they are domain names) and the base64 secret must be fully qualified - as they are domain names) and the base64 secret
...@@ -174,7 +174,7 @@ changes to the RRset after calling SetTsig() the signature will be incorrect. ...@@ -174,7 +174,7 @@ changes to the RRset after calling SetTsig() the signature will be incorrect.
c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
m := new(dns.Msg) m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX) m.SetQuestion("miek.nl.", dns.TypeMX)
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix())
... ...
// When sending the TSIG RR is calculated and filled in before sending // When sending the TSIG RR is calculated and filled in before sending
...@@ -187,7 +187,7 @@ request an AXFR for miek.nl. with TSIG key named "axfr." and secret ...@@ -187,7 +187,7 @@ request an AXFR for miek.nl. with TSIG key named "axfr." and secret
m := new(dns.Msg) m := new(dns.Msg)
t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
m.SetAxfr("miek.nl.") m.SetAxfr("miek.nl.")
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix())
c, err := t.In(m, "176.58.119.54:53") c, err := t.In(m, "176.58.119.54:53")
for r := range c { ... } for r := range c { ... }
...@@ -214,7 +214,7 @@ client must be configured with an implementation of the TsigProvider interface: ...@@ -214,7 +214,7 @@ client must be configured with an implementation of the TsigProvider interface:
c.TsigProvider = new(Provider) c.TsigProvider = new(Provider)
m := new(dns.Msg) m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX) m.SetQuestion("miek.nl.", dns.TypeMX)
m.SetTsig(keyname, dns.HmacSHA1, 300, time.Now().Unix()) m.SetTsig(keyname, dns.HmacSHA256, 300, time.Now().Unix())
... ...
// TSIG RR is calculated by calling your Generate method // TSIG RR is calculated by calling your Generate method
...@@ -231,7 +231,7 @@ Basic use pattern validating and replying to a message that has TSIG set. ...@@ -231,7 +231,7 @@ Basic use pattern validating and replying to a message that has TSIG set.
if r.IsTsig() != nil { if r.IsTsig() != nil {
if w.TsigStatus() == nil { if w.TsigStatus() == nil {
// *Msg r has an TSIG record and it was validated // *Msg r has an TSIG record and it was validated
m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) m.SetTsig("axfr.", dns.HmacSHA256, 300, time.Now().Unix())
} else { } else {
// *Msg r has an TSIG records and it was not validated // *Msg r has an TSIG records and it was not validated
} }
...@@ -251,7 +251,7 @@ information. ...@@ -251,7 +251,7 @@ information.
EDNS0 EDNS0
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by
RFC 6891. It defines an new RR type, the OPT RR, which is then completely RFC 6891. It defines a new RR type, the OPT RR, which is then completely
abused. abused.
Basic use pattern for creating an (empty) OPT RR: Basic use pattern for creating an (empty) OPT RR:
......
...@@ -14,6 +14,7 @@ const ( ...@@ -14,6 +14,7 @@ const (
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
EDNS0NSID = 0x3 // nsid (See RFC 5001) EDNS0NSID = 0x3 // nsid (See RFC 5001)
EDNS0ESU = 0x4 // ENUM Source-URI draft: https://datatracker.ietf.org/doc/html/draft-kaplan-enum-source-uri-00
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
EDNS0DHU = 0x6 // DS Hash Understood EDNS0DHU = 0x6 // DS Hash Understood
EDNS0N3U = 0x7 // NSEC3 Hash Understood EDNS0N3U = 0x7 // NSEC3 Hash Understood
...@@ -22,11 +23,49 @@ const ( ...@@ -22,11 +23,49 @@ const (
EDNS0COOKIE = 0xa // EDNS0 Cookie EDNS0COOKIE = 0xa // EDNS0 Cookie
EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828) EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828)
EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830) EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830)
EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914)
EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891) EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891)
EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891) EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891)
_DO = 1 << 15 // DNSSEC OK _DO = 1 << 15 // DNSSEC OK
) )
// makeDataOpt is used to unpack the EDNS0 option(s) from a message.
func makeDataOpt(code uint16) EDNS0 {
// All the EDNS0.* constants above need to be in this switch.
switch code {
case EDNS0LLQ:
return new(EDNS0_LLQ)
case EDNS0UL:
return new(EDNS0_UL)
case EDNS0NSID:
return new(EDNS0_NSID)
case EDNS0DAU:
return new(EDNS0_DAU)
case EDNS0DHU:
return new(EDNS0_DHU)
case EDNS0N3U:
return new(EDNS0_N3U)
case EDNS0SUBNET:
return new(EDNS0_SUBNET)
case EDNS0EXPIRE:
return new(EDNS0_EXPIRE)
case EDNS0COOKIE:
return new(EDNS0_COOKIE)
case EDNS0TCPKEEPALIVE:
return new(EDNS0_TCP_KEEPALIVE)
case EDNS0PADDING:
return new(EDNS0_PADDING)
case EDNS0EDE:
return new(EDNS0_EDE)
case EDNS0ESU:
return &EDNS0_ESU{Code: EDNS0ESU}
default:
e := new(EDNS0_LOCAL)
e.Code = code
return e
}
}
// OPT is the EDNS0 RR appended to messages to convey extra (meta) information. // OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
// See RFC 6891. // See RFC 6891.
type OPT struct { type OPT struct {
...@@ -59,6 +98,8 @@ func (rr *OPT) String() string { ...@@ -59,6 +98,8 @@ func (rr *OPT) String() string {
s += "\n; SUBNET: " + o.String() s += "\n; SUBNET: " + o.String()
case *EDNS0_COOKIE: case *EDNS0_COOKIE:
s += "\n; COOKIE: " + o.String() s += "\n; COOKIE: " + o.String()
case *EDNS0_TCP_KEEPALIVE:
s += "\n; KEEPALIVE: " + o.String()
case *EDNS0_UL: case *EDNS0_UL:
s += "\n; UPDATE LEASE: " + o.String() s += "\n; UPDATE LEASE: " + o.String()
case *EDNS0_LLQ: case *EDNS0_LLQ:
...@@ -73,6 +114,10 @@ func (rr *OPT) String() string { ...@@ -73,6 +114,10 @@ func (rr *OPT) String() string {
s += "\n; LOCAL OPT: " + o.String() s += "\n; LOCAL OPT: " + o.String()
case *EDNS0_PADDING: case *EDNS0_PADDING:
s += "\n; PADDING: " + o.String() s += "\n; PADDING: " + o.String()
case *EDNS0_EDE:
s += "\n; EDE: " + o.String()
case *EDNS0_ESU:
s += "\n; ESU: " + o.String()
} }
} }
return s return s
...@@ -92,7 +137,7 @@ func (*OPT) parse(c *zlexer, origin string) *ParseError { ...@@ -92,7 +137,7 @@ func (*OPT) parse(c *zlexer, origin string) *ParseError {
return &ParseError{err: "OPT records do not have a presentation format"} return &ParseError{err: "OPT records do not have a presentation format"}
} }
func (r1 *OPT) isDuplicate(r2 RR) bool { return false } func (rr *OPT) isDuplicate(r2 RR) bool { return false }
// return the old value -> delete SetVersion? // return the old value -> delete SetVersion?
...@@ -148,6 +193,16 @@ func (rr *OPT) SetDo(do ...bool) { ...@@ -148,6 +193,16 @@ func (rr *OPT) SetDo(do ...bool) {
} }
} }
// Z returns the Z part of the OPT RR as a uint16 with only the 15 least significant bits used.
func (rr *OPT) Z() uint16 {
return uint16(rr.Hdr.Ttl & 0x7FFF)
}
// SetZ sets the Z part of the OPT RR, note only the 15 least significant bits of z are used.
func (rr *OPT) SetZ(z uint16) {
rr.Hdr.Ttl = rr.Hdr.Ttl&^0x7FFF | uint32(z&0x7FFF)
}
// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it. // EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it.
type EDNS0 interface { type EDNS0 interface {
// Option returns the option code for the option. // Option returns the option code for the option.
...@@ -452,7 +507,7 @@ func (e *EDNS0_LLQ) copy() EDNS0 { ...@@ -452,7 +507,7 @@ func (e *EDNS0_LLQ) copy() EDNS0 {
return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife} return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife}
} }
// EDNS0_DUA implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975. // EDNS0_DAU implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975.
type EDNS0_DAU struct { type EDNS0_DAU struct {
Code uint16 // Always EDNS0DAU Code uint16 // Always EDNS0DAU
AlgCode []uint8 AlgCode []uint8
...@@ -525,18 +580,21 @@ func (e *EDNS0_N3U) String() string { ...@@ -525,18 +580,21 @@ func (e *EDNS0_N3U) String() string {
} }
func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} } func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314. // EDNS0_EXPIRE implements the EDNS0 option as described in RFC 7314.
type EDNS0_EXPIRE struct { type EDNS0_EXPIRE struct {
Code uint16 // Always EDNS0EXPIRE Code uint16 // Always EDNS0EXPIRE
Expire uint32 Expire uint32
Empty bool // Empty is used to signal an empty Expire option in a backwards compatible way, it's not used on the wire.
} }
// Option implements the EDNS0 interface. // Option implements the EDNS0 interface.
func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE } func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE }
func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) } func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire, e.Empty} }
func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire} }
func (e *EDNS0_EXPIRE) pack() ([]byte, error) { func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
if e.Empty {
return []byte{}, nil
}
b := make([]byte, 4) b := make([]byte, 4)
binary.BigEndian.PutUint32(b, e.Expire) binary.BigEndian.PutUint32(b, e.Expire)
return b, nil return b, nil
...@@ -545,15 +603,24 @@ func (e *EDNS0_EXPIRE) pack() ([]byte, error) { ...@@ -545,15 +603,24 @@ func (e *EDNS0_EXPIRE) pack() ([]byte, error) {
func (e *EDNS0_EXPIRE) unpack(b []byte) error { func (e *EDNS0_EXPIRE) unpack(b []byte) error {
if len(b) == 0 { if len(b) == 0 {
// zero-length EXPIRE query, see RFC 7314 Section 2 // zero-length EXPIRE query, see RFC 7314 Section 2
e.Empty = true
return nil return nil
} }
if len(b) < 4 { if len(b) < 4 {
return ErrBuf return ErrBuf
} }
e.Expire = binary.BigEndian.Uint32(b) e.Expire = binary.BigEndian.Uint32(b)
e.Empty = false
return nil return nil
} }
func (e *EDNS0_EXPIRE) String() (s string) {
if e.Empty {
return ""
}
return strconv.FormatUint(uint64(e.Expire), 10)
}
// The EDNS0_LOCAL option is used for local/experimental purposes. The option // The EDNS0_LOCAL option is used for local/experimental purposes. The option
// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND] // code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
// (RFC6891), although any unassigned code can actually be used. The content of // (RFC6891), although any unassigned code can actually be used. The content of
...@@ -605,56 +672,51 @@ func (e *EDNS0_LOCAL) unpack(b []byte) error { ...@@ -605,56 +672,51 @@ func (e *EDNS0_LOCAL) unpack(b []byte) error {
// the TCP connection alive. See RFC 7828. // the TCP connection alive. See RFC 7828.
type EDNS0_TCP_KEEPALIVE struct { type EDNS0_TCP_KEEPALIVE struct {
Code uint16 // Always EDNSTCPKEEPALIVE Code uint16 // Always EDNSTCPKEEPALIVE
Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present;
Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order. // Timeout is an idle timeout value for the TCP connection, specified in
// units of 100 milliseconds, encoded in network byte order. If set to 0,
// pack will return a nil slice.
Timeout uint16
// Length is the option's length.
// Deprecated: this field is deprecated and is always equal to 0.
Length uint16
} }
// Option implements the EDNS0 interface. // Option implements the EDNS0 interface.
func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE } func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE }
func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) { func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) {
if e.Timeout != 0 && e.Length != 2 { if e.Timeout > 0 {
return nil, errors.New("dns: timeout specified but length is not 2") b := make([]byte, 2)
} binary.BigEndian.PutUint16(b, e.Timeout)
if e.Timeout == 0 && e.Length != 0 {
return nil, errors.New("dns: timeout not specified but length is not 0")
}
b := make([]byte, 4+e.Length)
binary.BigEndian.PutUint16(b[0:], e.Code)
binary.BigEndian.PutUint16(b[2:], e.Length)
if e.Length == 2 {
binary.BigEndian.PutUint16(b[4:], e.Timeout)
}
return b, nil return b, nil
} }
return nil, nil
}
func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error { func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error {
if len(b) < 4 { switch len(b) {
return ErrBuf case 0:
} case 2:
e.Length = binary.BigEndian.Uint16(b[2:4]) e.Timeout = binary.BigEndian.Uint16(b)
if e.Length != 0 && e.Length != 2 { default:
return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10)) return fmt.Errorf("dns: length mismatch, want 0/2 but got %d", len(b))
}
if e.Length == 2 {
if len(b) < 6 {
return ErrBuf
}
e.Timeout = binary.BigEndian.Uint16(b[4:6])
} }
return nil return nil
} }
func (e *EDNS0_TCP_KEEPALIVE) String() (s string) { func (e *EDNS0_TCP_KEEPALIVE) String() string {
s = "use tcp keep-alive" s := "use tcp keep-alive"
if e.Length == 0 { if e.Timeout == 0 {
s += ", timeout omitted" s += ", timeout omitted"
} else { } else {
s += fmt.Sprintf(", timeout %dms", e.Timeout*100) s += fmt.Sprintf(", timeout %dms", e.Timeout*100)
} }
return return s
} }
func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Length, e.Timeout} }
func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Timeout, e.Length} }
// EDNS0_PADDING option is used to add padding to a request/response. The default // EDNS0_PADDING option is used to add padding to a request/response. The default
// value of padding SHOULD be 0x0 but other values MAY be used, for instance if // value of padding SHOULD be 0x0 but other values MAY be used, for instance if
...@@ -673,3 +735,117 @@ func (e *EDNS0_PADDING) copy() EDNS0 { ...@@ -673,3 +735,117 @@ func (e *EDNS0_PADDING) copy() EDNS0 {
copy(b, e.Padding) copy(b, e.Padding)
return &EDNS0_PADDING{b} return &EDNS0_PADDING{b}
} }
// Extended DNS Error Codes (RFC 8914).
const (
ExtendedErrorCodeOther uint16 = iota
ExtendedErrorCodeUnsupportedDNSKEYAlgorithm
ExtendedErrorCodeUnsupportedDSDigestType
ExtendedErrorCodeStaleAnswer
ExtendedErrorCodeForgedAnswer
ExtendedErrorCodeDNSSECIndeterminate
ExtendedErrorCodeDNSBogus
ExtendedErrorCodeSignatureExpired
ExtendedErrorCodeSignatureNotYetValid
ExtendedErrorCodeDNSKEYMissing
ExtendedErrorCodeRRSIGsMissing
ExtendedErrorCodeNoZoneKeyBitSet
ExtendedErrorCodeNSECMissing
ExtendedErrorCodeCachedError
ExtendedErrorCodeNotReady
ExtendedErrorCodeBlocked
ExtendedErrorCodeCensored
ExtendedErrorCodeFiltered
ExtendedErrorCodeProhibited
ExtendedErrorCodeStaleNXDOMAINAnswer
ExtendedErrorCodeNotAuthoritative
ExtendedErrorCodeNotSupported
ExtendedErrorCodeNoReachableAuthority
ExtendedErrorCodeNetworkError
ExtendedErrorCodeInvalidData
)
// ExtendedErrorCodeToString maps extended error info codes to a human readable
// description.
var ExtendedErrorCodeToString = map[uint16]string{
ExtendedErrorCodeOther: "Other",
ExtendedErrorCodeUnsupportedDNSKEYAlgorithm: "Unsupported DNSKEY Algorithm",
ExtendedErrorCodeUnsupportedDSDigestType: "Unsupported DS Digest Type",
ExtendedErrorCodeStaleAnswer: "Stale Answer",
ExtendedErrorCodeForgedAnswer: "Forged Answer",
ExtendedErrorCodeDNSSECIndeterminate: "DNSSEC Indeterminate",
ExtendedErrorCodeDNSBogus: "DNSSEC Bogus",
ExtendedErrorCodeSignatureExpired: "Signature Expired",
ExtendedErrorCodeSignatureNotYetValid: "Signature Not Yet Valid",
ExtendedErrorCodeDNSKEYMissing: "DNSKEY Missing",
ExtendedErrorCodeRRSIGsMissing: "RRSIGs Missing",
ExtendedErrorCodeNoZoneKeyBitSet: "No Zone Key Bit Set",
ExtendedErrorCodeNSECMissing: "NSEC Missing",
ExtendedErrorCodeCachedError: "Cached Error",
ExtendedErrorCodeNotReady: "Not Ready",
ExtendedErrorCodeBlocked: "Blocked",
ExtendedErrorCodeCensored: "Censored",
ExtendedErrorCodeFiltered: "Filtered",
ExtendedErrorCodeProhibited: "Prohibited",
ExtendedErrorCodeStaleNXDOMAINAnswer: "Stale NXDOMAIN Answer",
ExtendedErrorCodeNotAuthoritative: "Not Authoritative",
ExtendedErrorCodeNotSupported: "Not Supported",
ExtendedErrorCodeNoReachableAuthority: "No Reachable Authority",
ExtendedErrorCodeNetworkError: "Network Error",
ExtendedErrorCodeInvalidData: "Invalid Data",
}
// StringToExtendedErrorCode is a map from human readable descriptions to
// extended error info codes.
var StringToExtendedErrorCode = reverseInt16(ExtendedErrorCodeToString)
// EDNS0_EDE option is used to return additional information about the cause of
// DNS errors.
type EDNS0_EDE struct {
InfoCode uint16
ExtraText string
}
// Option implements the EDNS0 interface.
func (e *EDNS0_EDE) Option() uint16 { return EDNS0EDE }
func (e *EDNS0_EDE) copy() EDNS0 { return &EDNS0_EDE{e.InfoCode, e.ExtraText} }
func (e *EDNS0_EDE) String() string {
info := strconv.FormatUint(uint64(e.InfoCode), 10)
if s, ok := ExtendedErrorCodeToString[e.InfoCode]; ok {
info += fmt.Sprintf(" (%s)", s)
}
return fmt.Sprintf("%s: (%s)", info, e.ExtraText)
}
func (e *EDNS0_EDE) pack() ([]byte, error) {
b := make([]byte, 2+len(e.ExtraText))
binary.BigEndian.PutUint16(b[0:], e.InfoCode)
copy(b[2:], []byte(e.ExtraText))
return b, nil
}
func (e *EDNS0_EDE) unpack(b []byte) error {
if len(b) < 2 {
return ErrBuf
}
e.InfoCode = binary.BigEndian.Uint16(b[0:])
e.ExtraText = string(b[2:])
return nil
}
// The EDNS0_ESU option for ENUM Source-URI Extension
type EDNS0_ESU struct {
Code uint16
Uri string
}
// Option implements the EDNS0 interface.
func (e *EDNS0_ESU) Option() uint16 { return EDNS0ESU }
func (e *EDNS0_ESU) String() string { return e.Uri }
func (e *EDNS0_ESU) copy() EDNS0 { return &EDNS0_ESU{e.Code, e.Uri} }
func (e *EDNS0_ESU) pack() ([]byte, error) { return []byte(e.Uri), nil }
func (e *EDNS0_ESU) unpack(b []byte) error {
e.Uri = string(b)
return nil
}
module github.com/miekg/dns
go 1.12
require (
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect
)
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc=
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0=
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0=
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
package dns
import (
"bytes"
"crypto"
"hash"
)
// identityHash will not hash, it only buffers the data written into it and returns it as-is.
type identityHash struct {
b *bytes.Buffer
}
// Implement the hash.Hash interface.
func (i identityHash) Write(b []byte) (int, error) { return i.b.Write(b) }
func (i identityHash) Size() int { return i.b.Len() }
func (i identityHash) BlockSize() int { return 1024 }
func (i identityHash) Reset() { i.b.Reset() }
func (i identityHash) Sum(b []byte) []byte { return append(b, i.b.Bytes()...) }
func hashFromAlgorithm(alg uint8) (hash.Hash, crypto.Hash, error) {
hashnumber, ok := AlgorithmToHash[alg]
if !ok {
return nil, 0, ErrAlg
}
if hashnumber == 0 {
return identityHash{b: &bytes.Buffer{}}, hashnumber, nil
}
return hashnumber.New(), hashnumber, nil
}
...@@ -10,7 +10,7 @@ package dns ...@@ -10,7 +10,7 @@ package dns
// escaped dots (\.) for instance. // escaped dots (\.) for instance.
// s must be a syntactically valid domain name, see IsDomainName. // s must be a syntactically valid domain name, see IsDomainName.
func SplitDomainName(s string) (labels []string) { func SplitDomainName(s string) (labels []string) {
if len(s) == 0 { if s == "" {
return nil return nil
} }
fqdnEnd := 0 // offset of the final '.' or the length of the name fqdnEnd := 0 // offset of the final '.' or the length of the name
......