-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
net: add support for resolving DNS CAA records
This adds support for DNS Certification Authority Authorization (RFC 8659) to Node.js. PR-URL: #35466 Fixes: #19239 Refs: #14713 Reviewed-By: Anna Henningsen <[email protected]>
- Loading branch information
Showing
17 changed files
with
421 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
|
||
/* Copyright 2020 by <[email protected]> | ||
* | ||
* Permission to use, copy, modify, and distribute this | ||
* software and its documentation for any purpose and without | ||
* fee is hereby granted, provided that the above copyright | ||
* notice appear in all copies and that both that copyright | ||
* notice and this permission notice appear in supporting | ||
* documentation, and that the name of M.I.T. not be used in | ||
* advertising or publicity pertaining to distribution of the | ||
* software without specific, written prior permission. | ||
* M.I.T. makes no representations about the suitability of | ||
* this software for any purpose. It is provided "as is" | ||
* without express or implied warranty. | ||
*/ | ||
|
||
#include "ares_setup.h" | ||
|
||
#ifdef HAVE_NETINET_IN_H | ||
# include <netinet/in.h> | ||
#endif | ||
#ifdef HAVE_NETDB_H | ||
# include <netdb.h> | ||
#endif | ||
#ifdef HAVE_ARPA_INET_H | ||
# include <arpa/inet.h> | ||
#endif | ||
#ifdef HAVE_ARPA_NAMESER_H | ||
# include <arpa/nameser.h> | ||
#else | ||
# include "nameser.h" | ||
#endif | ||
#ifdef HAVE_ARPA_NAMESER_COMPAT_H | ||
# include <arpa/nameser_compat.h> | ||
#endif | ||
|
||
#ifdef HAVE_STRINGS_H | ||
# include <strings.h> | ||
#endif | ||
|
||
#include "ares.h" | ||
#include "ares_dns.h" | ||
#include "ares_data.h" | ||
#include "ares_private.h" | ||
|
||
#ifndef T_CAA | ||
# define T_CAA 257 /* Certification Authority Authorization */ | ||
#endif | ||
|
||
int | ||
ares_parse_caa_reply (const unsigned char *abuf, int alen, | ||
struct ares_caa_reply **caa_out) | ||
{ | ||
unsigned int qdcount, ancount, i; | ||
const unsigned char *aptr; | ||
const unsigned char *strptr; | ||
int status, rr_type, rr_class, rr_len; | ||
long len; | ||
char *hostname = NULL, *rr_name = NULL; | ||
struct ares_caa_reply *caa_head = NULL; | ||
struct ares_caa_reply *caa_last = NULL; | ||
struct ares_caa_reply *caa_curr; | ||
|
||
/* Set *caa_out to NULL for all failure cases. */ | ||
*caa_out = NULL; | ||
|
||
/* Give up if abuf doesn't have room for a header. */ | ||
if (alen < HFIXEDSZ) | ||
return ARES_EBADRESP; | ||
|
||
/* Fetch the question and answer count from the header. */ | ||
qdcount = DNS_HEADER_QDCOUNT (abuf); | ||
ancount = DNS_HEADER_ANCOUNT (abuf); | ||
if (qdcount != 1) | ||
return ARES_EBADRESP; | ||
if (ancount == 0) | ||
return ARES_ENODATA; | ||
|
||
/* Expand the name from the question, and skip past the question. */ | ||
aptr = abuf + HFIXEDSZ; | ||
status = ares_expand_name (aptr, abuf, alen, &hostname, &len); | ||
if (status != ARES_SUCCESS) | ||
return status; | ||
|
||
if (aptr + len + QFIXEDSZ > abuf + alen) | ||
{ | ||
ares_free (hostname); | ||
return ARES_EBADRESP; | ||
} | ||
aptr += len + QFIXEDSZ; | ||
|
||
/* Examine each answer resource record (RR) in turn. */ | ||
for (i = 0; i < ancount; i++) | ||
{ | ||
/* Decode the RR up to the data field. */ | ||
status = ares_expand_name (aptr, abuf, alen, &rr_name, &len); | ||
if (status != ARES_SUCCESS) | ||
{ | ||
break; | ||
} | ||
aptr += len; | ||
if (aptr + RRFIXEDSZ > abuf + alen) | ||
{ | ||
status = ARES_EBADRESP; | ||
break; | ||
} | ||
rr_type = DNS_RR_TYPE (aptr); | ||
rr_class = DNS_RR_CLASS (aptr); | ||
rr_len = DNS_RR_LEN (aptr); | ||
aptr += RRFIXEDSZ; | ||
if (aptr + rr_len > abuf + alen) | ||
{ | ||
status = ARES_EBADRESP; | ||
break; | ||
} | ||
|
||
/* Check if we are really looking at a CAA record */ | ||
if ((rr_class == C_IN || rr_class == C_CHAOS) && rr_type == T_CAA) | ||
{ | ||
strptr = aptr; | ||
|
||
/* Allocate storage for this CAA answer appending it to the list */ | ||
caa_curr = ares_malloc_data(ARES_DATATYPE_CAA_REPLY); | ||
if (!caa_curr) | ||
{ | ||
status = ARES_ENOMEM; | ||
break; | ||
} | ||
if (caa_last) | ||
{ | ||
caa_last->next = caa_curr; | ||
} | ||
else | ||
{ | ||
caa_head = caa_curr; | ||
} | ||
caa_last = caa_curr; | ||
if (rr_len < 2) | ||
{ | ||
status = ARES_EBADRESP; | ||
break; | ||
} | ||
caa_curr->critical = (int)*strptr++; | ||
caa_curr->plength = (int)*strptr++; | ||
if (caa_curr->plength <= 0 || (int)caa_curr->plength >= rr_len - 2) | ||
{ | ||
status = ARES_EBADRESP; | ||
break; | ||
} | ||
caa_curr->property = ares_malloc (caa_curr->plength + 1/* Including null byte */); | ||
if (caa_curr->property == NULL) | ||
{ | ||
status = ARES_ENOMEM; | ||
break; | ||
} | ||
memcpy ((char *) caa_curr->property, strptr, caa_curr->plength); | ||
/* Make sure we NULL-terminate */ | ||
caa_curr->property[caa_curr->plength] = 0; | ||
strptr += caa_curr->plength; | ||
|
||
caa_curr->length = rr_len - caa_curr->plength - 2; | ||
if (caa_curr->length <= 0) | ||
{ | ||
status = ARES_EBADRESP; | ||
break; | ||
} | ||
caa_curr->value = ares_malloc (caa_curr->length + 1/* Including null byte */); | ||
if (caa_curr->value == NULL) | ||
{ | ||
status = ARES_ENOMEM; | ||
break; | ||
} | ||
memcpy ((char *) caa_curr->value, strptr, caa_curr->length); | ||
/* Make sure we NULL-terminate */ | ||
caa_curr->value[caa_curr->length] = 0; | ||
} | ||
|
||
/* Propagate any failures */ | ||
if (status != ARES_SUCCESS) | ||
{ | ||
break; | ||
} | ||
|
||
/* Don't lose memory in the next iteration */ | ||
ares_free (rr_name); | ||
rr_name = NULL; | ||
|
||
/* Move on to the next record */ | ||
aptr += rr_len; | ||
} | ||
|
||
if (hostname) | ||
ares_free (hostname); | ||
if (rr_name) | ||
ares_free (rr_name); | ||
|
||
/* clean up on error */ | ||
if (status != ARES_SUCCESS) | ||
{ | ||
if (caa_head) | ||
ares_free_data (caa_head); | ||
return status; | ||
} | ||
|
||
/* everything looks fine, return the data */ | ||
*caa_out = caa_head; | ||
|
||
return ARES_SUCCESS; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,6 +81,7 @@ The following methods from the `dns` module are available: | |
* [`resolver.resolve4()`][`dns.resolve4()`] | ||
* [`resolver.resolve6()`][`dns.resolve6()`] | ||
* [`resolver.resolveAny()`][`dns.resolveAny()`] | ||
* [`resolver.resolveCaa()`][`dns.resolveCaa()`] | ||
* [`resolver.resolveCname()`][`dns.resolveCname()`] | ||
* [`resolver.resolveMx()`][`dns.resolveMx()`] | ||
* [`resolver.resolveNaptr()`][`dns.resolveNaptr()`] | ||
|
@@ -289,6 +290,7 @@ records. The type and structure of individual results varies based on `rrtype`: | |
| `'A'` | IPv4 addresses (default) | {string} | [`dns.resolve4()`][] | | ||
| `'AAAA'` | IPv6 addresses | {string} | [`dns.resolve6()`][] | | ||
| `'ANY'` | any records | {Object} | [`dns.resolveAny()`][] | | ||
| `'CAA'` | CA authorization records | {Object} | [`dns.resolveCaa()`][] | | ||
| `'CNAME'` | canonical name records | {string} | [`dns.resolveCname()`][] | | ||
| `'MX'` | mail exchange records | {Object} | [`dns.resolveMx()`][] | | ||
| `'NAPTR'` | name authority pointer records | {Object} | [`dns.resolveNaptr()`][] | | ||
|
@@ -414,6 +416,22 @@ Uses the DNS protocol to resolve `CNAME` records for the `hostname`. The | |
will contain an array of canonical name records available for the `hostname` | ||
(e.g. `['bar.example.com']`). | ||
|
||
## `dns.resolveCaa(hostname, callback)` | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
* `hostname` {string} | ||
* `callback` {Function} | ||
* `err` {Error} | ||
* `records` {Object[]} | ||
|
||
Uses the DNS protocol to resolve `CAA` records for the `hostname`. The | ||
`addresses` argument passed to the `callback` function | ||
will contain an array of certification authority authorization records | ||
available for the `hostname` (e.g. `[{critial: 0, iodef: | ||
'mailto:[email protected]'}, {critical: 128, issue: 'pki.example.com'}]`). | ||
|
||
## `dns.resolveMx(hostname, callback)` | ||
<!-- YAML | ||
added: v0.1.27 | ||
|
@@ -665,6 +683,7 @@ The following methods from the `dnsPromises` API are available: | |
* [`resolver.resolve4()`][`dnsPromises.resolve4()`] | ||
* [`resolver.resolve6()`][`dnsPromises.resolve6()`] | ||
* [`resolver.resolveAny()`][`dnsPromises.resolveAny()`] | ||
* [`resolver.resolveCaa()`][`dnsPromises.resolveCaa()`] | ||
* [`resolver.resolveCname()`][`dnsPromises.resolveCname()`] | ||
* [`resolver.resolveMx()`][`dnsPromises.resolveMx()`] | ||
* [`resolver.resolveNaptr()`][`dnsPromises.resolveNaptr()`] | ||
|
@@ -806,6 +825,7 @@ based on `rrtype`: | |
| `'A'` | IPv4 addresses (default) | {string} | [`dnsPromises.resolve4()`][] | | ||
| `'AAAA'` | IPv6 addresses | {string} | [`dnsPromises.resolve6()`][] | | ||
| `'ANY'` | any records | {Object} | [`dnsPromises.resolveAny()`][] | | ||
| `'CAA'` | CA authorization records | {Object} | [`dnsPromises.resolveCaa()`][] | | ||
| `'CNAME'` | canonical name records | {string} | [`dnsPromises.resolveCname()`][] | | ||
| `'MX'` | mail exchange records | {Object} | [`dnsPromises.resolveMx()`][] | | ||
| `'NAPTR'` | name authority pointer records | {Object} | [`dnsPromises.resolveNaptr()`][] | | ||
|
@@ -895,6 +915,19 @@ Here is an example of the result object: | |
minttl: 60 } ] | ||
``` | ||
|
||
## `dnsPromises.resolveCaa(hostname)` | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
* `hostname` {string} | ||
|
||
Uses the DNS protocol to resolve `CAA` records for the `hostname`. On success, | ||
the `Promise` is resolved with an array of objects containing available | ||
certification authority authorization records available for the `hostname` | ||
(e.g. `[{critial: 0, iodef: 'mailto:[email protected]'},{critical: 128, issue: | ||
'pki.example.com'}]`). | ||
|
||
### `dnsPromises.resolveCname(hostname)` | ||
<!-- YAML | ||
added: v10.6.0 | ||
|
@@ -1174,6 +1207,7 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_. | |
[`dns.resolve4()`]: #dns_dns_resolve4_hostname_options_callback | ||
[`dns.resolve6()`]: #dns_dns_resolve6_hostname_options_callback | ||
[`dns.resolveAny()`]: #dns_dns_resolveany_hostname_callback | ||
[`dns.resolveCaa()`]: #dns_dns_resolvecaa_hostname_callback | ||
[`dns.resolveCname()`]: #dns_dns_resolvecname_hostname_callback | ||
[`dns.resolveMx()`]: #dns_dns_resolvemx_hostname_callback | ||
[`dns.resolveNaptr()`]: #dns_dns_resolvenaptr_hostname_callback | ||
|
@@ -1190,6 +1224,7 @@ uses. For instance, _they do not use the configuration from `/etc/hosts`_. | |
[`dnsPromises.resolve4()`]: #dns_dnspromises_resolve4_hostname_options | ||
[`dnsPromises.resolve6()`]: #dns_dnspromises_resolve6_hostname_options | ||
[`dnsPromises.resolveAny()`]: #dns_dnspromises_resolveany_hostname | ||
[`dnsPromises.resolveCaa()`]: #dns_dnspromises_resolvecaa_hostname | ||
[`dnsPromises.resolveCname()`]: #dns_dnspromises_resolvecname_hostname | ||
[`dnsPromises.resolveMx()`]: #dns_dnspromises_resolvemx_hostname | ||
[`dnsPromises.resolveNaptr()`]: #dns_dnspromises_resolvenaptr_hostname | ||
|
Oops, something went wrong.