Move all (almost all) Go files in middleware into their own packages. This makes for better naming and discoverability. Lot of changes elsewhere to make this change. The middleware.State was renamed to request.Request which is better, but still does not cover all use-cases. It was also moved out middleware because it is used by `dnsserver` as well. A pkg/dnsutil packages was added for shared, handy, dns util functions. All normalize functions are now put in normalize.go
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
// Package errors implements an HTTP error handling middleware.
|
|
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/miekg/coredns/middleware"
|
|
"github.com/miekg/coredns/middleware/pkg/roller"
|
|
"github.com/miekg/coredns/request"
|
|
|
|
"github.com/miekg/dns"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// ErrorHandler handles DNS errors (and errors from other middleware).
|
|
type ErrorHandler struct {
|
|
Next middleware.Handler
|
|
LogFile string
|
|
Log *log.Logger
|
|
LogRoller *roller.LogRoller
|
|
Debug bool // if true, errors are written out to client rather than to a log
|
|
}
|
|
|
|
func (h ErrorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
|
defer h.recovery(ctx, w, r)
|
|
|
|
rcode, err := h.Next.ServeDNS(ctx, w, r)
|
|
|
|
if err != nil {
|
|
state := request.Request{W: w, Req: r}
|
|
errMsg := fmt.Sprintf("%s [ERROR %d %s %s] %v", time.Now().Format(timeFormat), rcode, state.Name(), state.Type(), err)
|
|
|
|
if h.Debug {
|
|
// Write error to response as a txt message instead of to log
|
|
answer := debugMsg(rcode, r)
|
|
txt, _ := dns.NewRR(". IN 0 TXT " + errMsg)
|
|
answer.Answer = append(answer.Answer, txt)
|
|
state.SizeAndDo(answer)
|
|
w.WriteMsg(answer)
|
|
return 0, err
|
|
}
|
|
h.Log.Println(errMsg)
|
|
}
|
|
|
|
return rcode, err
|
|
}
|
|
|
|
func (h ErrorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
|
|
rec := recover()
|
|
if rec == nil {
|
|
return
|
|
}
|
|
|
|
state := request.Request{W: w, Req: r}
|
|
// Obtain source of panic
|
|
// From: https://gist.github.com/swdunlop/9629168
|
|
var name, file string // function name, file name
|
|
var line int
|
|
var pc [16]uintptr
|
|
n := runtime.Callers(3, pc[:])
|
|
for _, pc := range pc[:n] {
|
|
fn := runtime.FuncForPC(pc)
|
|
if fn == nil {
|
|
continue
|
|
}
|
|
file, line = fn.FileLine(pc)
|
|
name = fn.Name()
|
|
if !strings.HasPrefix(name, "runtime.") {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Trim file path
|
|
delim := "/coredns/"
|
|
pkgPathPos := strings.Index(file, delim)
|
|
if pkgPathPos > -1 && len(file) > pkgPathPos+len(delim) {
|
|
file = file[pkgPathPos+len(delim):]
|
|
}
|
|
|
|
panicMsg := fmt.Sprintf("%s [PANIC %s %s] %s:%d - %v", time.Now().Format(timeFormat), r.Question[0].Name, dns.Type(r.Question[0].Qtype), file, line, rec)
|
|
if h.Debug {
|
|
// Write error and stack trace to the response rather than to a log
|
|
var stackBuf [4096]byte
|
|
stack := stackBuf[:runtime.Stack(stackBuf[:], false)]
|
|
answer := debugMsg(dns.RcodeServerFailure, r)
|
|
// add stack buf in TXT, limited to 255 chars for now.
|
|
txt, _ := dns.NewRR(". IN 0 TXT " + string(stack[:255]))
|
|
answer.Answer = append(answer.Answer, txt)
|
|
state.SizeAndDo(answer)
|
|
w.WriteMsg(answer)
|
|
} else {
|
|
// Currently we don't use the function name, since file:line is more conventional
|
|
h.Log.Printf(panicMsg)
|
|
}
|
|
}
|
|
|
|
// debugMsg creates a debug message that gets send back to the client.
|
|
func debugMsg(rcode int, r *dns.Msg) *dns.Msg {
|
|
answer := new(dns.Msg)
|
|
answer.SetRcode(r, rcode)
|
|
return answer
|
|
}
|
|
|
|
const timeFormat = "02/Jan/2006:15:04:05 -0700"
|