This file is indexed.

/usr/include/gdnsd/dname.h is in gdnsd-dev 2.2.0-1ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/* Copyright © 2012 Brandon L Black <blblack@gmail.com> and Jay Reitz <jreitz@gmail.com>
 *
 * This file is part of gdnsd.
 *
 * gdnsd is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * gdnsd is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gdnsd.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef GDNSD_DNAME_H
#define GDNSD_DNAME_H

#include <gdnsd/compiler.h>
#include <gdnsd/alloc.h>
#include <gdnsd/dmn.h>

#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

/*
 * Notes about Domain Names in general:
 * All domainnames are composed from labels.
 *
 * In the wire format:
 *  (compression is not considered here)
 *  The maximum length of a label is 63 bytes.
 *  Each label is prefixed by a length byte with a value
 *   of 1-63 inclusive.
 *  The domainname as a whole is terminated by a NUL byte (which is
 *   technically a label of length zero).
 *  The domainname for the root of the DNS is simply a NUL byte
 *   with no preceding labels.
 *  The maximum length of a domainname is 255 bytes in wire-encoded
 *   form, including the final NUL.
 *
 * In ASCII format without considering escaping:
 *  The maximum label length is still 63 bytes
 *  Labels have no explicit length prefix, and instead are
 *   separated by a single period.
 *  A final trailing dot is often used to signify that the
 *   name is fully qualified (as opposed to a name which is
 *   intended to have the local domain appended automatically).
 *  In some cases the final trailing dot is basically ignored
 *   and names are considered fully-qualified regardless.
 *  The maximum overall length is ultimately determined by the
 *   wire format, not this format, but works out to 254 in practice:
 *   63 . 63 . 63 . 63   -> illegal (257 in wire form)
 *   63 . 63 . 63 . 62   -> illegal (256 in wire form)
 *   63 . 63 . 63 . 61   -> 253 (255 in wire form)
 *   63 . 63 . 63 . 61 . -> 254 (255 in wire form)
 *    1 .  1 .. 1 .  1   -> 253 (255 in wire form) (127 1-byte labels)
 *    1 .  1 .. 1 .  1 . -> 254 (255 in wire form) (127 1-byte labels)
 *
 * Escaping in the ASCII format:
 *  RFC1035 specifies an escaping method for zonefiles in general, which
 *   allows embedding strange characters (including dots) within labels.
 *  The escapes come in two forms: "\X" and "\DDD".  In the \X case, the
 *   X character is taken to have no special meaning (e.g. "\." is a dot
 *   as part of label data, instead of as a label separator).  In the
 *   \DDD case, DDD are interpreted as a 3-digit base 10 number from the
 *   range 0-255, and the escape represents a byte of that same value
 *   (again with no special meaning).
 *  The maximum width of an escape (representing a single "real" byte)
 *   is 4 (the "\DDD" form).
 *  The maximum width of an ASCII label in escaped form is 252 (63 * 4).
 *  When considering whole domainnames, one would multiply out the label
 *   bytes, but not the separator dots.  Therefore those names which pack
 *   the maximal overall length into the minimum set of labels see the
 *   most escape-expansion.  Reusing the maximal pair above:
 *   63 . 63 . 63 . 61   -> 253 (255 in wire form)
 *     ((63 + 63 + 63 + 61) * 4) + 3 = 1003
 *   63 . 63 . 63 . 61 . -> 254 (255 in wire form)
 *     ((63 + 63 + 63 + 61) * 4) + 4 = 1004
 *
 * Keep in mind these are data lengths only.  If your ASCII strings are
 *  NUL-terminated, that's an extra byte.
 *
 * So to recap:
 *  ASCII with escapes:
 *   Max label storage (NUL-terminated): 252 (253)
 *   Max name storage (NUL-terminated): 1004 (1005)
 *  ASCII without escapes:
 *   Max label storage (NUL-terminated): 63 (64)
 *   Max name storage (NUL-terminated): 254 (255)
 *  Wire:
 *   Max label len: 63
 *   Max overall len: 255
 *
 * BUT:
 *  The fact that an ASCII representation of a full name
 *   fits within 1004 (1005 for ASCIIZ) bytes does *not*
 *   imply that it is legal (that it encodes to a wire
 *   format name of legal length).  This is obvious because
 *   the 1004-byte ASCII name might contain no escapes.
 * For that matter, even if escapes are not allowed and the
 *   individual labels are checked for the 63 limit, fitting
 *   within 254 (255 for ASCIIZ) doesn't imply legality
 *   either.  Example:
 *   63 . 63 . 63 . 62 -> 254 (256, illegal, in wire form)
 *
 */

