Coverage Report

Created: 2026-01-17 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/dns64.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
#include <stdbool.h>
15
#include <string.h>
16
17
#include <isc/list.h>
18
#include <isc/mem.h>
19
#include <isc/netaddr.h>
20
#include <isc/result.h>
21
#include <isc/string.h>
22
#include <isc/util.h>
23
24
#include <dns/acl.h>
25
#include <dns/dns64.h>
26
#include <dns/rdata.h>
27
#include <dns/rdatalist.h>
28
#include <dns/rdataset.h>
29
30
struct dns_dns64 {
31
  unsigned char bits[16]; /*
32
         * Prefix + suffix bits.
33
         */
34
  dns_acl_t *clients; /*
35
         * Which clients get mapped
36
         * addresses.
37
         */
38
  dns_acl_t *mapped;  /*
39
         * IPv4 addresses to be mapped.
40
         */
41
  dns_acl_t *excluded;  /*
42
         * IPv6 addresses that are
43
         * treated as not existing.
44
         */
45
  unsigned int prefixlen; /*
46
         * Start of mapped address.
47
         */
48
  unsigned int flags;
49
  isc_mem_t *mctx;
50
  ISC_LINK(dns_dns64_t) link;
51
};
52
53
void
54
dns_dns64_create(isc_mem_t *mctx, const isc_netaddr_t *prefix,
55
     unsigned int prefixlen, const isc_netaddr_t *suffix,
56
     dns_acl_t *clients, dns_acl_t *mapped, dns_acl_t *excluded,
57
0
     unsigned int flags, dns_dns64_t **dns64p) {
58
0
  dns_dns64_t *dns64;
59
0
  unsigned int nbytes = 16;
60
61
0
  REQUIRE(prefix != NULL && prefix->family == AF_INET6);
62
  /* Legal prefix lengths from rfc6052.txt. */
63
0
  REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
64
0
    prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
65
0
  REQUIRE(isc_netaddr_prefixok(prefix, prefixlen) == ISC_R_SUCCESS);
66
0
  REQUIRE(dns64p != NULL && *dns64p == NULL);
67
68
0
  if (suffix != NULL) {
69
0
    static const unsigned char zeros[16];
70
0
    REQUIRE(prefix->family == AF_INET6);
71
0
    nbytes = prefixlen / 8 + 4;
72
    /* Bits 64-71 are zeros. rfc6052.txt */
73
0
    if (prefixlen >= 32 && prefixlen <= 64) {
74
0
      nbytes++;
75
0
    }
76
0
    REQUIRE(memcmp(suffix->type.in6.s6_addr, zeros, nbytes) == 0);
77
0
  }
78
79
0
  dns64 = isc_mem_get(mctx, sizeof(dns_dns64_t));
80
0
  memset(dns64->bits, 0, sizeof(dns64->bits));
81
0
  memmove(dns64->bits, prefix->type.in6.s6_addr, prefixlen / 8);
82
0
  if (suffix != NULL) {
83
0
    memmove(dns64->bits + nbytes, suffix->type.in6.s6_addr + nbytes,
84
0
      16 - nbytes);
85
0
  }
86
0
  dns64->clients = NULL;
87
0
  if (clients != NULL) {
88
0
    dns_acl_attach(clients, &dns64->clients);
89
0
  }
90
0
  dns64->mapped = NULL;
91
0
  if (mapped != NULL) {
92
0
    dns_acl_attach(mapped, &dns64->mapped);
93
0
  }
94
0
  dns64->excluded = NULL;
95
0
  if (excluded != NULL) {
96
0
    dns_acl_attach(excluded, &dns64->excluded);
97
0
  }
98
0
  dns64->prefixlen = prefixlen;
99
0
  dns64->flags = flags;
100
0
  ISC_LINK_INIT(dns64, link);
101
0
  dns64->mctx = NULL;
102
0
  isc_mem_attach(mctx, &dns64->mctx);
103
0
  *dns64p = dns64;
104
0
}
105
106
void
107
0
dns_dns64_destroy(dns_dns64list_t *list, dns_dns64_t **dns64p) {
108
0
  dns_dns64_t *dns64;
109
110
0
  REQUIRE(dns64p != NULL && *dns64p != NULL);
111
112
0
  dns64 = *dns64p;
113
0
  *dns64p = NULL;
114
115
0
  ISC_LIST_UNLINK(*list, dns64, link);
116
117
0
  if (dns64->clients != NULL) {
118
0
    dns_acl_detach(&dns64->clients);
119
0
  }
120
0
  if (dns64->mapped != NULL) {
121
0
    dns_acl_detach(&dns64->mapped);
122
0
  }
123
0
  if (dns64->excluded != NULL) {
124
0
    dns_acl_detach(&dns64->excluded);
125
0
  }
126
0
  isc_mem_putanddetach(&dns64->mctx, dns64, sizeof(*dns64));
127
0
}
128
129
isc_result_t
130
dns_dns64_aaaafroma(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
131
        const dns_name_t *reqsigner, dns_aclenv_t *env,
132
0
        unsigned int flags, unsigned char *a, unsigned char *aaaa) {
133
0
  unsigned int nbytes, i;
134
0
  int match;
135
136
0
  if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
137
0
      (flags & DNS_DNS64_RECURSIVE) == 0)
