xref: /openbsd/usr.bin/dig/lib/dns/rcode.c (revision 873f12b9)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /* $Id: rcode.c,v 1.9 2020/02/26 18:47:58 florian Exp $ */
18 
19 #include <ctype.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <isc/buffer.h>
24 #include <isc/region.h>
25 #include <isc/result.h>
26 #include <isc/types.h>
27 #include <isc/util.h>
28 
29 #include <dns/cert.h>
30 #include <dns/keyvalues.h>
31 #include <dns/rcode.h>
32 #include <dns/rdataclass.h>
33 #include <dns/result.h>
34 #include <dns/secalg.h>
35 
36 #define TOTEXTONLY 0x01
37 
38 #define RCODENAMES \
39 	/* standard rcodes */ \
40 	{ dns_rcode_noerror, "NOERROR", 0}, \
41 	{ dns_rcode_formerr, "FORMERR", 0}, \
42 	{ dns_rcode_servfail, "SERVFAIL", 0}, \
43 	{ dns_rcode_nxdomain, "NXDOMAIN", 0}, \
44 	{ dns_rcode_notimp, "NOTIMP", 0}, \
45 	{ dns_rcode_refused, "REFUSED", 0}, \
46 	{ dns_rcode_yxdomain, "YXDOMAIN", 0}, \
47 	{ dns_rcode_yxrrset, "YXRRSET", 0}, \
48 	{ dns_rcode_nxrrset, "NXRRSET", 0}, \
49 	{ dns_rcode_notauth, "NOTAUTH", 0}, \
50 	{ dns_rcode_notzone, "NOTZONE", 0}, \
51 	{ 11, "RESERVED11", TOTEXTONLY}, \
52 	{ 12, "RESERVED12", TOTEXTONLY}, \
53 	{ 13, "RESERVED13", TOTEXTONLY}, \
54 	{ 14, "RESERVED14", TOTEXTONLY}, \
55 	{ 15, "RESERVED15", TOTEXTONLY},
56 
57 #define TSIGRCODENAMES \
58 	/* extended rcodes */ \
59 	{ dns_tsigerror_badsig, "BADSIG", 0}, \
60 	{ dns_tsigerror_badkey, "BADKEY", 0}, \
61 	{ dns_tsigerror_badtime, "BADTIME", 0}, \
62 	{ dns_tsigerror_badmode, "BADMODE", 0}, \
63 	{ dns_tsigerror_badname, "BADNAME", 0}, \
64 	{ dns_tsigerror_badalg, "BADALG", 0}, \
65 	{ dns_tsigerror_badtrunc, "BADTRUNC", 0}, \
66 	{ 0, NULL, 0 }
67 
68 /* RFC4398 section 2.1 */
69 
70 #define CERTNAMES \
71 	{ 1, "PKIX", 0}, \
72 	{ 2, "SPKI", 0}, \
73 	{ 3, "PGP", 0}, \
74 	{ 4, "IPKIX", 0}, \
75 	{ 5, "ISPKI", 0}, \
76 	{ 6, "IPGP", 0}, \
77 	{ 7, "ACPKIX", 0}, \
78 	{ 8, "IACPKIX", 0}, \
79 	{ 253, "URI", 0}, \
80 	{ 254, "OID", 0}, \
81 	{ 0, NULL, 0}
82 
83 /* RFC2535 section 7, RFC3110 */
84 
85 #define MD5_SECALGNAMES
86 #define DH_SECALGNAMES
87 #define DSA_SECALGNAMES
88 
89 #define SECALGNAMES \
90 	MD5_SECALGNAMES \
91 	DH_SECALGNAMES \
92 	DSA_SECALGNAMES \
93 	{ DNS_KEYALG_ECC, "ECC", 0 }, \
94 	{ DNS_KEYALG_RSASHA1, "RSASHA1", 0 }, \
95 	{ DNS_KEYALG_NSEC3RSASHA1, "NSEC3RSASHA1", 0 }, \
96 	{ DNS_KEYALG_RSASHA256, "RSASHA256", 0 }, \
97 	{ DNS_KEYALG_RSASHA512, "RSASHA512", 0 }, \
98 	{ DNS_KEYALG_ECCGOST, "ECCGOST", 0 }, \
99 	{ DNS_KEYALG_ECDSA256, "ECDSAP256SHA256", 0 }, \
100 	{ DNS_KEYALG_ECDSA384, "ECDSAP384SHA384", 0 }, \
101 	{ DNS_KEYALG_ED25519, "ED25519", 0 }, \
102 	{ DNS_KEYALG_ED448, "ED448", 0 }, \
103 	{ DNS_KEYALG_INDIRECT, "INDIRECT", 0 }, \
104 	{ DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \
105 	{ DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, \
106 	{ 0, NULL, 0}
107 
108 /* RFC2535 section 7.1 */
109 
110 struct tbl {
111 	unsigned int    value;
112 	const char      *name;
113 	int             flags;
114 };
115 
116 static struct tbl tsigrcodes[] = { RCODENAMES TSIGRCODENAMES };
117 static struct tbl certs[] = { CERTNAMES };
118 static struct tbl secalgs[] = { SECALGNAMES };
119 
120 static isc_result_t
dns_mnemonic_totext(unsigned int value,isc_buffer_t * target,struct tbl * table)121 dns_mnemonic_totext(unsigned int value, isc_buffer_t *target,
122 		    struct tbl *table)
123 {
124 	int i = 0;
125 	char buf[sizeof("4294967296")];
126 	while (table[i].name != NULL) {
127 		if (table[i].value == value) {
128 			return (isc_str_tobuffer(table[i].name, target));
129 		}
130 		i++;
131 	}
132 	snprintf(buf, sizeof(buf), "%u", value);
133 	return (isc_str_tobuffer(buf, target));
134 }
135 
136 isc_result_t
dns_tsigrcode_totext(dns_rcode_t rcode,isc_buffer_t * target)137 dns_tsigrcode_totext(dns_rcode_t rcode, isc_buffer_t *target) {
138 	return (dns_mnemonic_totext(rcode, target, tsigrcodes));
139 }
140 
141 isc_result_t
dns_cert_totext(dns_cert_t cert,isc_buffer_t * target)142 dns_cert_totext(dns_cert_t cert, isc_buffer_t *target) {
143 	return (dns_mnemonic_totext(cert, target, certs));
144 }
145 
146 isc_result_t
dns_secalg_totext(dns_secalg_t secalg,isc_buffer_t * target)147 dns_secalg_totext(dns_secalg_t secalg, isc_buffer_t *target) {
148 	return (dns_mnemonic_totext(secalg, target, secalgs));
149 }
150 
151 void
dns_secalg_format(dns_secalg_t alg,char * cp,unsigned int size)152 dns_secalg_format(dns_secalg_t alg, char *cp, unsigned int size) {
153 	isc_buffer_t b;
154 	isc_region_t r;
155 	isc_result_t result;
156 
157 	REQUIRE(cp != NULL && size > 0);
158 	isc_buffer_init(&b, cp, size - 1);
159 	result = dns_secalg_totext(alg, &b);
160 	isc_buffer_usedregion(&b, &r);
161 	r.base[r.length] = 0;
162 	if (result != ISC_R_SUCCESS)
163 		r.base[0] = 0;
164 }
165 
166 /*
167  * This uses lots of hard coded values, but how often do we actually
168  * add classes?
169  */
170 isc_result_t
dns_rdataclass_fromtext(dns_rdataclass_t * classp,isc_textregion_t * source)171 dns_rdataclass_fromtext(dns_rdataclass_t *classp, isc_textregion_t *source) {
172 #define COMPARE(string, rdclass) \
173 	if (((sizeof(string) - 1) == source->length) \
174 	    && (strncasecmp(source->base, string, source->length) == 0)) { \
175 		*classp = rdclass; \
176 		return (ISC_R_SUCCESS); \
177 	}
178 
179 	switch (tolower((unsigned char)source->base[0])) {
180 	case 'a':
181 		COMPARE("any", dns_rdataclass_any);
182 		break;
183 	case 'c':
184 		/*
185 		 * RFC1035 says the mnemonic for the CHAOS class is CH,
186 		 * but historical BIND practice is to call it CHAOS.
187 		 * We will accept both forms, but only generate CH.
188 		 */
189 		COMPARE("ch", dns_rdataclass_chaos);
190 		COMPARE("chaos", dns_rdataclass_chaos);
191 
192 		if (source->length > 5 &&
193 		    source->length < (5 + sizeof("65000")) &&
194 		    strncasecmp("class", source->base, 5) == 0) {
195 			char buf[sizeof("65000")];
196 			char *endp;
197 			unsigned int val;
198 
199 			/*
200 			 * source->base is not required to be NUL terminated.
201 			 * Copy up to remaining bytes and NUL terminate.
202 			 */
203 			snprintf(buf, sizeof(buf), "%.*s",
204 				 (int)(source->length - 5), source->base + 5);
205 			val = strtoul(buf, &endp, 10);
206 			if (*endp == '\0' && val <= 0xffff) {
207 				*classp = (dns_rdataclass_t)val;
208 				return (ISC_R_SUCCESS);
209 			}
210 		}
211 		break;
212 	case 'h':
213 		COMPARE("hs", dns_rdataclass_hs);
214 		COMPARE("hesiod", dns_rdataclass_hs);
215 		break;
216 	case 'i':
217 		COMPARE("in", dns_rdataclass_in);
218 		break;
219 	case 'n':
220 		COMPARE("none", dns_rdataclass_none);
221 		break;
222 	case 'r':
223 		COMPARE("reserved0", dns_rdataclass_reserved0);
224 		break;
225 	}
226 
227 #undef COMPARE
228 
229 	return (DNS_R_UNKNOWN);
230 }
231 
232 isc_result_t
dns_rdataclass_totext(dns_rdataclass_t rdclass,isc_buffer_t * target)233 dns_rdataclass_totext(dns_rdataclass_t rdclass, isc_buffer_t *target) {
234 	char buf[sizeof("CLASS65535")];
235 
236 	switch (rdclass) {
237 	case dns_rdataclass_any:
238 		return (isc_str_tobuffer("ANY", target));
239 	case dns_rdataclass_chaos:
240 		return (isc_str_tobuffer("CH", target));
241 	case dns_rdataclass_hs:
242 		return (isc_str_tobuffer("HS", target));
243 	case dns_rdataclass_in:
244 		return (isc_str_tobuffer("IN", target));
245 	case dns_rdataclass_none:
246 		return (isc_str_tobuffer("NONE", target));
247 	case dns_rdataclass_reserved0:
248 		return (isc_str_tobuffer("RESERVED0", target));
249 	default:
250 		snprintf(buf, sizeof(buf), "CLASS%u", rdclass);
251 		return (isc_str_tobuffer(buf, target));
252 	}
253 }
254 
255 void
dns_rdataclass_format(dns_rdataclass_t rdclass,char * array,unsigned int size)256 dns_rdataclass_format(dns_rdataclass_t rdclass,
257 		      char *array, unsigned int size)
258 {
259 	isc_result_t result;
260 	isc_buffer_t buf;
261 
262 	if (size == 0U)
263 		return;
264 
265 	isc_buffer_init(&buf, array, size);
266 	result = dns_rdataclass_totext(rdclass, &buf);
267 	/*
268 	 * Null terminate.
269 	 */
270 	if (result == ISC_R_SUCCESS) {
271 		if (isc_buffer_availablelength(&buf) >= 1)
272 			isc_buffer_putuint8(&buf, 0);
273 		else
274 			result = ISC_R_NOSPACE;
275 	}
276 	if (result != ISC_R_SUCCESS)
277 		strlcpy(array, "<unknown>", size);
278 }
279