api

package
v1.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 16 Imported by: 0

README

HTTP API

SDNS exposes an optional HTTP API for managing the blocklist, purging cached answers, scraping Prometheus metrics, and — with SDNS_PPROF=1 — serving Go pprof profiles. It listens on whatever address you put in api in sdns.conf (default 127.0.0.1:8080); set api = "" to turn it off entirely.

Authentication

Set bearertoken in sdns.conf to require this header on every request:

Authorization: Bearer <token>

Missing, malformed, or mismatched headers get 401 {"error":"unauthorized"}. The token is never logged.

/debug/pprof/* is the one exception — pprof tooling doesn't send Authorization headers, so those routes stay open even when a token is set. If you enable pprof, keep the API listener on loopback or behind an authenticating proxy.

Endpoints

Method Path Purpose
GET /api/v1/block/set/:key Add a single block entry
GET /api/v1/block/get/:key Look up a block entry
GET /api/v1/block/exists/:key Membership probe
GET /api/v1/block/remove/:key Delete a block entry
POST /api/v1/block/set/batch Bulk-add (JSON body)
POST /api/v1/block/remove/batch Bulk-remove (JSON body)
GET /api/v1/purge/:qname/:qtype Drop cached answer for one question
GET /metrics Prometheus exposition
GET /debug/pprof/* pprof (only with SDNS_PPROF=1)

The block/* routes are only registered when the blocklist middleware is enabled — without it they return 404.

Blocklist

:key is a domain name (domain.com) or wildcard (*.evil.example). Wildcards are canonicalised to *.example.com. form on disk, same as a manual entry in the local blocklist file.

$ curl http://localhost:8080/api/v1/block/set/domain.com
{"success":true}

$ curl http://localhost:8080/api/v1/block/exists/domain.com
{"exists":true}

$ curl -i http://localhost:8080/api/v1/block/get/missing.example
HTTP/1.1 404 Not Found
{"error":"missing.example not found"}

$ curl http://localhost:8080/api/v1/block/remove/domain.com
{"success":true}

set returns success:false when the key was already present or sits on the whitelist; remove returns success:false when the key wasn't there to begin with. exists is the only single-key endpoint that uses an exists: payload — the rest all return success:.

Bulk operations

Both batch endpoints take the same body shape:

{"keys": ["domain.com", "*.evil.example", "tracker.test"]}

The body is capped at 8 MiB and unknown fields are rejected. The whole batch lands as a single map mutation and a single disk write, so DNS queries aren't paused while a multi-thousand-entry import is running.

$ curl -X POST http://localhost:8080/api/v1/block/set/batch \
       -H 'Content-Type: application/json' \
       -d '{"keys":["domain.com","*.evil.example","tracker.test"]}'
{"requested":3,"added":3,"skipped":0}

$ curl -X POST http://localhost:8080/api/v1/block/remove/batch \
       -H 'Content-Type: application/json' \
       -d '{"keys":["domain.com","never-existed.test"]}'
{"requested":2,"removed":1,"missing":1}

added excludes duplicates and whitelisted keys; removed excludes keys that weren't present. So requested = added + skipped and requested = removed + missing.

A 200 response means in-memory state changed. The on-disk blocklist file is rewritten asynchronously via temp-file + atomic rename, so a crash or restart never sees a half-written file. Bad bodies (decoder error, unknown field, oversized payload) come back as 400 with the decoder's message; an empty or missing keys field returns 400 {"error":"keys is required and must be non-empty"}.

Cache purge

$ curl http://localhost:8080/api/v1/purge/example.com/MX
{"success":true}

The handler walks every middleware that exposes a Purger and drops cached entries for that question. Today that's the cache middleware (positive + negative entries for both CD=0 and CD=1) and the resolver's nameserver cache (only for qtype=NS). :qtype is case-insensitive; unknown types are rejected before any cache is touched:

$ curl -i http://localhost:8080/api/v1/purge/example.com/FOO
HTTP/1.1 400 Bad Request
{"error":"unknown qtype: FOO"}

Metrics

GET /metrics returns the Prometheus exposition for every metric the running middlewares register via promauto — cache, reflex, dns64, plugins, the lot. Auth-gated like everything else when a token is set.

pprof

SDNS_PPROF=1 in the sdns environment enables the standard net/http/pprof routes under /debug/pprof/ (heap, goroutine, allocs, profile, symbol, trace). These bypass the bearer-token check; see Authentication.

Server limits

ReadHeaderTimeout is 10 s. Batch bodies are bounded by MaxBytesReader at 8 MiB. Graceful shutdown waits up to 10 s for in-flight requests when the parent context is cancelled.

Documentation

Overview

Copyright (c) 2019 Eduard Urbach

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type API

type API struct {
	// contains filtered or unexported fields
}

API type.

func New

func New(cfg *config.Config) *API

New return new api.

func (*API) Run

func (a *API) Run(ctx context.Context)

(*API).Run run API server.

type Context added in v1.3.2

type Context struct {
	Request *http.Request
	Writer  http.ResponseWriter
	Handler Handler
	Params  *Params
}

func (*Context) JSON added in v1.3.2

func (ctx *Context) JSON(code int, data any)

func (*Context) Param added in v1.3.2

func (ctx *Context) Param(key string) string

type Group added in v1.3.2

type Group struct {
	// contains filtered or unexported fields
}

Group represents a group of routes with a common prefix.

func (*Group) GET added in v1.3.2

func (g *Group) GET(path string, handle Handler)

func (*Group) Handle added in v1.3.2

func (g *Group) Handle(method, path string, handle Handler)

func (*Group) POST added in v1.3.2

func (g *Group) POST(path string, handle Handler)

type Handler added in v1.3.2

type Handler func(ctx *Context)

type Json added in v1.3.2

type Json map[string]any

type Param added in v1.3.2

type Param struct {
	Key   string
	Value string
}

type Params added in v1.3.2

type Params []Param

type Router added in v1.3.2

type Router struct {
	// contains filtered or unexported fields
}

Router is a high-performance HTTP request router.

func NewRouter added in v1.3.2

func NewRouter() *Router

func (*Router) GET added in v1.3.2

func (rt *Router) GET(path string, handle Handler)

func (*Router) Group added in v1.3.2

func (rt *Router) Group(rp string) *Group

func (*Router) Handle added in v1.3.2

func (rt *Router) Handle(method, path string, handle Handler)

func (*Router) POST added in v1.3.2

func (rt *Router) POST(path string, handle Handler)

func (*Router) ServeHTTP added in v1.3.2

func (rt *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Tree added in v1.3.2

type Tree struct {
	// contains filtered or unexported fields
}

Tree represents a radix tree.

func (*Tree) Add added in v1.3.2

func (tree *Tree) Add(path string, data dataType)

(*Tree).Add add adds a new element to the tree.

func (*Tree) Lookup added in v1.3.2

func (tree *Tree) Lookup(ctx *Context)

(*Tree).Lookup lookup finds the data for the request path and assigns it to ctx.Handler, if available.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL