Kea 2.5.5
rrparamregistry.cc
Go to the documentation of this file.
1// Copyright (C) 2010-2022 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
15
16#include <config.h>
17
18#include <cassert>
19#include <algorithm>
20#include <cctype>
21#include <functional>
22#include <map>
23#include <string>
24#include <sstream>
25#include <utility>
26
27#include <stdint.h>
28
29#include <boost/shared_ptr.hpp>
30
32
33#include <dns/rrparamregistry.h>
34#include <dns/rrclass.h>
35#include <dns/rrtype.h>
36#include <dns/rdata.h>
37#include <dns/rdataclass.h>
38
39using namespace std;
40
41using namespace isc::util;
42using namespace isc::dns::rdata;
43
44namespace isc {
45namespace dns {
46
47namespace {
53bool
54CICharLess(char c1, char c2) {
55 return (tolower(static_cast<unsigned char>(c1)) <
56 tolower(static_cast<unsigned char>(c2)));
57}
58
59struct CIStringLess {
60 bool operator()(const string& s1, const string& s2) const
61 {
62 return (lexicographical_compare(s1.begin(), s1.end(),
63 s2.begin(), s2.end(), CICharLess));
64 }
65};
66
67struct RRTypeParam {
68 RRTypeParam(const string& code_string, uint16_t code) :
69 code_string_(code_string), code_(code) {}
71 uint16_t code_;
72
74 static const unsigned int MAX_CODE = 0xffff;
75 static const string& UNKNOWN_PREFIX();
76 static size_t UNKNOWN_PREFIXLEN();
77 static const string& UNKNOWN_MAX();
78 static size_t UNKNOWN_MAXLEN();
79};
80
81typedef boost::shared_ptr<RRTypeParam> RRTypeParamPtr;
82typedef map<string, RRTypeParamPtr, CIStringLess> StrRRTypeMap;
83typedef map<uint16_t, RRTypeParamPtr> CodeRRTypeMap;
84
85inline const string&
86RRTypeParam::UNKNOWN_PREFIX() {
87 static const string p("TYPE");
88 return (p);
89}
90
91inline size_t
92RRTypeParam::UNKNOWN_PREFIXLEN() {
93 static size_t plen = UNKNOWN_PREFIX().size();
94 return (plen);
95}
96
97inline const string&
98RRTypeParam::UNKNOWN_MAX() {
99 static const string p("TYPE65535");
100 return (p);
101}
102
103inline size_t
104RRTypeParam::UNKNOWN_MAXLEN() {
105 static size_t plen = UNKNOWN_MAX().size();
106 return (plen);
107}
108
109struct RRClassParam {
110 RRClassParam(const string& code_string, uint16_t code) :
111 code_string_(code_string), code_(code) {}
112 string code_string_;
113 uint16_t code_;
114
116 static const unsigned int MAX_CODE = 0xffff;
117 static const string& UNKNOWN_PREFIX();
118 static size_t UNKNOWN_PREFIXLEN();
119 static const string& UNKNOWN_MAX();
120 static size_t UNKNOWN_MAXLEN();
121};
122
123typedef boost::shared_ptr<RRClassParam> RRClassParamPtr;
124typedef map<string, RRClassParamPtr, CIStringLess> StrRRClassMap;
125typedef map<uint16_t, RRClassParamPtr> CodeRRClassMap;
126
127inline const string&
128RRClassParam::UNKNOWN_PREFIX() {
129 static const string p("CLASS");
130 return (p);
131}
132
133inline size_t
134RRClassParam::UNKNOWN_PREFIXLEN() {
135 static size_t plen = UNKNOWN_PREFIX().size();
136 return (plen);
137}
138
139inline const string&
140RRClassParam::UNKNOWN_MAX() {
141 static const string p("CLASS65535");
142 return (p);
143}
144
145inline size_t
146RRClassParam::UNKNOWN_MAXLEN() {
147 static size_t plen = UNKNOWN_MAX().size();
148 return (plen);
149}
150} // end of anonymous namespace
151
160typedef pair<RRType, RRClass> RRTypeClass;
161typedef map<RRTypeClass, RdataFactoryPtr> RdataFactoryMap;
162typedef map<RRType, RdataFactoryPtr> GenericRdataFactoryMap;
163
164template <typename T>
166public:
167 virtual RdataPtr create(const string& rdata_str) const
168 {
169 return (RdataPtr(new T(rdata_str)));
170 }
171
172 virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const
173 {
174 return (RdataPtr(new T(buffer, rdata_len)));
175 }
176
177 virtual RdataPtr create(const Rdata& source) const
178 {
179 return (RdataPtr(new T(dynamic_cast<const T&>(source))));
180 }
181
182 virtual RdataPtr create(MasterLexer& lexer, const Name* origin,
183 MasterLoader::Options options,
184 MasterLoaderCallbacks& callbacks) const
185 {
186 return (RdataPtr(new T(lexer, origin, options, callbacks)));
187 }
188};
189
199 StrRRTypeMap str2typemap;
201 CodeRRTypeMap code2typemap;
203 StrRRClassMap str2classmap;
205 CodeRRClassMap code2classmap;
208};
209
210RRParamRegistry::RRParamRegistry() {
211 impl_ = new RRParamRegistryImpl;
212
213 // set up parameters for well-known RRs
214 try {
215 // BEGIN_WELL_KNOWN_PARAMS
216 add("A", 1, "IN", 1, RdataFactoryPtr(new RdataFactory<in::A>()));
217 add("NS", 2, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NS>()));
218 add("CNAME", 5, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::CNAME>()));
219 add("SOA", 6, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::SOA>()));
220 add("PTR", 12, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::PTR>()));
221 add("HINFO", 13, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::HINFO>()));
222 add("MINFO", 14, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::MINFO>()));
223 add("MX", 15, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::MX>()));
224 add("TXT", 16, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TXT>()));
225 add("RP", 17, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::RP>()));
226 add("AFSDB", 18, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::AFSDB>()));
227 add("AAAA", 28, "IN", 1, RdataFactoryPtr(new RdataFactory<in::AAAA>()));
228 add("SRV", 33, "IN", 1, RdataFactoryPtr(new RdataFactory<in::SRV>()));
229 add("NAPTR", 35, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NAPTR>()));
230 add("DNAME", 39, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DNAME>()));
231 add("OPT", 41, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::OPT>()));
232 add("DS", 43, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DS>()));
233 add("SSHFP", 44, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::SSHFP>()));
234 add("RRSIG", 46, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::RRSIG>()));
235 add("NSEC", 47, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NSEC>()));
236 add("DNSKEY", 48, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DNSKEY>()));
237 add("DHCID", 49, "IN", 1, RdataFactoryPtr(new RdataFactory<in::DHCID>()));
238 add("NSEC3", 50, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NSEC3>()));
239 add("NSEC3PARAM", 51, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NSEC3PARAM>()));
240 add("TLSA", 52, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TLSA>()));
241 add("SPF", 99, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::SPF>()));
242 add("TKEY", 249, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TKEY>()));
243 add("CAA", 257, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::CAA>()));
244 add("DLV", 32769, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::DLV>()));
245 add("A", 1, "CH", 3, RdataFactoryPtr(new RdataFactory<ch::A>()));
246 add("A", 1, "HS", 4, RdataFactoryPtr(new RdataFactory<hs::A>()));
247 add("TSIG", 250, "ANY", 255, RdataFactoryPtr(new RdataFactory<any::TSIG>()));
267 add("NSEC3PARAM", 51, RdataFactoryPtr(new RdataFactory<generic::NSEC3PARAM>()));
272 add("DLV", 32769, RdataFactoryPtr(new RdataFactory<generic::DLV>()));
273 // Meta and non-implemented RR types
274 addType("IXFR", 251);
275 addType("AXFR", 252);
276 addType("ANY", 255);
277 addType("MD", 3);
278 addType("MF", 4);
279 addType("MB", 7);
280 addType("MG", 8);
281 addType("MR", 9);
282 addType("NXT", 30);
283 addType("A6", 38);
284 addType("MAILA", 254);
285 addType("NULL", 10);
286 addType("WKS", 11);
287 addType("X25", 19);
288 addType("RT", 21);
289 addType("NSAP", 22);
290 addType("NSAP-PTR", 23);
291 addType("SIG", 24);
292 addType("ISDN", 20);
293 addType("KEY", 25);
294 addType("PX", 26);
295 addType("GPOS", 27);
296 addType("LOC", 29);
297 addType("KX", 36);
298 addType("CERT", 37);
299 addType("APL", 42);
300 addType("IPSECKEY", 45);
301 addType("HIP", 55);
302 addType("UNSPEC", 103);
303 addType("NID", 104);
304 addType("L32", 105);
305 addType("L64", 106);
306 addType("LP", 107);
307 addType("MAILB", 253);
308 addType("URI", 256);
309 // Meta classes
310 addClass("NONE", 254);
311 // END_WELL_KNOWN_PARAMS
312 } catch (...) {
313 delete impl_;
314 throw;
315 }
316}
317
318RRParamRegistry::~RRParamRegistry() {
319 delete impl_;
320}
321
324 static RRParamRegistry registry;
325
326 return (registry);
327}
328
329void
330RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode,
331 RdataFactoryPtr rdata_factory)
332{
333 bool type_added = false;
334 try {
335 type_added = addType(typecode_string, typecode);
336 impl_->genericrdata_factories.insert(pair<RRType, RdataFactoryPtr>(
337 RRType(typecode),
338 rdata_factory));
339 } catch (...) {
340 if (type_added) {
341 removeType(typecode);
342 }
343 throw;
344 }
345}
346
347void
348RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode,
349 const std::string& classcode_string, uint16_t classcode,
350 RdataFactoryPtr rdata_factory)
351{
352 // Rollback logic on failure is complicated. If adding the new type or
353 // class fails, we should revert to the original state, cleaning up
354 // intermediate state. But we need to make sure that we don't remove
355 // existing data. addType()/addClass() will simply ignore an attempt to
356 // add the same data, so the cleanup should be performed only when we add
357 // something new but we fail in other part of the process.
358 bool type_added = false;
359 bool class_added = false;
360
361 try {
362 type_added = addType(typecode_string, typecode);
363 class_added = addClass(classcode_string, classcode);
364 impl_->rdata_factories.insert(pair<RRTypeClass, RdataFactoryPtr>(
365 RRTypeClass(RRType(typecode),
366 RRClass(classcode)),
367 rdata_factory));
368 } catch (...) {
369 if (type_added) {
370 removeType(typecode);
371 }
372 if (class_added) {
373 removeClass(classcode);
374 }
375 throw;
376 }
377}
378
379bool
381 const RRClass& rrclass)
382{
383 RdataFactoryMap::iterator found =
384 impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass));
385 if (found != impl_->rdata_factories.end()) {
386 impl_->rdata_factories.erase(found);
387 return (true);
388 }
389
390 return (false);
391}
392
393bool
395 GenericRdataFactoryMap::iterator found =
396 impl_->genericrdata_factories.find(rrtype);
397 if (found != impl_->genericrdata_factories.end()) {
398 impl_->genericrdata_factories.erase(found);
399 return (true);
400 }
401
402 return (false);
403}
404
405namespace {
412bool CICharEqual(char c1, char c2) {
413 return (tolower(static_cast<unsigned char>(c1)) ==
414 tolower(static_cast<unsigned char>(c2)));
415}
416
417bool
418caseStringEqual(const string& s1, const string& s2, size_t n) {
419 assert(s1.size() >= n && s2.size() >= n);
420
421 return (mismatch(s1.begin(), s1.begin() + n, s2.begin(), CICharEqual).first
422 == s1.begin() + n);
423}
424
435template <typename PT, typename MC, typename MS, typename ET>
436inline bool
437addParam(const string& code_string, uint16_t code, MC& codemap, MS& stringmap)
438{
439 // Duplicate type check
440 typename MC::const_iterator found = codemap.find(code);
441 if (found != codemap.end()) {
442 if (found->second->code_string_ != code_string) {
443 isc_throw(ET, "Duplicate RR parameter registration");
444 }
445 return (false);
446 }
447
448 typedef boost::shared_ptr<PT> ParamPtr;
449 typedef pair<string, ParamPtr> StrParamPair;
450 typedef pair<uint16_t, ParamPtr> CodeParamPair;
451 ParamPtr param = ParamPtr(new PT(code_string, code));
452 try {
453 stringmap.insert(StrParamPair(code_string, param));
454 codemap.insert(CodeParamPair(code, param));
455 } catch (...) {
456 // Rollback to the previous state: not all of the erase operations will
457 // find the entry, but we don't care.
458 stringmap.erase(code_string);
459 codemap.erase(code);
460 throw;
461 }
462
463 return (true);
464}
465
466template <typename MC, typename MS>
467inline bool
468removeParam(uint16_t code, MC& codemap, MS& stringmap) {
469 typename MC::iterator found = codemap.find(code);
470
471 if (found != codemap.end()) {
472 size_t erased = stringmap.erase(found->second->code_string_);
473 // We must have a corresponding entry of the str2 map exists
474 assert(erased == 1);
475
476 codemap.erase(found);
477
478 return (true);
479 }
480
481 return (false);
482}
483
484template <typename PT, typename MS>
485inline bool
486textToCode(const string& code_str, MS& stringmap, uint16_t& ret_code) {
487 typename MS::const_iterator found;
488
489 found = stringmap.find(code_str);
490 if (found != stringmap.end()) {
491 ret_code = found->second->code_;
492 return (true);
493 }
494
495 size_t l = code_str.size();
496 if (l > PT::UNKNOWN_PREFIXLEN() &&
497 l <= PT::UNKNOWN_MAXLEN() &&
498 caseStringEqual(code_str, PT::UNKNOWN_PREFIX(),
499 PT::UNKNOWN_PREFIXLEN())) {
500 unsigned int code;
501 istringstream iss(code_str.substr(PT::UNKNOWN_PREFIXLEN(),
502 l - PT::UNKNOWN_PREFIXLEN()));
503 iss >> dec >> code;
504 if (iss.rdstate() == ios::eofbit && code <= PT::MAX_CODE) {
505 ret_code = code;
506 return (true);
507 }
508 }
509
510 return (false);
511}
512
513template <typename PT, typename MC>
514inline string
515codeToText(uint16_t code, MC& codemap) {
516 typename MC::const_iterator found;
517
518 found = codemap.find(code);
519 if (found != codemap.end()) {
520 return (found->second->code_string_);
521 }
522
523 ostringstream ss;
524 ss << code;
525 return (PT::UNKNOWN_PREFIX() + ss.str());
526}
527}
528
529bool
530RRParamRegistry::addType(const string& type_string, uint16_t code) {
531 return (addParam<RRTypeParam, CodeRRTypeMap, StrRRTypeMap, RRTypeExists>
532 (type_string, code, impl_->code2typemap, impl_->str2typemap));
533}
534
535bool
537 return (removeParam<CodeRRTypeMap, StrRRTypeMap>(code, impl_->code2typemap,
538 impl_->str2typemap));
539}
540
541bool
542RRParamRegistry::textToTypeCode(const string& type_string,
543 uint16_t& type_code) const
544{
545 return (textToCode<RRTypeParam, StrRRTypeMap>
546 (type_string, impl_->str2typemap, type_code));
547}
548
549string
551 return (codeToText<RRTypeParam, CodeRRTypeMap>(code, impl_->code2typemap));
552}
553
554bool
555RRParamRegistry::addClass(const string& class_string, uint16_t code) {
556 return (addParam<RRClassParam, CodeRRClassMap, StrRRClassMap, RRClassExists>
557 (class_string, code, impl_->code2classmap, impl_->str2classmap));
558}
559
560bool
562 return (removeParam<CodeRRClassMap, StrRRClassMap>(code,
563 impl_->code2classmap,
564 impl_->str2classmap));
565}
566
567bool
568RRParamRegistry::textToClassCode(const string& class_string,
569 uint16_t& class_code) const
570{
571 return (textToCode<RRClassParam, StrRRClassMap>
572 (class_string, impl_->str2classmap, class_code));
573}
574
575string
577 return (codeToText<RRClassParam, CodeRRClassMap>(code,
578 impl_->code2classmap));
579}
580
581namespace {
582inline const AbstractRdataFactory*
583findRdataFactory(RRParamRegistryImpl* reg_impl,
584 const RRType& rrtype, const RRClass& rrclass)
585{
586 RdataFactoryMap::const_iterator found;
587 found = reg_impl->rdata_factories.find(RRTypeClass(rrtype, rrclass));
588 if (found != reg_impl->rdata_factories.end()) {
589 return (found->second.get());
590 }
591
592 GenericRdataFactoryMap::const_iterator genfound =
593 reg_impl->genericrdata_factories.find(rrtype);
594 if (genfound != reg_impl->genericrdata_factories.end()) {
595 return (genfound->second.get());
596 }
597
598 return (NULL);
599}
600}
601
603RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
604 const std::string& rdata_string)
605{
606 // If the text indicates that it's rdata of an "unknown" type (beginning
607 // with '\# n'), parse it that way. (TBD)
608
609 const AbstractRdataFactory* factory =
610 findRdataFactory(impl_, rrtype, rrclass);
611 if (factory != NULL) {
612 return (factory->create(rdata_string));
613 }
614
615 return (RdataPtr(new generic::Generic(rdata_string)));
616}
617
619RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
620 InputBuffer& buffer, size_t rdata_len)
621{
622 const AbstractRdataFactory* factory =
623 findRdataFactory(impl_, rrtype, rrclass);
624 if (factory != NULL) {
625 return (factory->create(buffer, rdata_len));
626 }
627
628 return (RdataPtr(new generic::Generic(buffer, rdata_len)));
629}
630
632RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
633 const Rdata& source)
634{
635 const AbstractRdataFactory* factory =
636 findRdataFactory(impl_, rrtype, rrclass);
637 if (factory != NULL) {
638 return (factory->create(source));
639 }
640
642 dynamic_cast<const generic::Generic&>(source))));
643}
644
646RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
647 MasterLexer& lexer, const Name* name,
648 MasterLoader::Options options,
649 MasterLoaderCallbacks& callbacks)
650{
651 const AbstractRdataFactory* factory =
652 findRdataFactory(impl_, rrtype, rrclass);
653 if (factory != NULL) {
654 return (factory->create(lexer, name, options, callbacks));
655 }
656
657 return (RdataPtr(new generic::Generic(lexer, name, options, callbacks)));
658}
659}
660}
Tokenizer for parsing DNS master files.
Definition: master_lexer.h:301
Set of issue callbacks for a loader.
Options
Options how the parsing should work.
Definition: master_loader.h:39
The Name class encapsulates DNS names.
Definition: name.h:223
The RRClass class encapsulates DNS resource record classes.
Definition: rrclass.h:98
The RRParamRegistry class represents a registry of parameters to manipulate DNS resource records (RRs...
rdata::RdataPtr createRdata(const RRType &rrtype, const RRClass &rrclass, const std::string &rdata_string)
Create RDATA of a given pair of RR type and class from a string.
bool removeType(uint16_t type_code)
Remove mappings between RR type code and textual representation for a given type.
bool textToTypeCode(const std::string &type_string, uint16_t &type_code) const
Convert a textual representation of an RR type to the corresponding 16-bit integer code.
bool textToClassCode(const std::string &class_string, uint16_t &class_code) const
Convert a textual representation of an RR class to the corresponding 16-bit integer code.
std::string codeToClassText(uint16_t class_code) const
Convert class code into its textual representation.
std::string codeToTypeText(uint16_t type_code) const
Convert type code into its textual representation.
bool addClass(const std::string &class_string, uint16_t class_code)
Add mappings between RR class code and textual representation.
static RRParamRegistry & getRegistry()
Return the singleton instance of RRParamRegistry.
bool addType(const std::string &type_string, uint16_t type_code)
Add mappings between RR type code and textual representation.
bool removeRdataFactory(const RRType &rrtype, const RRClass &rrclass)
Remove registered RDATA factory for the given pair of RRType and RRClass.
void add(const std::string &type_string, uint16_t type_code, const std::string &class_string, uint16_t class_code, rdata::RdataFactoryPtr rdata_factory)
Add a set of parameters for a pair of RR type and class.
bool removeClass(uint16_t class_code)
Remove mappings between RR class code and textual representation for a given class.
The RRType class encapsulates DNS resource record types.
Definition: rrtype.h:106
virtual RdataPtr create(const string &rdata_str) const
Create RDATA from a string.
virtual RdataPtr create(InputBuffer &buffer, size_t rdata_len) const
Create RDATA from wire-format data.
virtual RdataPtr create(MasterLexer &lexer, const Name *origin, MasterLoader::Options options, MasterLoaderCallbacks &callbacks) const
Create RDATA using MasterLexer.
virtual RdataPtr create(const Rdata &source) const
Create RDATA from another Rdata object of the same type.
The AbstractRdataFactory class is an abstract base class to encapsulate a set of Rdata factory method...
virtual RdataPtr create(const std::string &rdata_str) const =0
Create RDATA from a string.
The Rdata class is an abstract base class that provides a set of common interfaces to manipulate conc...
Definition: rdata.h:123
The generic::Generic class represents generic "unknown" RDATA.
Definition: rdata.h:249
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< AbstractRdataFactory > RdataFactoryPtr
The RdataFactoryPtr type is a pointer-like type, pointing to an object of some concrete derived class...
boost::shared_ptr< Rdata > RdataPtr
The RdataPtr type is a pointer-like type, pointing to an object of some concrete derived class of Rda...
pair< RRType, RRClass > RRTypeClass
Note: the element ordering in the type/class pair is intentional.
map< RRTypeClass, RdataFactoryPtr > RdataFactoryMap
map< RRType, RdataFactoryPtr > GenericRdataFactoryMap
Definition: edns.h:19
Defines the logger used by the top-level component of kea-lfc.
string code_string_
uint16_t code_
The RRParamRegistryImpl class is the actual implementation of RRParamRegistry.
CodeRRTypeMap code2typemap
Mappings from textual representations of RR types to integer codes.
StrRRClassMap str2classmap
Mappings from RR class codes to textual representations.
CodeRRClassMap code2classmap
Mappings from textual representations of RR classes to integer codes.
GenericRdataFactoryMap genericrdata_factories
StrRRTypeMap str2typemap
Mappings from RR type codes to textual representations.