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 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 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 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 147 dns_secalg_totext(dns_secalg_t secalg, isc_buffer_t *target) { 148 return (dns_mnemonic_totext(secalg, target, secalgs)); 149 } 150 151 void 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 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 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 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