Coverage Report

Created: 2026-06-09 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/rdata/generic/opt_41.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/* RFC2671 */
15
16
#ifndef RDATA_GENERIC_OPT_41_C
17
#define RDATA_GENERIC_OPT_41_C
18
19
#define RRTYPE_OPT_ATTRIBUTES                                   \
20
15.8k
  (DNS_RDATATYPEATTR_SINGLETON | DNS_RDATATYPEATTR_META | \
21
15.8k
   DNS_RDATATYPEATTR_NOTQUESTION)
22
23
#include <isc/utf8.h>
24
25
static isc_result_t
26
2
fromtext_opt(ARGS_FROMTEXT) {
27
  /*
28
   * OPT records do not have a text format.
29
   */
30
31
2
  REQUIRE(type == dns_rdatatype_opt);
32
33
2
  UNUSED(type);
34
2
  UNUSED(rdclass);
35
2
  UNUSED(lexer);
36
2
  UNUSED(origin);
37
2
  UNUSED(options);
38
2
  UNUSED(target);
39
2
  UNUSED(callbacks);
40
41
2
  return ISC_R_NOTIMPLEMENTED;
42
2
}
43
44
static isc_result_t
45
4.99k
totext_opt(ARGS_TOTEXT) {
46
4.99k
  isc_region_t r;
47
4.99k
  isc_region_t or;
48
4.99k
  uint16_t option;
49
4.99k
  uint16_t length;
50
4.99k
  char buf[sizeof("64000 64000")];
51
52
  /*
53
   * OPT records do not have a text format.
54
   */
55
56
4.99k
  REQUIRE(rdata->type == dns_rdatatype_opt);
57
58
4.99k
  dns_rdata_toregion(rdata, &r);
59
14.7k
  while (r.length > 0) {
60
9.80k
    option = uint16_fromregion(&r);
61
9.80k
    isc_region_consume(&r, 2);
62
9.80k
    length = uint16_fromregion(&r);
63
9.80k
    isc_region_consume(&r, 2);
64
9.80k
    snprintf(buf, sizeof(buf), "%u %u", option, length);
65
9.80k
    RETERR(str_totext(buf, target));
66
9.80k
    INSIST(r.length >= length);
67
9.80k
    if (length > 0) {
68
5.20k
      if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
69
0
        RETERR(str_totext(" (", target));
70
0
      }
71
5.20k
      RETERR(str_totext(tctx->linebreak, target));
72
5.20k
      or = r;
73
5.20k
      or.length = length;
74
5.20k
      if (tctx->width == 0) { /* No splitting */
75
0
        RETERR(isc_base64_totext(&or, 60, "", target));
76
5.20k
      } else {
77
5.20k
        RETERR(isc_base64_totext(&or, tctx->width - 2,
78
5.20k
               tctx->linebreak,
79
5.20k
               target));
80
5.20k
      }
81
5.20k
      isc_region_consume(&r, length);
82
5.20k
      if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
83
0
        RETERR(str_totext(" )", target));
84
0
      }
85
5.20k
    }
86
9.80k
    if (r.length > 0) {
87
8.33k
      RETERR(str_totext(" ", target));
88
8.33k
    }
89
9.80k
  }
90
91
4.99k
  return ISC_R_SUCCESS;
