Coverage Report

Created: 2026-07-30 06:31

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
17.0k
  (DNS_RDATATYPEATTR_SINGLETON | DNS_RDATATYPEATTR_META | \
21
17.0k
   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
5.03k
totext_opt(ARGS_TOTEXT) {
46
5.03k
  isc_region_t r;
47
5.03k
  isc_region_t or;
48
5.03k
  uint16_t option;
49
5.03k
  uint16_t length;
50
5.03k
  char buf[sizeof("64000 64000")];
51
52
  /*
53
   * OPT records do not have a text format.
54
   */
55
56
5.03k
  REQUIRE(rdata->type == dns_rdatatype_opt);
57
58
5.03k
  dns_rdata_toregion(rdata, &r);
59
15.7k
  while (r.length > 0) {
60
10.7k
    option = uint16_fromregion(&r);
61
10.7k
    isc_region_consume(&r, 2);
62
10.7k
    length = uint16_fromregion(&r);
63
10.7k
    isc_region_consume(&r, 2);
64
10.7k
    snprintf(buf, sizeof(buf), "%u %u", option, length);
65
10.7k
    RETERR(str_totext(buf, target));
66
10.7k
    INSIST(r.length >= length);
67
10.7k
    if (length > 0) {
68
5.80k
      if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
69
0
        RETERR(str_totext(" (", target));
70
0
      }
71
5.80k
      RETERR(str_totext(tctx->linebreak, target));
72
5.80k
      or = r;
73
5.80k
      or.length = length;
74
5.80k
      if (tctx->width == 0) { /* No splitting */
75
0
        RETERR(isc_base64_totext(&or, 60, "", target));
76
5.80k
      } else {
77
5.80k
        RETERR(isc_base64_totext(&or, tctx->width - 2,
78
5.80k
               tctx->linebreak,
79
5.80k
               target));
80
5.80k
      }
81
5.80k
      isc_region_consume(&r, length);
82
5.80k
      if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) {
83
0
        RETERR(str_totext(" )", target));
84
0
      }
85
5.80k
    }
86
10.7k
    if (r.length > 0) {
87
9.01k
      RETERR(str_totext(" ", target));
88
9.01k
    }
89
10.7k
  }
90
91
5.03k
  return ISC_R_SUCCESS;
