Coverage Report

Created: 2026-09-03 06:30

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.1k
  (DNS_RDATATYPEATTR_SINGLETON | DNS_RDATATYPEATTR_META | \
21
15.1k
   DNS_RDATATYPEATTR_NOTQUESTION)
22
23
#include <isc/utf8.h>
24
25
static isc_result_t
26
3
fromtext_opt(ARGS_FROMTEXT) {
27
  /*
28
   * OPT records do not have a text format.
29
   */
30
31
3
  REQUIRE(type == dns_rdatatype_opt);
32
33
3
  UNUSED(type);
34
3
  UNUSED(rdclass);
35
3
  UNUSED(lexer);
36
3
  UNUSED(origin);
37
3
  UNUSED(options);
38
3
  UNUSED(target);
39
3
  UNUSED(callbacks);
40
41
3
  return ISC_R_NOTIMPLEMENTED;
42
3
}
43
44
static isc_result_t
45
4.19k
totext_opt(ARGS_TOTEXT) {
46
4.19k
  isc_region_t r;
47
4.19k
  isc_region_t or;
48
4.19k
  uint16_t option;
49
4.19k
  uint16_t length;
50
4.19k
  char buf[sizeof("64000 64000")];
51
52
  /*
53
   * OPT records do not have a text format.
54
   */
55
56
4.19k
  REQUIRE(rdata->type == dns_rdatatype_opt);
57
58
4.19k
  dns_rdata_toregion(rdata, &r);
59
10.2k
  while (r.length > 0) {
60
6.05k
    option = uint16_fromregion(&r);
61
6.05k
    isc_region_consume(&r, 2);
62
6.05k
    length = uint16_fromregion(&r);
63
6.05k
    isc_region_consume(&r, 2);
64
6.05k
    snprintf(buf, sizeof(buf), "%u %u", option, length);
65
6.05k
    RETERR(str_totext(buf, target));
66
6.05k
    INSIST(r.length >= length);
67
6.05k
    if (length > 0) {
68
3.83k
      if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
69
0
        RETERR(str_totext(" (", target));
70
0
      }
71
3.83k
      RETERR(str_totext(tctx->linebreak, target));
72
3.83k
      or = r;
73
3.83k
      or.length = length;
74
3.83k
      if (tctx->width == 0) { /* No splitting */
75
0
        RETERR(isc_base64_totext(&or, 60, "", target));
76
3.83k
      } else {
77
3.83k
        RETERR(isc_base64_totext(&or, tctx->width - 2,
78
3.83k
               tctx->linebreak,
79
3.83k
               target));
80
3.83k
      }
81
3.83k
      isc_region_consume(&r, length);
82
3.83k
      if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
83
0
        RETERR(str_totext(" )", target));
84
0
      }
85
3.83k
    }
86
6.05k
    if (r.length > 0) {
87
4.54k
      RETERR(str_totext(" ", target));
88
4.54k
    }
89
6.05k
  }
90
91
4.19k
  return ISC_R_SUCCESS;