/*
 * Notes on the "dname" format:
 *
 * "dname"-formatted data is the common representation of
 *  a domainname throughout the gdnsd source code.
 *
 * The first byte is always overall len of the rest of the string.
 * The rest of the string is a wire-format domainname without
 *  compression pointers.
 * A full dname should be \x00 terminated as it would be in wire form.
 * Partial dnames (in the process of being constructed, e.g. a dname
 *  for a relative name that had no trailing dot in ASCII form, before
 *  the origin is appended) are signalled by replacing the trailing \0
 *  with a trailing \xff.  These are not valid for passing as "dname"
 *  data to the core code, but are useful during intermediate stages
 *  of construction.
 * The maximum required storage size is 256 bytes for a maximally long
 *  name.  e.g. ("uint8_t dname[256];" or "uint8_t* dname = xmalloc(256);").
 * As a general rule, all generic dname storage a plugin creates should
 *  be allocated for the full 256 bytes if you are going to construct
 *  them using the helper functions here.  However, keep in mind that
 *  constant dname arguments passed down from the core code could be
 *  (and often are) allocated to their exact size. (e.g. 2 bytes for the
 *  first example below).
 * Examples:
 *  "." -> \x01\x00
 *  "com." -> \x05\x03com\x00
 *  "a.com." -> \x07\x01a\x03com\x00
 *  "www" -> \x05\x03www\xff (not yet fully qualified)
 */

// Status of a dname after some of the operations below.
//  DNAME_VALID means the operation succeeded and the result is fully qualified (ends in \0).
//  DNAME_PARTIAL means the operation succeeded, but the result is not yet fully qualified (ends in \xff).
//  DNAME_INVALID means the operation failed (invalid input), output contents are undefined.
//  "PARTIAL" might be valid for your code for intermediate purposes, but is not considered
//    a valid dname for normal use with the core code.
typedef enum {
    DNAME_VALID = 0,
    DNAME_PARTIAL,
    DNAME_INVALID,
} gdnsd_dname_status_t;

#pragma GCC visibility push(default)

// Unescape the string "in" into the storage at "out", using
//  DNS zonefile escaping rules.
// Return value is output len, which will be <= input len
// input len must be >0
// Note: you are responsible for allocating output storage of at least "len" at "out".
// This function cannot fail given correct inputs.
// If it is given invalid/dangling escapes, it will return
//   the error indication by returning a zero length
// Note the use of "restrict": out and in cannot overlap
F_NONNULL
unsigned gdnsd_dns_unescape(char* restrict out, const char* restrict in, const unsigned len);

// Parse a uint8_t* human-readable string into a dname.  len is the length of the input.
// Escapes will be unescaped and any uppercase ASCII characters will be normalized to lowercase.
// If there is no trailing dot, the result will be left in the PARTIAL state.
// It is assumed you have allocated a full 256 bytes at "dname" for storage of the
//  result.  Any less could result in random crashy bugs.
// Note the use of "restrict": dname and instr cannot overlap
F_NONNULL
gdnsd_dname_status_t gdnsd_dname_from_string(uint8_t* restrict dname, const char* restrict instr, const unsigned len);

// Turns a dname back into a printable string
// "dname" must be DNAME_VALID or DNAME_PARTIAL
// "str" must be preallocated to at least 1024 bytes,
// retval is the length of the string stored to "str", including the terminal NUL
F_NONNULL
unsigned gdnsd_dname_to_string(const uint8_t* restrict dname, char* restrict str);

// Concatenate one dname onto the end of another.  Invalid inputs are not allowed.
//  You are responsible for ensuring this is the case before calling, or crashes
//  could ensue.  As before, "dn1" must have a full 256 bytes of storage.
// If the concantenation would result in an illegally-long dname, the return
//  value will be INVALID.  Otherwise the return value should mirror
//  the status of the "origin" argument (VALID vs PARTIAL).
// Note the use of "restrict": dn1 and dn2 cannot overlap
F_NONNULL
gdnsd_dname_status_t gdnsd_dname_cat(uint8_t* restrict dn1, const uint8_t* restrict dn2);

