detached 33a243c

override.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package dnsfwd
4
5import (
6	"encoding/binary"
7	"net/netip"
8	"strings"
9)
10
11const (
12	typeA   = 1
13	classIN = 1
14	// overrideTTL is short: the sandbox lives for one job.
15	overrideTTL = 30
16)
17
18// overrideName returns Override normalised for comparison: lower case, without
19// a trailing dot.
20func (f *Forwarder) overrideName() string {
21	return strings.TrimSuffix(strings.ToLower(f.Override), ".")
22}
23
24// questionName reads the name of the question that starts at q[hdrLen]. It
25// returns the lower-cased name without trailing dot and the offset just after
26// the name. Compression pointers and over-long labels are refused (a question
27// never uses them): ok is false and the query is forwarded as usual.
28func questionName(q []byte) (name string, end int, ok bool) {
29	var sb strings.Builder
30	i := hdrLen
31	for {
32		if i >= len(q) {
33			return "", 0, false
34		}
35		n := int(q[i])
36		i++
37		if n == 0 {
38			return sb.String(), i, true
39		}
40		if n&0xC0 != 0 || i+n > len(q) {
41			return "", 0, false
42		}
43		if sb.Len() > 0 {
44			sb.WriteByte('.')
45		}
46		sb.WriteString(strings.ToLower(string(q[i : i+n])))
47		i += n
48		if sb.Len() > 253 {
49			return "", 0, false
50		}
51	}
52}
53
54// overrideAnswer builds the local answer to q when q asks for the overridden
55// name in class IN, else nil. A gets addr; every other type gets NOERROR with
56// no data (so AAAA is an empty answer: sandboxes have no IPv6, and an upstream
57// NXDOMAIN or CNAME for the name must not leak in).
58func (f *Forwarder) overrideAnswer(addr netip.Addr, q []byte) []byte {
59	want := f.overrideName()
60	if want == "" || len(q) < hdrLen {
61		return nil
62	}
63	// A plain query: QR=0, opcode 0, one question, nothing else but additional
64	// records (EDNS).
65	if q[2]&0xF8 != 0 || binary.BigEndian.Uint16(q[4:]) != 1 ||
66		binary.BigEndian.Uint16(q[6:]) != 0 || binary.BigEndian.Uint16(q[8:]) != 0 {
67		return nil
68	}
69	name, end, ok := questionName(q)
70	if !ok || name != want || end+4 > len(q) {
71		return nil
72	}
73	qtype := binary.BigEndian.Uint16(q[end:])
74	if binary.BigEndian.Uint16(q[end+2:]) != classIN {
75		return nil
76	}
77	end += 4
78	withA := qtype == typeA && addr.Is4()
79
80	r := make([]byte, 0, end+16+11)
81	r = append(r, q[0], q[1])
82	r = append(r, 0x80|(q[2]&0x01), 0x80) // QR, RD copied; RA; NOERROR
83	r = append(r, 0, 1)                   // QDCOUNT
84	if withA {
85		r = append(r, 0, 1)
86	} else {
87		r = append(r, 0, 0)
88	}
89	r = append(r, 0, 0) // NSCOUNT
90	// An EDNS query gets an OPT record back (RFC 6891), without options.
91	edns := binary.BigEndian.Uint16(q[10:]) >= 1 && len(q) >= end+11 &&
92		q[end] == 0 && binary.BigEndian.Uint16(q[end+1:]) == 41
93	if edns {
94		r = append(r, 0, 1)
95	} else {
96		r = append(r, 0, 0)
97	}
98	r = append(r, q[hdrLen:end]...)
99	if withA {
100		r = append(r, 0xC0, hdrLen, 0, typeA, 0, classIN)
101		r = binary.BigEndian.AppendUint32(r, overrideTTL)
102		a := addr.As4()
103		r = append(r, 0, 4, a[0], a[1], a[2], a[3])
104	}
105	if edns {
106		r = append(r, 0, 0, 41, 0x10, 0, 0, 0, 0, 0, 0, 0) // root, OPT, 4096 bytes, no options
107	}
108	return r
109}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close