92
4.19k
}
93
94
static isc_result_t
95
10.0k
fromwire_opt(ARGS_FROMWIRE) {
96
10.0k
  dns_fixedname_t fixed;
97
10.0k
  dns_name_t *name;
98
10.0k
  isc_buffer_t b;
99
10.0k
  isc_region_t sregion;
100
10.0k
  isc_region_t tregion;
101
10.0k
  isc_result_t result;
102
10.0k
  uint16_t length;
103
10.0k
  uint16_t opt;
104
10.0k
  unsigned int total;
105
106
10.0k
  REQUIRE(type == dns_rdatatype_opt);
107
108
10.0k
  UNUSED(type);
109
10.0k
  UNUSED(rdclass);
110
111
10.0k
  dctx = dns_decompress_setpermitted(dctx, false);
112
113
10.0k
  isc_buffer_activeregion(source, &sregion);
114
10.0k
  if (sregion.length == 0) {
115
4.91k
    return ISC_R_SUCCESS;
116
4.91k
  }
117
5.11k
  total = 0;
118
670k
  while (sregion.length != 0) {
119
665k
    if (sregion.length < 4) {
120
9
      return ISC_R_UNEXPECTEDEND;
121
9
    }
122
665k
    opt = uint16_fromregion(&sregion);
123
665k
    isc_region_consume(&sregion, 2);
124
665k
    length = uint16_fromregion(&sregion);
125
665k
    isc_region_consume(&sregion, 2);
126
665k
    total += 4;
127
665k
    if (sregion.length < length) {
128
30
      return ISC_R_UNEXPECTEDEND;
129
30
    }
130
665k
    switch (opt) {
131
5.18k
    case DNS_OPT_LLQ:
132
5.18k
      if (length != 18U) {
133
2
        return DNS_R_OPTERR;
134
2
      }
135
5.18k
      isc_region_consume(&sregion, length);
136
5.18k
      break;
137
15.8k
    case DNS_OPT_UL:
138
15.8k
      if (length != 4U && length != 8U) {
139
4
        return DNS_R_OPTERR;
140
4
      }
141
15.8k
      isc_region_consume(&sregion, length);
142
15.8k
      break;
143
26.4k
    case DNS_OPT_CLIENT_SUBNET: {
144
26.4k
      uint16_t family;
145
26.4k
      uint8_t addrlen;
146
26.4k
      uint8_t scope;
147
26.4k
      uint8_t addrbytes;
148
149
26.4k
      if (length < 4) {
150
3
        return DNS_R_OPTERR;
151
3
      }
152
26.4k
      family = uint16_fromregion(&sregion);
153
26.4k
      isc_region_consume(&sregion, 2);
154
26.4k
      addrlen = uint8_fromregion(&sregion);
155
26.4k
      isc_region_consume(&sregion, 1);
156
26.4k
      scope = uint8_fromregion(&sregion);
157
26.4k
      isc_region_consume(&sregion, 1);
158
159
26.4k
      switch (family) {
160
7.97k
      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.97k
        if (addrlen != 0U || scope != 0U) {
171
15
          return DNS_R_OPTERR;
172
15
        }
173
7.95k
        break;
174
7.95k
      case 1:
175
5.26k
        if (addrlen > 32U || scope > 32U) {
176
6
          return DNS_R_OPTERR;
177
6
        }
178
5.25k
        break;
179
13.2k
      case 2:
180
13.2k
        if (addrlen > 128U || scope > 128U) {
181
2
          return DNS_R_OPTERR;
182
2
        }
183
13.2k
        break;
184
13.2k
      default:
185
3
        return DNS_R_OPTERR;
186
26.4k
      }
187
26.4k
      addrbytes = (addrlen + 7) / 8;
188
26.4k
      if (addrbytes + 4 != length) {
189
12
        return DNS_R_OPTERR;
190
12
      }
191
192
26.4k
      if (addrbytes != 0U && (addrlen % 8) != 0) {
193
5.44k
        uint8_t bits = ~0U << (8 - (addrlen % 8));
194
5.44k
        bits &= sregion.base[addrbytes - 1];
195
5.44k
        if (bits != sregion.base[addrbytes - 1]) {
196
1
          return DNS_R_OPTERR;
197
1
        }
198
5.44k
      }
199
26.4k
      isc_region_consume(&sregion, addrbytes);
200
26.4k
      break;
201
26.4k
    }
202
30.6k
    case DNS_OPT_EXPIRE:
203
      /*
204
       * Request has zero length.  Response is 32 bits.
205
       */
206
30.6k
      if (length != 0 && length != 4) {
207
1
        return DNS_R_OPTERR;
208
1
      }
209
30.6k
      isc_region_consume(&sregion, length);
210
30.6k
      break;
211
2.04k
    case DNS_OPT_COOKIE:
212
      /*
213
       * Client cookie alone has length 8.
214
       * Client + server cookie is 8 + [8..32].
215
       */
216
2.04k
      if (length != 8 && (length < 16 || length > 40)) {
217
15
        return DNS_R_OPTERR;
218
15
      }
219
2.02k
      isc_region_consume(&sregion, length);
220
2.02k
      break;
221
3.30k
    case DNS_OPT_KEY_TAG:
222
3.30k
      if (length == 0 || (length % 2) != 0) {
223
3
        return DNS_R_OPTERR;
224
3
      }
225
3.29k
      isc_region_consume(&sregion, length);
226
3.29k
      break;
227
12.0k
    case DNS_OPT_EDE:
228
12.0k
      if (length < 2) {
229
1
        return DNS_R_OPTERR;
230
1
      }
231
      /* UTF-8 Byte Order Mark is not permitted. RFC 5198 */
232
12.0k
      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
12.0k
      if (!isc_utf8_valid(sregion.base + 2, length - 2)) {
241
55
        return DNS_R_OPTERR;
242
55
      }
243
12.0k
      isc_region_consume(&sregion, length);
244
12.0k
      break;
245
688
    case DNS_OPT_CLIENT_TAG:
246
688
      FALLTHROUGH;
247
1.46k
    case DNS_OPT_SERVER_TAG:
248
1.46k
      if (length != 2) {
249
4
        return DNS_R_OPTERR;
250
4
      }
251
1.46k
      isc_region_consume(&sregion, length);
252
1.46k
      break;
253
2.33k
    case DNS_OPT_REPORT_CHANNEL:
254
      /* A domain name in wire format. RFC 9567 */
255
2.33k
      if (length == 0 || length > DNS_NAME_MAXWIRE) {
256
2
        return DNS_R_OPTERR;
257
2
      }
258
2.33k
      isc_buffer_init(&b, sregion.base, length);
259
2.33k
      isc_buffer_add(&b, length);
260
2.33k
      isc_buffer_setactive(&b, length);
261
2.33k
      name = dns_fixedname_initname(&fixed);
262
2.33k
      result = dns_name_fromwire(name, &b, dctx, NULL);
263
2.33k
      if (result != ISC_R_SUCCESS || name->length != length ||
264
2.32k
          !dns_name_isabsolute(name))
265
7
      {
266
7
        return DNS_R_OPTERR;
267
7
      }
268
2.32k
      isc_region_consume(&sregion, length);
269
2.32k
      break;
270
61.6k
    case DNS_OPT_ZONEVERSION:
271
61.6k
      if (length == 0) {
272
        /* Request */
273
11.6k
        break;
274
11.6k
      }
275
      /* Labels and Type */
276
49.9k
      if (length < 2) {
277
1
        return DNS_R_OPTERR;
278
1
      }
279
      /* Type 0 (serial), length is 6. */
280
49.9k
      if (sregion.base[1] == 0 && length != 6) {
281
1
        return DNS_R_OPTERR;
282
1
      }
283
49.9k
      isc_region_consume(&sregion, length);
284
49.9k
      break;
285
504k
    default:
286
504k
      isc_region_consume(&sregion, length);
287
504k
      break;
288
665k
    }
289
665k
    total += length;
290
665k
  }
291
292
4.93k
  isc_buffer_activeregion(source, &sregion);
293
4.93k
  isc_buffer_availableregion(target, &tregion);
294
4.93k
  if (tregion.length < total) {
295
536
    return ISC_R_NOSPACE;
296
536
  }
297
4.39k
  memmove(tregion.base, sregion.base, total);
298
4.39k
  isc_buffer_forward(source, total);
299
4.39k
  isc_buffer_add(target, total);
300
301
4.39k
  return ISC_R_SUCCESS;
302
4.93k
}
303
304
static isc_result_t
305
4.38k
towire_opt(ARGS_TOWIRE) {
306
4.38k
  REQUIRE(rdata->type == dns_rdatatype_opt);
307
308
4.38k
  UNUSED(cctx);
309
310
4.38k
  return mem_tobuffer(target, rdata->data, rdata->length);
311
4.38k
}
312
313
static int
314
3.17k
compare_opt(ARGS_COMPARE) {
315
3.17k
  isc_region_t r1;
316
3.17k
  isc_region_t r2;
317
318
3.17k
  REQUIRE(rdata1->type == rdata2->type);
319
3.17k
  REQUIRE(rdata1->rdclass == rdata2->rdclass);
320
3.17k
  REQUIRE(rdata1->type == dns_rdatatype_opt);
321
322
3.17k
  dns_rdata_toregion(rdata1, &r1);
323
3.17k
  dns_rdata_toregion(rdata2, &r2);
324
3.17k
  return isc_region_compare(&r1, &r2);
325
3.17k
}
326
327
static isc_result_t
328
0
fromstruct_opt(ARGS_FROMSTRUCT) {
329
0
  dns_rdata_opt_t *opt = source;
330
0
  isc_region_t region;
331
0
  uint16_t length;
332
333
0
  REQUIRE(type == dns_rdatatype_opt);
334
0
  REQUIRE(opt != NULL);
335
0
  REQUIRE(opt->common.rdtype == type);
336
0
  REQUIRE(opt->common.rdclass == rdclass);
337
0
  REQUIRE(opt->options != NULL || opt->length == 0);
338
339
0
  UNUSED(type);
340
0
  UNUSED(rdclass);
341
342
0
  region.base = opt->options;
343
0
  region.length = opt->length;
344
0
  while (region.length >= 4) {
345
0
    isc_region_consume(&region, 2); /* opt */
346
0
    length = uint16_fromregion(&region);
347
0
    isc_region_consume(&region, 2);
348
0
    if (region.length < length) {
349
0
      return ISC_R_UNEXPECTEDEND;
350
0
    }
351
0
    isc_region_consume(&region, length);
352
0
  }
353
0
  if (region.length != 0) {
354
0
    return ISC_R_UNEXPECTEDEND;
355
0
  }
356
357
0
  return mem_tobuffer(target, opt->options, opt->length);
358
0
}
359
360
static isc_result_t
361
0
tostruct_opt(ARGS_TOSTRUCT) {
362
0
  dns_rdata_opt_t *opt = target;
363
0
  isc_region_t r;
364
365
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
366
0
  REQUIRE(opt != NULL);
367
368
0
  DNS_RDATACOMMON_INIT(opt, rdata->type, rdata->rdclass);
369
370
0
  dns_rdata_toregion(rdata, &r);
371
0
  opt->length = r.length;
372
0
  opt->options = mem_maybedup(mctx, r.base, r.length);
373
0
  opt->offset = 0;
374
0
  opt->mctx = mctx;
375
0
  return ISC_R_SUCCESS;
376
0
}
377
378
static void
379
0
freestruct_opt(ARGS_FREESTRUCT) {
380
0
  dns_rdata_opt_t *opt = source;
381
382
0
  REQUIRE(opt != NULL);
383
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
384
385
0
  if (opt->mctx == NULL) {
386
0
    return;
387
0
  }
388
389
0
  if (opt->options != NULL) {
390
0
    isc_mem_free(opt->mctx, opt->options);
391
0
  }
392
0
  opt->mctx = NULL;
393
0
}
394
395
static isc_result_t
396
0
additionaldata_opt(ARGS_ADDLDATA) {
397
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
398
399
0
  UNUSED(rdata);
400
0
  UNUSED(owner);
401
0
  UNUSED(add);
402
0
  UNUSED(arg);
403
404
0
  return ISC_R_SUCCESS;
405
0
}
406
407
static isc_result_t
408
0
digest_opt(ARGS_DIGEST) {
409
  /*
410
   * OPT records are not digested.
411
   */
412
413
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
414
415
0
  UNUSED(rdata);
416
0
  UNUSED(digest);
417
0
  UNUSED(arg);
418
419
0
  return ISC_R_NOTIMPLEMENTED;
420
0
}
421
422
static bool
423
0
checkowner_opt(ARGS_CHECKOWNER) {
424
0
  REQUIRE(type == dns_rdatatype_opt);
425
426
0
  UNUSED(type);
427
0
  UNUSED(rdclass);
428
0
  UNUSED(wildcard);
429
430
0
  return dns_name_isroot(name);
431
0
}
432
433
static bool
434
0
checknames_opt(ARGS_CHECKNAMES) {
435
0
  REQUIRE(rdata->type == dns_rdatatype_opt);
436
437
0
  UNUSED(rdata);
438
0
  UNUSED(owner);
439
0
  UNUSED(bad);
440
441
0
  return true;
442
0
}
443
444
static int
445
0
casecompare_opt(ARGS_COMPARE) {
446
0
  return compare_opt(rdata1, rdata2);
447
0
}
448
449
isc_result_t
450
0
dns_rdata_opt_first(dns_rdata_opt_t *opt) {
451
0
  REQUIRE(opt != NULL);
452
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
453
0
  REQUIRE(opt->options != NULL || opt->length == 0);
454
455
0
  if (opt->length == 0) {
456
0
    return ISC_R_NOMORE;
457
0
  }
458
459
0
  opt->offset = 0;
460
0
  return ISC_R_SUCCESS;
461
0
}
462
463
isc_result_t
464
0
dns_rdata_opt_next(dns_rdata_opt_t *opt) {
465
0
  isc_region_t r;
466
0
  uint16_t length;
467
468
0
  REQUIRE(opt != NULL);
469
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
470
0
  REQUIRE(opt->options != NULL && opt->length != 0);
471
0
  REQUIRE(opt->offset < opt->length);
472
473
0
  INSIST(opt->offset + 4 <= opt->length);
474
0
  r.base = opt->options + opt->offset + 2;
475
0
  r.length = opt->length - opt->offset - 2;
476
0
  length = uint16_fromregion(&r);
477
0
  INSIST(opt->offset + 4 + length <= opt->length);
478
0
  opt->offset = opt->offset + 4 + length;
479
0
  if (opt->offset == opt->length) {
480
0
    return ISC_R_NOMORE;
481
0
  }
482
0
  return ISC_R_SUCCESS;
483
0
}
484
485
isc_result_t
486
0
dns_rdata_opt_current(dns_rdata_opt_t *opt, dns_rdata_opt_opcode_t *opcode) {
487
0
  isc_region_t r;
488
489
0
  REQUIRE(opt != NULL);
490
0
  REQUIRE(opcode != NULL);
491
0
  REQUIRE(opt->common.rdtype == dns_rdatatype_opt);
492
0
  REQUIRE(opt->options != NULL);
493
0
  REQUIRE(opt->offset < opt->length);
494
495
0
  INSIST(opt->offset + 4 <= opt->length);
496
0
  r.base = opt->options + opt->offset;
497
0
  r.length = opt->length - opt->offset;
498
499
0
  opcode->opcode = uint16_fromregion(&r);
500
0
  isc_region_consume(&r, 2);
501
0
  opcode->length = uint16_fromregion(&r);
502
0
  isc_region_consume(&r, 2);
503
0
  opcode->data = r.base;
504
0
  INSIST(opt->offset + 4 + opcode->length <= opt->length);
505
506
0
  return ISC_R_SUCCESS;
507
0
}
508
509
#endif /* RDATA_GENERIC_OPT_41_C */