// Terminate a DNAME_PARTIAL name, converting it to DNAME_VALID.  Idempotent
//  and thus harmless on names which are already DNAME_VALID.  Invalid input
//  could cause crashes.
F_NONNULL F_UNUSED
static void gdnsd_dname_terminate(uint8_t* dname) {
    dmn_assert(dname); dmn_assert(*dname);
    dname[*dname] = 0;
}

// Check the status/validity of a dname.  The dname will be carefully parsed,
//  and should handle any random input (although most random inputs will give
//  DNAME_INVALID) assuming dname has at least a 256 byte memory allocation.
// If dname is allocated to less than 256 bytes, then crashes are possible with
//  input that looks like a name longer than the allocation.
F_NONNULL F_PURE
gdnsd_dname_status_t gdnsd_dname_status(const uint8_t* dname);

F_PURE F_NONNULL
unsigned gdnsd_dname_hash(const uint8_t* input);

#pragma GCC visibility pop

// Check the status of a known-good dname.  It is assumed that the dname was
//  constructed correctly by other code, and merely differentiates quickly
//  between the partial and fully-qualfied cases.  If the input is invalid,
//  it could crash.
F_NONNULL F_UNUSED
static bool gdnsd_dname_is_partial(const uint8_t* dname) {
    dmn_assert(dname); dmn_assert(*dname);
    return dname[*dname] == 255;
}

// Trim a dname's storage to the minimum required size.  Assumes storage was
//  originally allocated with xmalloc() or equivalent.  Note that after trimming
//  you cannot perform operations like dname_cat() on this directly.
F_WUNUSED F_NONNULL F_UNUSED
static uint8_t* gdnsd_dname_trim(uint8_t* dname) {
    dmn_assert(dname); dmn_assert(*dname);
    return xrealloc(dname, *dname + 1U);
}

// Copy "source" dname to the storage at dest, which must be allocated
//  large enough (256 max).
F_NONNULL F_UNUSED
static void gdnsd_dname_copy(uint8_t* dest, const uint8_t* source) {
    dmn_assert(dest); dmn_assert(source);
    const unsigned len = *source;
    dmn_assert(len); dmn_assert(len < 256U);
    memcpy(dest, source, len + 1U);
}

// Allocate new storage (via xmalloc()), clone the input dname into it, and return.
// The second argument "exact" determines whether the new copy will be allocated
//  to 256 bytes or to the exact amount necessary to hold the data.
F_WUNUSED F_NONNULL F_UNUSED
static uint8_t* gdnsd_dname_dup(const uint8_t* dname, bool exact) {
    dmn_assert(dname); dmn_assert(*dname);
    uint8_t* out = xmalloc(exact ? (*dname + 1U) : 256U);
    gdnsd_dname_copy(out, dname);
    return out;
}

// Returns memcmp()-like return values, primary sort is
//  on overall length (<0 means dn1 is shorter than dn2).
// dn1 and dn2 must be DNAME_VALID or DNAME_PARTIAL
// When lengths are equal the return values will make
//  for a stable sort assuming no duplicates, but are
//  somewhat meaningless.
// Equality (0) will only be returned on a complete match,
//  including the difference between VALID and PARTIAL.
F_NONNULL F_PURE F_UNUSED
static int gdnsd_dname_cmp(const uint8_t* dn1, const uint8_t* dn2) {
    dmn_assert(dn1); dmn_assert(dn2);
    dmn_assert(gdnsd_dname_status(dn1) != DNAME_INVALID);
    dmn_assert(gdnsd_dname_status(dn2) != DNAME_INVALID);
    const uint8_t len1 = *dn1++;
    const uint8_t len2 = *dn2++;
    int rv = len1 - len2;
    if(!rv)
        rv = memcmp(dn1, dn2, len1);
    return rv;
}