92
5.03k
}
93
94
static isc_result_t
95
10.8k
fromwire_opt(ARGS_FROMWIRE) {
96
10.8k
  dns_fixedname_t fixed;
97
10.8k
  dns_name_t *name;
98
10.8k
  isc_buffer_t b;
99
10.8k
  isc_region_t sregion;
100
10.8k
  isc_region_t tregion;
101
10.8k
  isc_result_t result;
102
10.8k
  uint16_t length;
103
10.8k
  uint16_t opt;
104
10.8k
  unsigned int total;
105
106
10.8k
  REQUIRE(type == dns_rdatatype_opt);
107
108
10.8k
  UNUSED(type);
109
10.8k
  UNUSED(rdclass);
110
111
10.8k
  dctx = dns_decompress_setpermitted(dctx, false);
112
113
10.8k
  isc_buffer_activeregion(source, &sregion);
114
10.8k
  if (sregion.length == 0) {
115
5.54k
    return ISC_R_SUCCESS;
116
5.54k
  }
117
5.30k
  total = 0;
118
773k
  while (sregion.length != 0) {
119
768k
    if (sregion.length < 4) {
120
7
      return ISC_R_UNEXPECTEDEND;
121
7
    }
122
768k
    opt = uint16_fromregion(&sregion);
123
768k
    isc_region_consume(&sregion, 2);
124
768k
    length = uint16_fromregion(&sregion);
125
768k
    isc_region_consume(&sregion, 2);
126
768k
    total += 4;
127
768k
    if (sregion.length < length) {
128
22
      return ISC_R_UNEXPECTEDEND;
129
22
    }
130
768k
    switch (opt) {
131
6.39k
    case DNS_OPT_LLQ:
132
6.39k
      if (length != 18U) {
133
17
        return DNS_R_OPTERR;
134
17
      }
135
6.37k
      isc_region_consume(&sregion, length);
136
6.37k
      break;
137
22.1k
    case DNS_OPT_UL:
138
22.1k
      if (length != 4U && length != 8U) {
139
4
        return DNS_R_OPTERR;
140
4
      }
141
22.0k
      isc_region_consume(&sregion, length);
142
22.0k
      break;
143
24.8k
    case DNS_OPT_CLIENT_SUBNET: {
144
24.8k
      uint16_t family;
145
24.8k
      uint8_t addrlen;
146
24.8k
      uint8_t scope;
147
24.8k
      uint8_t addrbytes;
148
149
24.8k
      if (length < 4) {
150
2
        return DNS_R_OPTERR;
151
2
      }
152
24.8k
      family = uint16_fromregion(&sregion);
153
24.8k
      isc_region_consume(&sregion, 2);
154
24.8k
      addrlen = uint8_fromregion(&sregion);
155
24.8k
      isc_region_consume(&sregion, 1);
156
24.8k
      scope = uint8_fromregion(&sregion);
157
24.8k
      isc_region_consume(&sregion, 1);
158
159
24.8k
      switch (family) {
160
9.53k
      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
9.53k
        if (addrlen != 0U || scope != 0U) {
171
16
          return DNS_R_OPTERR;
172
16
        }
173
9.51k
        break;
174
9.51k
      case 1:
175
3.84k
        if (addrlen > 32U || scope > 32U) {
176
5
          return DNS_R_OPTERR;
177
5
        }
178
3.84k
        break;
179
11.4k
      case 2:
180
11.4k
        if (addrlen > 128U || scope > 128U) {
181
3
          return DNS_R_OPTERR;
182
3
        }
183
11.4k
        break;
184
11.4k
      default:
185
3
        return DNS_R_OPTERR;
186
24.8k
      }
187
24.7k
      addrbytes = (addrlen + 7) / 8;
188
24.7k
      if (addrbytes + 4 != length) {
189
14
        return DNS_R_OPTERR;
190
14
      }
191
192
24.7k
      if (addrbytes != 0U && (addrlen % 8) != 0) {
193
3.15k
        uint8_t bits = ~0U << (8 - (addrlen % 8));
194
3.15k
        bits &= sregion.base[addrbytes - 1];
195
3.15k
        if (bits != sregion.base[addrbytes - 1]) {
196
1
          return DNS_R_OPTERR;
197
1
        }
198
3.15k
      }
199
24.7k
      isc_region_consume(&sregion, addrbytes);
200
24.7k
      break;
201
24.7k
    }
202
31.3k
    case DNS_OPT_EXPIRE:
203
      /*
204
       * Request has zero length.  Response is 32 bits.
205
       */
206
31.3k
      if (length != 0 && length != 4) {
207
1
        return DNS_R_OPTERR;
208
1
      }
209
31.3k
      isc_region_consume(&sregion, length);
210
31.3k
      break;
211
2.23k
    case DNS_OPT_COOKIE:
212
      /*
213
       * Client cookie alone has length 8.
214
       * Client + server cookie is 8 + [8..32].
215
       */
216
2.23k
      if (length != 8 && (length < 16 || length > 40)) {
217
7
        return DNS_R_OPTERR;
218
7
      }
219
2.22k
      isc_region_consume(&sregion, length);
220
2.22k
      break;
221
3.39k
    case DNS_OPT_KEY_TAG:
222
3.39k
      if (length == 0 || (length % 2) != 0) {
223
2
        return DNS_R_OPTERR;
224
2
      }
225
3.39k
      isc_region_consume(&sregion, length);
226
3.39k
      break;
227
15.7k
    case DNS_OPT_EDE:
228
15.7k
      if (length < 2) {
229
1
        return DNS_R_OPTERR;
230
1
      }
231
      /* UTF-8 Byte Order Mark is not permitted. RFC 5198 */
232
15.7k
      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
15.7k
      if (!isc_utf8_valid(sregion.base + 2, length - 2)) {
241
85
        return DNS_R_OPTERR;
242
85
      }
243
15.7k
      isc_region_consume(&sregion, length);
244
15.7k
      break;
245
743
    case DNS_OPT_CLIENT_TAG:
246
743
      FALLTHROUGH;
247
1.57k
    case DNS_OPT_SERVER_TAG:
248
1.57k
      if (length != 2) {
249
6
        return DNS_R_OPTERR;
250
6
      }
251
1.56k
      isc_region_consume(&sregion, length);
252
1.56k
      break;
253
2.90k
    case DNS_OPT_REPORT_CHANNEL:
254
      /* A domain name in wire format. RFC 9567 */
255
2.90k
      if (length == 0 || length > DNS_NAME_MAXWIRE) {
256
2
        return DNS_R_OPTERR;
257
2
      }
258
2.90k
      isc_buffer_init(&b, sregion.base, length);
259
2.90k
      isc_buffer_add(&b, length);
260
2.90k
      isc_buffer_setactive(&b, length);
261
2.90k
      name = dns_fixedname_initname(&fixed);
262
2.90k
      result = dns_name_fromwire(name, &b, dctx, NULL);
263
2.90k
      if (result != ISC_R_SUCCESS || name->length != length ||
264
2.90k
          !dns_name_isabsolute(name))
265
2
      {
266
2
        return DNS_R_OPTERR;
267
2
      }
268
2.90k
      isc_region_consume(&sregion, length);
269
2.90k
      break;
270
72.4k
    case DNS_OPT_ZONEVERSION:
271
72.4k
      if (length == 0) {
272
        /* Request */
273
13.5k
        break;
274
13.5k
      }
275
      /* Labels and Type */
276
58.8k
      if (length < 2) {
277
1
        return DNS_R_OPTERR;
278
1
      }
279
      /* Type 0 (serial), length is 6. */
280
58.8k
      if (sregion.base[1] == 0 && length != 6) {
281
5
        return DNS_R_OPTERR;
282
5
      }
283
58.8k
      isc_region_consume(&sregion, length);
284
58.8k
      break;
285
585k
    default:
286
585k
      isc_region_consume(&sregion, length);
287
585k
      break;
288
768k
    }
289
768k
    total += length;
290
768k
  }
291
292
5.09k
  isc_buffer_activeregion(source, &sregion);
293
5.09k
  isc_buffer_availableregion(target, &tregion);
294
5.09k
  if (tregion.length < total) {
295
555
    return ISC_R_NOSPACE;
296
555
  }
297
4.54k
  memmove(tregion.base, sregion.base, total);
298
4.54k
  isc_buffer_forward(source, total);
299
4.54k
  isc_buffer_add(target, total);
300
301
4.54k
  return ISC_R_SUCCESS;
302
5.09k
}
303
304
static isc_result_t
305
4.79k
towire_opt(ARGS_TOWIRE) {
306
4.79k
  REQUIRE(rdata->type == dns_rdatatype_opt);
307
308
4.79k
  UNUSED(cctx);
309
310
4.79k
  return mem_tobuffer(target, rdata->data, rdata->length);
311
4.79k
}
312
313
static int
314
3.84k
compare_opt(ARGS_COMPARE) {
315
3.84k
  isc_region_t r1;
316
3.84k
  isc_region_t r2;
317
318
3.84k
  REQUIRE(rdata1->type == rdata2->type);
319
3.84k
  REQUIRE(rdata1->rdclass == rdata2->rdclass);
320
3.84k
  REQUIRE(rdata1->type == dns_rdatatype_opt);
321
322
3.84k
  dns_rdata_toregion(rdata1, &r1);
323
3.84k
  dns_rdata_toregion(rdata2, &r2);
324
3.84k
  return isc_region_compare(&r1, &r2);
325
3.84k
}
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_equal(name, dns_rootname);
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 */