138
0
  {
139
0
    return DNS_R_DISALLOWED;
140
0
  }
141
142
0
  if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
143
0
      (flags & DNS_DNS64_DNSSEC) != 0)
144
0
  {
145
0
    return DNS_R_DISALLOWED;
146
0
  }
147
148
0
  if (dns64->clients != NULL && reqaddr != NULL) {
149
0
    RETERR(dns_acl_match(reqaddr, reqsigner, dns64->clients, env,
150
0
             &match, NULL));
151
0
    if (match <= 0) {
152
0
      return DNS_R_DISALLOWED;
153
0
    }
154
0
  }
155
156
0
  if (dns64->mapped != NULL) {
157
0
    struct in_addr ina;
158
0
    isc_netaddr_t netaddr;
159
160
0
    memmove(&ina.s_addr, a, 4);
161
0
    isc_netaddr_fromin(&netaddr, &ina);
162
0
    RETERR(dns_acl_match(&netaddr, NULL, dns64->mapped, env, &match,
163
0
             NULL));
164
0
    if (match <= 0) {
165
0
      return DNS_R_DISALLOWED;
166
0
    }
167
0
  }
168
169
0
  nbytes = dns64->prefixlen / 8;
170
0
  INSIST(nbytes <= 12);
171
  /* Copy prefix. */
172
0
  memmove(aaaa, dns64->bits, nbytes);
173
  /* Bits 64-71 are zeros. rfc6052.txt */
174
0
  if (nbytes == 8) {
175
0
    aaaa[nbytes++] = 0;
176
0
  }
177
  /* Copy mapped address. */
178
0
  for (i = 0; i < 4U; i++) {
179
0
    aaaa[nbytes++] = a[i];
180
    /* Bits 64-71 are zeros. rfc6052.txt */
181
0
    if (nbytes == 8) {
182
0
      aaaa[nbytes++] = 0;
183
0
    }
184
0
  }
185
  /* Copy suffix. */
186
0
  memmove(aaaa + nbytes, dns64->bits + nbytes, 16 - nbytes);
187
0
  return ISC_R_SUCCESS;
