ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
dns_wire.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file dns_wire.h
6 * @brief The DNS name on the wire (RFC 1035 sec 3.1, sec 4.1.4): labels in, dotted string out.
7 *
8 * RFC 1035 sec 3.1: a domain name is a sequence of labels, each one a length octet followed by that
9 * many octets, ending in the null label of the root, which is a zero length octet. The high order
10 * two bits of a length octet are 00 for a label. RFC 1035 sec 4.1.4 gives 11 to a pointer, a two
11 * octet sequence whose remaining fourteen bits are the OFFSET of a prior occurrence of the same name
12 * in the message, and reserves 10 and 01.
13 *
14 * Every DNS-shaped protocol carries that encoding, so it is written once here: the unicast server
15 * answering a query and the multicast responder advertising a service read and write names through
16 * this handle.
17 *
18 * Names compare case-insensitively (RFC 1035 sec 2.3.3), which is a property of the encoding rather
19 * than of either caller, so the comparison sits here too.
20 *
21 * Pure: no state, no allocation, no I/O.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef PROTOCORE_DNS_WIRE_H
28#define PROTOCORE_DNS_WIRE_H
29
30#include "protocore_config.h"
31
33
34/** @brief Longest label a name may carry, the six bits a length octet leaves (RFC 1035 sec 2.3.4). */
35#define PROTOCORE_DNS_LABEL_MAX 63u
36
37/** @brief Pointers one name may follow before it is read as a loop (RFC 1035 sec 4.1.4). */
38#define PROTOCORE_DNS_PTR_HOPS 8u
39
40/**
41 * @brief The message a name is read out of, and where its dotted text lands (RFC 1035 sec 4.1).
42 */
43typedef struct
44{
45 const uint8_t *pkt; ///< the message the name sits in
46 size_t len; ///< octets in that message
47 size_t off; ///< where the name's first length octet sits
48 char *out; ///< where the dotted, NUL-terminated name is written
49 size_t out_cap; ///< octets that buffer holds
50 proto_bool allow_ptr; ///< follow a pointer (RFC 1035 sec 4.1.4); false makes one a decode failure
52
53/**
54 * @brief The dotted name to write, and where its label sequence lands (RFC 1035 sec 3.1).
55 */
56typedef struct
57{
58 const char *dotted; ///< the name as text, labels separated by dots, trailing root dot optional
59 uint8_t *out; ///< where the labels and the root octet are written
60 size_t out_cap; ///< octets that buffer holds
62
63/** @brief The pair a case-insensitive compare judges (RFC 1035 sec 2.3.3). */
64typedef struct
65{
66 const char *a; ///< the left dotted name
67 const char *b; ///< the right dotted name
69
70/**
71 * @brief The DNS name codec: labels to a dotted string, a dotted string to labels, and the compare.
72 *
73 * A caller sets the members a call takes, invokes it through ::DnsWire, and reads the outcome off
74 * the same handle.
75 *
76 * @var DnsWireNs::msg the message a decode reads, and where its dotted text lands
77 * @var DnsWireNs::text the dotted name an encode writes, and where its labels land
78 * @var DnsWireNs::cmp the pair a compare judges
79 * @var DnsWireNs::ok a decode's or a compare's true/false outcome
80 * @var DnsWireNs::next offset just past the name as it sits at @c msg.off, 0 on a failed decode
81 * @var DnsWireNs::n octets an encode wrote, 0 when it wrote none
82 * @var DnsWireNs::decode read the name at @c msg.off into @c msg.out as a dotted string
83 * @var DnsWireNs::encode write @c text.dotted into @c text.out as labels plus the root octet
84 * @var DnsWireNs::eq compare two dotted names ignoring ASCII case (RFC 1035 sec 2.3.3)
85 *
86 * decode follows pointers only with @c msg.allow_ptr, at most ::PROTOCORE_DNS_PTR_HOPS of them, so a
87 * message that points at itself terminates. The first name in a message has nothing earlier to point
88 * at, which makes a pointer in a question malformed by construction: the unicast server clears the
89 * flag, the responder reading answers sets it. @c next is where the name ends as it sits at
90 * @c msg.off, so a name ending in a pointer reports two octets past the pointer rather than the end
91 * of what it pointed at, and the caller keeps walking the record the name came from.
92 *
93 * decode reports false on a truncated name, a reserved label type, a label over
94 * ::PROTOCORE_DNS_LABEL_MAX, a name that does not fit @c msg.out_cap, or more than
95 * ::PROTOCORE_DNS_PTR_HOPS pointers.
96 *
97 * encode writes no pointer: an OFFSET is meaningful against one particular message, so composing one
98 * belongs to whoever lays that message out. An empty name is the root octet alone. It reports 0 when
99 * the name does not fit @c text.out_cap, carries an empty label, or carries one over
100 * ::PROTOCORE_DNS_LABEL_MAX.
101 *
102 * No storage member: every octet a call touches belongs to the caller, so nothing survives a call.
103 */
104typedef struct
105{
106 DnsWireMsgArgs msg; ///< what a decode reads (RFC 1035 sec 4.1.4)
107 DnsWireTextArgs text; ///< what an encode writes (RFC 1035 sec 3.1)
108 DnsWireCmpArgs cmp; ///< what a compare judges (RFC 1035 sec 2.3.3)
110 size_t next;
111 size_t n;
113
114/** @brief The operands and the outcome. */
115extern DnsWireVars DnsWireV;
116
117/** @brief The entries. */
118typedef struct
119{
120 void (*const decode)(uint8_t *work);
121 void (*const encode)(uint8_t *work);
122 void (*const eq)(uint8_t *work);
123} DnsWireNs;
124
125// What the table binds, defined once in the .c and taking one parameter each: everything
126// else an entry needs is an operand in DnsWireV or a region of the borrow at a fixed offset.
127void protocore_dns_wire_decode(uint8_t *work);
128void protocore_dns_wire_encode(uint8_t *work);
129void protocore_dns_wire_eq(uint8_t *work);
130
131// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
132// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
133// `DnsWire.decode(work)` resolves to a named function and becomes a DIRECT call. An extern table
134// leaves the call indirect and the symbol live at every level, -O2 -flto included.
135static const DnsWireNs DnsWire __attribute__((unused)) = {
139};
140
142
143#endif // PROTOCORE_DNS_WIRE_H
void protocore_dns_wire_decode(uint8_t *work)
void protocore_dns_wire_encode(uint8_t *work)
DnsWireVars DnsWireV
The operands and the outcome.
void protocore_dns_wire_eq(uint8_t *work)
The pair a case-insensitive compare judges (RFC 1035 sec 2.3.3).
Definition dns_wire.h:65
const char * a
the left dotted name
Definition dns_wire.h:66
const char * b
the right dotted name
Definition dns_wire.h:67
The message a name is read out of, and where its dotted text lands (RFC 1035 sec 4....
Definition dns_wire.h:44
const uint8_t * pkt
the message the name sits in
Definition dns_wire.h:45
size_t len
octets in that message
Definition dns_wire.h:46
size_t out_cap
octets that buffer holds
Definition dns_wire.h:49
char * out
where the dotted, NUL-terminated name is written
Definition dns_wire.h:48
proto_bool allow_ptr
follow a pointer (RFC 1035 sec 4.1.4); false makes one a decode failure
Definition dns_wire.h:50
size_t off
where the name's first length octet sits
Definition dns_wire.h:47
The entries.
Definition dns_wire.h:119
void(*const decode)(uint8_t *work)
Definition dns_wire.h:120
The dotted name to write, and where its label sequence lands (RFC 1035 sec 3.1).
Definition dns_wire.h:57
size_t out_cap
octets that buffer holds
Definition dns_wire.h:60
const char * dotted
the name as text, labels separated by dots, trailing root dot optional
Definition dns_wire.h:58
uint8_t * out
where the labels and the root octet are written
Definition dns_wire.h:59
size_t next
Definition dns_wire.h:110
DnsWireTextArgs text
what an encode writes (RFC 1035 sec 3.1)
Definition dns_wire.h:107
proto_bool ok
Definition dns_wire.h:109
DnsWireMsgArgs msg
what a decode reads (RFC 1035 sec 4.1.4)
Definition dns_wire.h:106
size_t n
Definition dns_wire.h:111
DnsWireCmpArgs cmp
what a compare judges (RFC 1035 sec 2.3.3)
Definition dns_wire.h:108
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97