// As above but for labels (no dname_status() assertion)
F_NONNULL F_PURE F_UNUSED
static int gdnsd_label_cmp(const uint8_t* label1, const uint8_t* label2) {
    dmn_assert(label1); dmn_assert(label2);
    const uint8_t len1 = *label1++;
    const uint8_t len2 = *label2++;
    int rv = len1 - len2;
    if(!rv)
        rv = memcmp(label1, label2, len1);
    return rv;
}

// returns true if dname is within zone
// returns true if they are identical
// dname and zone must be DNAME_VALID (fully-qualified).
F_NONNULL F_PURE F_UNUSED
static bool gdnsd_dname_isinzone(const uint8_t* parent, const uint8_t* child) {
    dmn_assert(parent); dmn_assert(child);
    dmn_assert(gdnsd_dname_status(parent) == DNAME_VALID);
    dmn_assert(gdnsd_dname_status(child) == DNAME_VALID);

    bool rv = false;
    const unsigned plen = *parent++;
    const unsigned clen = *child++;
    dmn_assert(plen); // implied by DNAME_VALID check above
    dmn_assert(clen); // implied by DNAME_VALID check above

    if(plen <= clen) { // if child shorter than parent, cannot be isinzone
        // child_pstart is the hypothetical location of
        //   the trailing "parent" in "child" if isinzone
        const uint8_t* child_pstart = child + (clen - plen);
        if(!memcmp(child_pstart, parent, plen)) { // basic trailing ~match
            // There are corner cases that can fool the quick memcmp check
            //   into a false positive.  Basically, picture www.xfoo.com vs
            //   foo.com, where 'x' happens to be the integer value 3 (the
            //   length of "foo") within child's label. This is more
            //   realistic for long labels where the length byte could be
            //   in the ASCII range for numerals, so we must iterate
            //   child's actual labels and make sure that one of them falls
            //   exactly on child_pstart.
            while(*child) { // not reached the terminal \0
                if(child == child_pstart) { // definite match
                    rv = true;
                    break;
                }
                // jump to next start-of-label
                const unsigned llen = *child++;
                child += llen;
            }
            // the above misses the case of both parent and child being the
            //   root zone of the DNS, and this catches it.
            if(plen == 1)
                rv = true;
        }
    }

    return rv;
}

// both arguments must be DNAME_VALID, and dname must be known
//   to be a child of (or equal to) parent (e.g. via gdnsd_dname_isinzone()).
// chops the zone part off the end of dname, re-rooting it as a valid name.
// this is used for the LHS of in-zone records that are fully qualified
//   during zonefile scanning, since insertion is rooted at the top of the
//   zone.
F_NONNULL F_UNUSED
static void gdnsd_dname_drop_zone(uint8_t* dname, const uint8_t* zroot) {
    dmn_assert(dname); dmn_assert(zroot);
    dmn_assert(gdnsd_dname_status(dname) == DNAME_VALID);
    dmn_assert(gdnsd_dname_status(zroot) == DNAME_VALID);
    dmn_assert((*dname) >= (*zroot));
    const unsigned newterm = (*dname) - ((*zroot) - 1U);
    dmn_assert(dname[newterm] == zroot[1]);
    dname[0] = newterm;
    dname[newterm] = 0;
}

// Returns true if dname is a wildcard name (first label is a lone "*").
// Argument must be DNAME_VALID or DNAME_PARTIAL
F_NONNULL F_PURE F_UNUSED
static bool gdnsd_dname_iswild(const uint8_t* dname) {
    dmn_assert(dname);
    dmn_assert(gdnsd_dname_status(dname) != DNAME_INVALID);
    if(dname[1] == 1 && dname[2] == '*')
        return true;
    return false;
}

typedef gdnsd_dname_status_t dname_status_t;
#define dns_unescape gdnsd_dns_unescape
#define dname_from_string gdnsd_dname_from_string
#define dname_cat gdnsd_dname_cat
#define dname_terminate gdnsd_dname_terminate
#define dname_status gdnsd_dname_status
#define dname_is_partial gdnsd_dname_is_partial
#define dname_trim gdnsd_dname_trim
#define dname_copy gdnsd_dname_copy
#define dname_dup gdnsd_dname_dup
#define dname_cmp gdnsd_dname_cmp
#define dname_isinzone gdnsd_dname_isinzone
#define dname_iswild gdnsd_dname_iswild
#define dname_hash gdnsd_dname_hash

#endif // GDNSD_DNAME_H