188
0
}
189
190
void
191
0
dns_dns64_append(dns_dns64list_t *list, dns_dns64_t *dns64) {
192
0
  ISC_LIST_APPEND(*list, dns64, link);
193
0
}
194
195
bool
196
dns_dns64_aaaaok(const dns_dns64_t *dns64, const isc_netaddr_t *reqaddr,
197
     const dns_name_t *reqsigner, dns_aclenv_t *env,
198
     unsigned int flags, dns_rdataset_t *rdataset, bool *aaaaok,
199
0
     size_t aaaaoklen) {
200
0
  struct in6_addr in6;
201
0
  isc_netaddr_t netaddr;
202
0
  isc_result_t result;
203
0
  int match;
204
0
  bool answer = false;
205
0
  bool found = false;
206
0
  unsigned int i, ok;
207
208
0
  REQUIRE(rdataset != NULL);
209
0
  REQUIRE(rdataset->type == dns_rdatatype_aaaa);
210
0
  REQUIRE(rdataset->rdclass == dns_rdataclass_in);
211
0
  if (aaaaok != NULL) {
212
0
    REQUIRE(aaaaoklen == dns_rdataset_count(rdataset));
213
0
  }
214
215
0
  for (; dns64 != NULL; dns64 = ISC_LIST_NEXT(dns64, link)) {
216
0
    if ((dns64->flags & DNS_DNS64_RECURSIVE_ONLY) != 0 &&
217
0
        (flags & DNS_DNS64_RECURSIVE) == 0)
218
0
    {
219
0
      continue;
220
0
    }
221
222
0
    if ((dns64->flags & DNS_DNS64_BREAK_DNSSEC) == 0 &&
223
0
        (flags & DNS_DNS64_DNSSEC) != 0)
224
0
    {
225
0
      continue;
226
0
    }
227
    /*
228
     * Work out if this dns64 structure applies to this client.
229
     */
230
0
    if (dns64->clients != NULL) {
231
0
      result = dns_acl_match(reqaddr, reqsigner,
232
0
                 dns64->clients, env, &match,
233
0
                 NULL);
234
0
      if (result != ISC_R_SUCCESS) {
235
0
        continue;
236
0
      }
237
0
      if (match <= 0) {
238
0
        continue;
239
0
      }
240
0
    }
241
242
0
    if (!found && aaaaok != NULL) {
243
0
      for (i = 0; i < aaaaoklen; i++) {
244
0
        aaaaok[i] = false;
245
0
      }
246
0
    }
247
0
    found = true;
248
249
    /*
250
     * If we are not excluding any addresses then any AAAA
251
     * will do.
252
     */
253
0
    if (dns64->excluded == NULL) {
254
0
      answer = true;
255
0
      if (aaaaok == NULL) {
256
0
        goto done;
257
0
      }
258
0
      for (i = 0; i < aaaaoklen; i++) {
259
0
        aaaaok[i] = true;
260
0
      }
261
0
      goto done;
262
0
    }
263
264
0
    i = 0;
265
0
    ok = 0;
266
0
    DNS_RDATASET_FOREACH(rdataset) {
267
0
      dns_rdata_t rdata = DNS_RDATA_INIT;
268
0
      if (aaaaok == NULL || !aaaaok[i]) {
269
0
        dns_rdataset_current(rdataset, &rdata);
270
0
        memmove(&in6.s6_addr, rdata.data, 16);
271
0
        isc_netaddr_fromin6(&netaddr, &in6);
272
273
0
        result = dns_acl_match(&netaddr, NULL,
274
0
                   dns64->excluded, env,
275
0
                   &match, NULL);
276
0
        if (result == ISC_R_SUCCESS && match <= 0) {
277
0
          answer = true;
278
0
          if (aaaaok == NULL) {
279
0
            goto done;
280
0
          }
281
0
          aaaaok[i] = true;
282
0
          ok++;
283
0
        }
284
0
      } else {
285
0
        ok++;
286
0
      }
287
0
      i++;
288
0
    }
289
    /*
290
     * Are all addresses ok?
291
     */
292
0
    if (aaaaok != NULL && ok == aaaaoklen) {
293
0
      goto done;
294
0
    }
295
0
  }
296
297
0
done:
298
0
  if (!found && aaaaok != NULL) {
299
0
    for (i = 0; i < aaaaoklen; i++) {
300
0
      aaaaok[i] = true;
301
0
    }
302
0
  }
303
0
  return found ? answer : true;
304
0
}
305
306
/*
307
 * Posible mapping of IPV4ONLY.ARPA A records into AAAA records
308
 * for valid RFC6052 prefixes.
309
 */
310
static struct {
311
  const unsigned char aa[16]; /* mapped version of 192.0.0.170 */
312
  const unsigned char ab[16]; /* mapped version of 192.0.0.171 */
313
  const unsigned char mask[16];
314
  const unsigned int plen;
315
} const prefixes[6] = {
316
  { { 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0 },
317
    { 0, 0, 0, 0, 192, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, 0 },
318
    { 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0 },
319
    32 },
320
  { { 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0 },
321
    { 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0 },
322
    { 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0 },
323
    40 },
324
  { { 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0, 0 },
325
    { 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0, 0 },
326
    { 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0 },
327
    48 },
328
  { { 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 170, 0, 0, 0, 0 },
329
    { 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 171, 0, 0, 0, 0 },
330
    { 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0 },
331
    56 },
332
  { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 170, 0, 0, 0 },
333
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 171, 0, 0, 0 },
334
    { 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0 },
