diff --git a/internal/engine/engine.go b/internal/engine/engine.go index b7f018f..7a0f2cf 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -368,6 +368,13 @@ func (e *Engine) Handle(ctx context.Context, req Request) Response { res.Duration = time.Since(start) e.finish(req, res, start, decision) return res + case blocklist.ActionRedirect, blocklist.ActionCNAME: + res.Msg = e.finalize(e.blockedMsg(q, blocked), q, req) + res.Action = qlog.ActionRedirect + res.BlockSource = blocked.Source + res.CacheHit = false + e.finish(req, res, start, decision) + return res default: res.Msg = e.finalize(e.blockedMsg(q, blocked), q, req) res.Action = qlog.ActionBlock @@ -502,6 +509,8 @@ func outcomeFor(res Response) string { return metrics.OutcomeCache case qlog.ActionBlock: return metrics.OutcomeBlocked + case qlog.ActionRedirect: + return metrics.OutcomeLocal case qlog.ActionError, qlog.ActionDrop: return metrics.OutcomeError case qlog.ActionRefused: diff --git a/internal/qlog/qlog.go b/internal/qlog/qlog.go index d63cc0f..da1c515 100644 --- a/internal/qlog/qlog.go +++ b/internal/qlog/qlog.go @@ -28,6 +28,8 @@ const ( ActionAllow Action = "allow" // ActionBlock is a blocklist hit. ActionBlock Action = "block" + // ActionRedirect is a blocklist rewrite to an address or CNAME target. + ActionRedirect Action = "redirect" // ActionCached is a cache hit. ActionCached Action = "cached" // ActionError is a SERVFAIL or other failure. @@ -246,11 +248,11 @@ var ( ) // ShouldLog reports whether an entry with this action passes the sampling -// policy. Blocked, cached and error entries are always kept when their -// category flag is set; ordinary resolutions are sampled. +// policy. Blocked, redirected, cached and error entries are always kept when +// their category flag is set; ordinary resolutions are sampled. func (l *Logger) ShouldLog(a Action) bool { switch a { - case ActionBlock: + case ActionBlock, ActionRedirect: return l.opts.LogBlocked case ActionCached: return l.opts.LogCached diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts index 0a4189c..41fc10a 100644 --- a/web/src/lib/types.ts +++ b/web/src/lib/types.ts @@ -22,7 +22,7 @@ export type Health = 'ok' | 'degraded' | 'down' /** One upstream server's circuit-breaker view. */ export type UpstreamHealth = 'up' | 'down' | 'degraded' /** The decision the pipeline reached for a query (qlog.Action). */ -export type LogAction = 'allow' | 'block' | 'cached' | 'error' | 'drop' | 'refused' +export type LogAction = 'allow' | 'block' | 'redirect' | 'cached' | 'error' | 'drop' | 'refused' /** What a rule does to the name it matches (blocklist.ParseAction). */ export type DomainAction = | 'allow' diff --git a/web/src/pages/Logs.tsx b/web/src/pages/Logs.tsx index 39806ee..93f7daa 100644 --- a/web/src/pages/Logs.tsx +++ b/web/src/pages/Logs.tsx @@ -54,6 +54,7 @@ const ACTION_OPTIONS = [ { value: '', label: 'All actions' }, { value: 'allow', label: 'Allow' }, { value: 'cached', label: 'Cached' }, + { value: 'redirect', label: 'Redirect' }, { value: 'block', label: 'Block' }, { value: 'error', label: 'Error' }, { value: 'drop', label: 'Drop' }, @@ -73,6 +74,8 @@ function actionTone(action: string): 'default' | 'ok' | 'warn' | 'danger' | 'inf return 'default' case 'cached': return 'info' + case 'redirect': + return 'info' case 'block': return 'warn' case 'error':