92
4.99k
}
93
94
static isc_result_t
95
10.7k
fromwire_opt(ARGS_FROMWIRE) {
96
10.7k
  dns_fixedname_t fixed;
97
10.7k
  dns_name_t *name;
98
10.7k
  isc_buffer_t b;
99
10.7k
  isc_region_t sregion;
100
10.7k
  isc_region_t tregion;
101
10.7k
  isc_result_t result;
102
10.7k
  uint16_t length;
103
10.7k
  uint16_t opt;
104
10.7k
  unsigned int total;
105
106
10.7k
  REQUIRE(type == dns_rdatatype_opt);
107
108
10.7k
  UNUSED(type);
109
10.7k
  UNUSED(rdclass);
110
111
10.7k
  dctx = dns_decompress_setpermitted(dctx, false);
112
113
10.7k
  isc_buffer_activeregion(source, &sregion);
114
10.7k
  if (sregion.length == 0) {
115
5.51k
    return ISC_R_SUCCESS;
116
5.51k
  }
117
5.22k
  total = 0;
118
647k
  while (sregion.length != 0) {
119
642k
    if (sregion.length < 4) {
120
10
      return ISC_R_UNEXPECTEDEND;
121
10
    }
122
642k
    opt = uint16_fromregion(&sregion);
123
642k
    isc_region_consume(&sregion, 2);
124
642k
    length = uint16_fromregion(&sregion);
125
642k
    isc_region_consume(&sregion, 2);
126
642k
    total += 4;
127
642k
    if (sregion.length < length) {
128
18
      return ISC_R_UNEXPECTEDEND;
129
18
    }
130
642k
    switch (opt) {
131
5.19k
    case DNS_OPT_LLQ:
132
5.19k
      if (length != 18U) {
133
3
        return DNS_R_OPTERR;
134
3
      }
135
5.19k
      isc_region_consume(&sregion, length);
136
5.19k
      break;
137
15.8k
    case DNS_OPT_UL:
138
15.8k
      if (length != 4U && length != 8U) {
139
14
        return DNS_R_OPTERR;
140
14
      }
141
15.8k
      isc_region_consume(&sregion, length);
142
15.8k
      break;
143
19.5k
    case DNS_OPT_CLIENT_SUBNET: {
144
19.5k
      uint16_t family;
145
19.5k
      uint8_t addrlen;
146
19.5k
      uint8_t scope;
147
19.5k
      uint8_t addrbytes;
148
149
19.5k
      if (length < 4) {
150
3
        return DNS_R_OPTERR;
151
3
      }
152
19.5k
      family = uint16_fromregion(&sregion);
153
19.5k
      isc_region_consume(&sregion, 2);
154
19.5k
      addrlen = uint8_fromregion(&sregion);
155
19.5k
      isc_region_consume(&sregion, 1);
156
19.5k
      scope = uint8_fromregion(&sregion);
157
19.5k
      isc_region_consume(&sregion, 1);
158
159
19.5k
      switch (family) {
160
7.82k
      case 0:
161
        /*
162
         * XXXMUKS: In queries and replies, if
163
         * FAMILY is set to 0, SOURCE
164
         * PREFIX-LENGTH and SCOPE PREFIX-LENGTH
165
         * must be 0 and ADDRESS should not be
166
         * present as the address and prefix
167
         * lengths don't make sense because the
168
         * family is unknown.
169
         */
170
7.82k
        if (addrlen != 0U || scope != 0U) {
171
13
          return DNS_R_OPTERR;
172
13
        }
173
7.80k
        break;
174
7.80k
      case 1:
175
3.78k
        if (addrlen > 32U || scope > 32U) {
176
6
          return DNS_R_OPTERR;
177
6
        }
178
3.78k
        break;
179
7.96k
      case 2:
180
7.96k
        if (addrlen > 128U || scope > 128U) {
181
3
          return DNS_R_OPTERR;
182
3
        }
183
7.96k
        break;
184
7.96k
      default:
185
2
        return DNS_R_OPTERR;
186
19.5k
      }
187
19.5k
      addrbytes = (addrlen + 7) / 8;
188
19.5k
      if (addrbytes + 4 != length) {
189
15
        return DNS_R_OPTERR;
190
15
      }
191
192
19.5k
      if (addrbytes != 0U && (addrlen % 8) != 0) {
193
2.38k
        uint8_t bits = ~0U << (8 - (addrlen % 8));
194
2.38k
        bits &= sregion.base[addrbytes - 1];
195
2.38k
        if (bits != sregion.base[addrbytes - 1]) {
196
1
          return DNS_R_OPTERR;
197
1
        }
198
2.38k
      }
199
19.5k
      isc_region_consume(&sregion, addrbytes);
200
19.5k
      break;
201
19.5k
    }
202
29.1k
    case DNS_OPT_EXPIRE:
203
      /*
204
       * Request has zero length.  Response is 32 bits.
205
       */
206
29.1k
      if (length != 0 && length != 4) {
207
1
        return DNS_R_OPTERR;
208
1
      }
209
29.1k
      isc_region_consume(&sregion, length);
210
29.1k
      break;
211
2.51k
    case DNS_OPT_COOKIE:
212
      /*
213
       * Client cookie alone has length 8.
214
       * Client + server cookie is 8 + [8..32].
215
       */
216
2.51k
      if (length != 8 && (length < 16 || length > 40)) {
217
9
        return DNS_R_OPTERR;
218
9
      }
219
2.50k
      isc_region_consume(&sregion, length);
220
2.50k
      break;
221
3.62k
    case DNS_OPT_KEY_TAG:
222
3.62k
      if (length == 0 || (length % 2) != 0) {
223
5
        return DNS_R_OPTERR;
224
5
      }
225
3.62k
      isc_region_consume(&sregion, length);
226
3.62k
      break;
227
10.2k
    case DNS_OPT_EDE:
228
10.2k
      if (length < 2) {
229
2
        return DNS_R_OPTERR;
230
2
      }
231
      /* UTF-8 Byte Order Mark is not permitted. RFC 5198 */
232
10.1k
      if (isc_utf8_bom(sregion.base + 2, length - 2)) {
233
1
        return DNS_R_OPTERR;
234
1
      }
235
      /*
236
       * The EXTRA-TEXT field is specified as UTF-8, and
237
       * therefore must be validated for correctness
238
       * according to RFC 3269 security considerations.
239
       */
240
10.1k
      if (!isc_utf8_valid(sregion.base + 2, length - 2)) {
241
75
        return DNS_R_OPTERR;
242
75
      }
243
10.1k
      isc_region_consume(&sregion, length);
244
10.1k
      break;
245
1.35k
    case DNS_OPT_CLIENT_TAG:
246
1.35k
      FALLTHROUGH;
247
2.62k
    case DNS_OPT_SERVER_TAG:
248
2.62k
      if (length != 2) {
249
5
        return DNS_R_OPTERR;
250
5
      }
251
2.61k
      isc_region_consume(&sregion, length);
252
2.61k
      break;
253
2.35k
    case DNS_OPT_REPORT_CHANNEL:
254
      /* A domain name in wire format. RFC 9567 */
255
2.35k
      if (length == 0 || length > DNS_NAME_MAXWIRE) {
256
2
        return DNS_R_OPTERR;
257
2
      }
258
2.35k
      isc_buffer_init(&b, sregion.base, length);
259
2.35k
      isc_buffer_add(&b, length);
260
2.35k
      name = dns_fixedname_initname(&fixed);
261
2.35k
      result = dns_name_fromwire(name, &b, dctx, NULL);
262
2.35k
      if (result != ISC_R_SUCCESS || name->length != length ||
263
2.35k
          !dns_name_isabsolute(name))
264
2
      {
265
2
        return DNS_R_OPTERR;
266
2
      }
267
2.35k
      isc_region_consume(&sregion, length);
268
2.35k
      break;
269
61.9k
    case DNS_OPT_ZONEVERSION:
270
61.9k
      if (length == 0) {
271
        /* Request */
272
12.1k
        break;
273
12.1k
      }
274
      /* Labels and Type */
275
49.7k
      if (length < 2) {
276
1
        return DNS_R_OPTERR;
277
1
      }
278
      /* Type 0 (serial), length is 6. */
279
49.7k
      if (sregion.base[1] == 0 && length != 6) {
280
2
        return DNS_R_OPTERR;
281
2
      }
282
49.7k
      isc_region_consume(&sregion, length);
283
49.7k
      break;
284
489k
    default:
285
489k
      isc_region_consume(&sregion, length);
286
489k
      break;
287
642k
    }
288
642k
    total += length;
289
642k
  }
290
291
5.03k
  isc_buffer_activeregion(source, &sregion);
292
5.03k
  isc_buffer_availableregion(target, &tregion);
293
5.03k
  if (tregion.length < total) {
294
560
    return ISC_R_NOSPACE;
295
560
  }
296
4.47k
  memmove(tregion.base, sregion.base, total);
297
4.47k
  isc_buffer_forward(source, total);
298
4.47k
  isc_buffer_add(target, total);
299
300
4.47k
  return ISC_R_SUCCESS;
301
5.03k
}
302
303
static isc_result_t
304
4.88k
towire_opt(ARGS_TOWIRE) {
305
4.88k
  REQUIRE(rdata->type == dns_rdatatype_opt);
306
307
4.88k
  UNUSED(cctx);
308
309
4.88k
  return mem_tobuffer(target, rdata->data, rdata->length);
310
4.88k
}
311
312
static int
313
3.52k
compare_opt(ARGS_COMPARE) {
314
3.52k
  isc_region_t r1;
315
3.52k
  isc_region_t r2;
316
317
3.52k
  REQUIRE(rdata1->type == rdata2->type);
318
3.52k
  REQUIRE(rdata1->rdclass == rdata2->rdclass);
319
3.52k
  REQUIRE(rdata1->type == dns_rdatatype_opt);
320
321
3.52k
  dns_rdata_toregion(rdata1, &r1);
322
3.52k
  dns_rdata_toregion(rdata2, &r2);
323
3.52k
  return isc_region_compare(&r1, &r2);
324
3.52k
}
325
326
static isc_result_t
327
0
fromstruct_opt(ARGS_FROMSTRUCT) {
328
0
  dns_rdata_opt_t *opt = source;
329
0
  isc_region_t region;
330
0
  uint16_t length;
331
332
0
  REQUIRE(type == dns_rdatatype_opt);
333
0
  REQUIRE(opt != NULL);
334
0
  REQUIRE(opt->common.rdtype == type);
335
0
  REQUIRE(opt->common.rdclass == rdclass);
336
0
  REQUIRE(opt->options != NULL || opt->length == 0);
337
338
0
  UNUSED(type);
339
0
  UNUSED(rdclass);
340
341
0
  region.base = opt->options;
342
0
  region.length = opt->length;
343
0
  while (region.length >= 4) {
344
0
    isc_region_consume(&region, 2); /* opt */
345
0
    length = uint16_fromregion(&region);
346
0
    isc_region_consume(&region, 2);
347
0
    if (region.length < length) {
348
0
      return ISC_R_UNEXPECTEDEND;
349
0
    }
350
0
    isc_region_consume(&region, length);
351
0
  }
352
0
  if (region.length != 0) {
353
0
    return ISC_R_UNEXPECTEDEND;
354
0
  }
355
356
0
  return mem_tobuffer(target, opt->options, opt->length);
357
0
}
358
359
static isc_result_t
360
0
tostruct_opt(ARGS_TOSTRUCT) {
361
0
  dns_rdata_opt_t *opt = target;
362
0
  isc_region_t r;
363
364
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
365
0
  REQUIRE(opt != NULL);
366
367
0
  DNS_RDATACOMMON_INIT(opt, rdata->type, rdata->rdclass);
368
369
0
  dns_rdata_toregion(rdata, &r);
370
0
  opt->length = r.length;
371
0
  opt->options = mem_maybedup(mctx, r.base, r.length);
372
0
  opt->offset = 0;
373
0
  opt->mctx = mctx;
374
0
  return ISC_R_SUCCESS;
375
0
}
376
377
static void
378
0
freestruct_opt(ARGS_FREESTRUCT) {
379
0
  dns_rdata_opt_t *opt = source;
380
381
0
  REQUIRE(opt != NULL);
382
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
383
384
0
  if (opt->mctx == NULL) {
385
0
    return;
386
0
  }
387
388
0
  if (opt->options != NULL) {
389
0
    isc_mem_free(opt->mctx, opt->options);
390
0
  }
391
0
  opt->mctx = NULL;
392
0
}
393
394
static isc_result_t
395
0
additionaldata_opt(ARGS_ADDLDATA) {
396
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
397
398
0
  UNUSED(rdata);
399
0
  UNUSED(owner);
400
0
  UNUSED(add);
401
0
  UNUSED(arg);
402
403
0
  return ISC_R_SUCCESS;
404
0
}
405
406
static isc_result_t
407
0
digest_opt(ARGS_DIGEST) {
408
  /*
409
   * OPT records are not digested.
410
   */
411
412
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
413
414
0
  UNUSED(rdata);
415
0
  UNUSED(digest);
416
0
  UNUSED(arg);
417
418
0
  return ISC_R_NOTIMPLEMENTED;
419
0
}
420
421
static bool
422
0
checkowner_opt(ARGS_CHECKOWNER) {
423
0
  REQUIRE(type == dns_rdatatype_opt);
424
425
0
  UNUSED(type);
426
0
  UNUSED(rdclass);
427
0
  UNUSED(wildcard);
428
429
0
  return dns_name_equal(name, dns_rootname);
430
0
}
431
432
static bool
433
0
checknames_opt(ARGS_CHECKNAMES) {
434
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
435
436
0
  UNUSED(rdata);
437
0
  UNUSED(owner);
438
0
  UNUSED(bad);
439
440
0
  return true;
441
0
}
442
443
static int
444
0
casecompare_opt(ARGS_COMPARE) {
445
0
  return compare_opt(rdata1, rdata2);
446
0
}
447
448
isc_result_t
449
0
dns_rdata_opt_first(dns_rdata_opt_t *opt) {
450
0
  REQUIRE(opt != NULL);
451
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
452
0
  REQUIRE(opt->options != NULL || opt->length == 0);
453
454
0
  if (opt->length == 0) {
455
0
    return ISC_R_NOMORE;
456
0
  }
457
458
0
  opt->offset = 0;
459
0
  return ISC_R_SUCCESS;
460
0
}
461
462
isc_result_t
463
0
dns_rdata_opt_next(dns_rdata_opt_t *opt) {
464
0
  isc_region_t r;
465
0
  uint16_t length;
466
467
0
  REQUIRE(opt != NULL);
468
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
469
0
  REQUIRE(opt->options != NULL && opt->length != 0);
470
0
  REQUIRE(opt->offset < opt->length);
471
472
0
  INSIST(opt->offset + 4 <= opt->length);
473
0
  r.base = opt->options + opt->offset + 2;
474
0
  r.length = opt->length - opt->offset - 2;
475
0
  length = uint16_fromregion(&r);
476
0
  INSIST(opt->offset + 4 + length <= opt->length);
477
0
  opt->offset = opt->offset + 4 + length;
478
0
  if (opt->offset == opt->length) {
479
0
    return ISC_R_NOMORE;
480
0
  }
481
0
  return ISC_R_SUCCESS;
482
0
}
483
484
isc_result_t
485
0
dns_rdata_opt_current(dns_rdata_opt_t *opt, dns_rdata_opt_opcode_t *opcode) {
486
0
  isc_region_t r;
487
488
0
  REQUIRE(opt != NULL);
489
0
  REQUIRE(opcode != NULL);
490
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
491
0
  REQUIRE(opt->options != NULL);
492
0
  REQUIRE(opt->offset < opt->length);
493
494
0
  INSIST(opt->offset + 4 <= opt->length);
495
0
  r.base = opt->options + opt->offset;
496
0
  r.length = opt->length - opt->offset;
497
498
0
  opcode->opcode = uint16_fromregion(&r);
499
0
  isc_region_consume(&r, 2);
500
0
  opcode->length = uint16_fromregion(&r);
501
0
  isc_region_consume(&r, 2);
502
0
  opcode->data = r.base;
503
0
  INSIST(opt->offset + 4 + opcode->length <= opt->length);
504
505
0
  return ISC_R_SUCCESS;
506
0
}
507
508
#endif /* RDATA_GENERIC_OPT_41_C */