335
    64 },
336
  { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 170 },
337
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 171 },
338
    { 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255 },
339
    96 }
340
};
341
342
static unsigned int
343
0
search(const dns_rdata_t *rd1, const dns_rdata_t *rd2, unsigned int plen) {
344
0
  unsigned int i = 0, j;
345
0
  const unsigned char *c, *m;
346
347
  /*
348
   * Resume looking for another aa match?
349
   */
350
0
  if (plen != 0U && rd2 == NULL) {
351
0
    while (i < 6U) {
352
      /* Post increment as we resume on next entry. */
353
0
      if (prefixes[i++].plen == plen) {
354
0
        break;
355
0
      }
356
0
    }
357
0
  }
358
359
0
  for (; i < 6U; i++) {
360
0
    j = 0;
361
0
    if (rd2 != NULL) {
362
      /* Find the right entry. */
363
0
      if (prefixes[i].plen != plen) {
364
0
        continue;
365
0
      }
366
      /* Does the prefix match? */
367
0
      while ((j * 8U) < plen) {
368
0
        if (rd1->data[j] != rd2->data[j]) {
369
0
          return 0;
370
0
        }
371
0
        j++;
372
0
      }
373
0
    }
374
375
    /* Match well known mapped addresses. */
376
0
    c = (rd2 == NULL) ? prefixes[i].aa : prefixes[i].ab;
377
0
    m = prefixes[i].mask;
378
0
    for (; j < 16U; j++) {
379
0
      if ((rd1->data[j] & m[j]) != (c[j] & m[j])) {
380
0
        break;
381
0
      }
382
0
    }
383
0
    if (j == 16U) {
384
0
      return prefixes[i].plen;
385
0
    }
386
0
    if (rd2 != NULL) {
387
0
      return 0;
388
0
    }
389
0
  }
390
0
  return 0;
391
0
}
392
393
isc_result_t
394
dns_dns64_findprefix(dns_rdataset_t *rdataset, isc_netprefix_t *prefix,
395
0
         size_t *len) {
396
0
  dns_rdataset_t outer, inner;
397
0
  unsigned int oplen, iplen;
398
0
  size_t count = 0;
399
0
  struct in6_addr ina6;
400
401
0
  REQUIRE(prefix != NULL && len != NULL && *len != 0U);
402
0
  REQUIRE(rdataset != NULL && rdataset->type == dns_rdatatype_aaaa);
403
404
0
  dns_rdataset_init(&outer);
405
0
  dns_rdataset_init(&inner);
406
0
  dns_rdataset_clone(rdataset, &outer);
407
0
  dns_rdataset_clone(rdataset, &inner);
408
409
0
  DNS_RDATASET_FOREACH(&outer) {
410
0
    dns_rdata_t rd1 = DNS_RDATA_INIT;
411
0
    dns_rdataset_current(&outer, &rd1);
412
0
    oplen = 0;
413
0
  resume:
414
    /* Look for a 192.0.0.170 match. */
415
0
    oplen = search(&rd1, NULL, oplen);
416
0
    if (oplen == 0) {
417
0
      continue;
418
0
    }
419
420
    /* Look for the 192.0.0.171 match. */
421
0
    bool matched = false;
422
0
    DNS_RDATASET_FOREACH(&inner) {
423
0
      dns_rdata_t rd2 = DNS_RDATA_INIT;
424
425
0
      dns_rdataset_current(&inner, &rd2);
426
0
      iplen = search(&rd2, &rd1, oplen);
427
0
      if (iplen != 0) {
428
0
        matched = true;
429
0
        INSIST(iplen == oplen);
430
0
        if (count >= *len) {
431
0
          count++;
432
0
          break;
433
0
        }
434
435
        /* We have a prefix. */
436
0
        memset(ina6.s6_addr, 0, sizeof(ina6.s6_addr));
437
0
        memmove(ina6.s6_addr, rd1.data, oplen / 8);
438
0
        isc_netaddr_fromin6(&prefix[count].addr, &ina6);
439
0
        prefix[count].prefixlen = oplen;
440
0
        count++;
441
0
      }
442
0
    }
443
    /* Didn't find a match look for a different prefix length. */
444
0
    if (!matched) {
445
0
      goto resume;
446
0
    }
447
0
  }
448
0
  if (count == 0U) {
449
0
    return ISC_R_NOTFOUND;
450
0
  }
451
0
  if (count > *len) {
452
0
    *len = count;
453
0
    return ISC_R_NOSPACE;
454
0
  }
455
0
  *len = count;
456
0
  return ISC_R_SUCCESS;
457
0
}
458
459
isc_result_t
460
dns_dns64_apply(isc_mem_t *mctx, dns_dns64list_t dns64s, unsigned int count,
461
    dns_message_t *message, dns_aclenv_t *env, isc_sockaddr_t *peer,
462
    dns_name_t *reqsigner, unsigned int flags, dns_rdataset_t *a,
463
0
    dns_rdataset_t **aaaap) {
464
0
  isc_result_t result;
465
0
  dns_rdatalist_t *aaaalist = NULL;
466
0
  isc_buffer_t *buffer = NULL;
467
0
  isc_netaddr_t netaddr;
468
469
0
  REQUIRE(aaaap != NULL && *aaaap == NULL);
470
0
  REQUIRE(a->type == dns_rdatatype_a);
471
472
0
  isc_netaddr_fromsockaddr(&netaddr, peer);
473
474
0
  isc_buffer_allocate(mctx, &buffer, count * 16 * dns_rdataset_count(a));
475
476
0
  dns_message_gettemprdatalist(message, &aaaalist);
477
0
  aaaalist->rdclass = dns_rdataclass_in;
478
0
  aaaalist->type = dns_rdatatype_aaaa;
479
480
0
  DNS_RDATASET_FOREACH(a) {
481
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
482
0
    dns_rdataset_current(a, &rdata);
483
484
0
    ISC_LIST_FOREACH(dns64s, dns64, link) {
485
0
      dns_rdata_t *dns64_rdata = NULL;
486
0
      isc_region_t r;
487
488
0
      isc_buffer_availableregion(buffer, &r);
489
0
      INSIST(r.length >= 16);
490
0
      result = dns_dns64_aaaafroma(dns64, &netaddr, reqsigner,
491
0
                 env, flags, rdata.data,
492
0
                 r.base);
493
0
      if (result != ISC_R_SUCCESS) {
494
0
        continue;
495
0
      }
496
0
      isc_buffer_add(buffer, 16);
497
0
      isc_buffer_remainingregion(buffer, &r);
498
0
      isc_buffer_forward(buffer, 16);
499
0
      dns_message_gettemprdata(message, &dns64_rdata);
500
0
      dns_rdata_fromregion(dns64_rdata, dns_rdataclass_in,
501
0
               dns_rdatatype_aaaa, &r);
502
0
      ISC_LIST_APPEND(aaaalist->rdata, dns64_rdata, link);
503
0
    }
504
0
  }
505
506
0
  if (!ISC_LIST_EMPTY(aaaalist->rdata)) {
507
0
    dns_rdataset_t *aaaa = NULL;
508
0
    dns_message_gettemprdataset(message, &aaaa);
509
0
    dns_rdatalist_tordataset(aaaalist, aaaa);
510
0
    dns_message_takebuffer(message, &buffer);
511
0
    aaaa->trust = a->trust;
512
0
    *aaaap = aaaa;
513
0
    return ISC_R_SUCCESS;
514
0
  }
515
516
  /* No applicable dns64; free the resources */
517
0
  isc_buffer_free(&buffer);
518
0
  ISC_LIST_FOREACH(aaaalist->rdata, rdata, link) {
519
0
    ISC_LIST_UNLINK(aaaalist->rdata, rdata, link);
520
0
    dns_message_puttemprdata(message, &rdata);
521
0
  }
522
0
  dns_message_puttemprdatalist(message, &aaaalist);
523
524
0
  return ISC_R_NOMORE;
525
0
}