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 /*! \file */ 18 19 #include <limits.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include <isc/types.h> 24 #include <isc/util.h> 25 26 #include <dns/fixedname.h> 27 #include <dns/masterdump.h> 28 #include <dns/rdata.h> 29 #include <dns/rdataclass.h> 30 #include <dns/rdataset.h> 31 #include <dns/rdatatype.h> 32 #include <dns/result.h> 33 34 #define RETERR(x) do { \ 35 isc_result_t _r = (x); \ 36 if (_r != ISC_R_SUCCESS) \ 37 return (_r); \ 38 } while (0) 39 40 struct dns_master_style { 41 dns_masterstyle_flags_t flags; /* DNS_STYLEFLAG_* */ 42 unsigned int ttl_column; 43 unsigned int class_column; 44 unsigned int type_column; 45 unsigned int rdata_column; 46 unsigned int line_length; 47 unsigned int tab_width; 48 unsigned int split_width; 49 }; 50 51 /*% 52 * The maximum length of the newline+indentation that is output 53 * when inserting a line break in an RR. This effectively puts an 54 * upper limits on the value of "rdata_column", because if it is 55 * very large, the tabs and spaces needed to reach it will not fit. 56 */ 57 #define DNS_TOTEXT_LINEBREAK_MAXLEN 100 58 59 /*% 60 * Context structure for a masterfile dump in progress. 61 */ 62 typedef struct dns_totext_ctx { 63 dns_master_style_t style; 64 isc_boolean_t class_printed; 65 char * linebreak; 66 char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN]; 67 dns_name_t * origin; 68 dns_name_t * neworigin; 69 dns_fixedname_t origin_fixname; 70 uint32_t current_ttl; 71 isc_boolean_t current_ttl_valid; 72 } dns_totext_ctx_t; 73 74 /*% 75 * A style suitable for dns_rdataset_totext(). 76 */ 77 const dns_master_style_t 78 dns_master_style_debug = { 79 DNS_STYLEFLAG_REL_OWNER, 80 24, 32, 40, 48, 80, 8, UINT_MAX 81 }; 82 83 #define N_SPACES 10 84 static char spaces[N_SPACES+1] = " "; 85 86 #define N_TABS 10 87 static char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t"; 88 89 /*% 90 * Output tabs and spaces to go from column '*current' to 91 * column 'to', and update '*current' to reflect the new 92 * current column. 93 */ 94 static isc_result_t 95 indent(unsigned int *current, unsigned int to, int tabwidth, 96 isc_buffer_t *target) 97 { 98 isc_region_t r; 99 unsigned char *p; 100 unsigned int from; 101 int ntabs, nspaces, t; 102 103 from = *current; 104 105 if (to < from + 1) 106 to = from + 1; 107 108 ntabs = to / tabwidth - from / tabwidth; 109 if (ntabs < 0) 110 ntabs = 0; 111 112 if (ntabs > 0) { 113 isc_buffer_availableregion(target, &r); 114 if (r.length < (unsigned) ntabs) 115 return (ISC_R_NOSPACE); 116 p = r.base; 117 118 t = ntabs; 119 while (t) { 120 int n = t; 121 if (n > N_TABS) 122 n = N_TABS; 123 memmove(p, tabs, n); 124 p += n; 125 t -= n; 126 } 127 isc_buffer_add(target, ntabs); 128 from = (to / tabwidth) * tabwidth; 129 } 130 131 nspaces = to - from; 132 INSIST(nspaces >= 0); 133 134 isc_buffer_availableregion(target, &r); 135 if (r.length < (unsigned) nspaces) 136 return (ISC_R_NOSPACE); 137 p = r.base; 138 139 t = nspaces; 140 while (t) { 141 int n = t; 142 if (n > N_SPACES) 143 n = N_SPACES; 144 memmove(p, spaces, n); 145 p += n; 146 t -= n; 147 } 148 isc_buffer_add(target, nspaces); 149 150 *current = to; 151 return (ISC_R_SUCCESS); 152 } 153 154 static isc_result_t 155 totext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) { 156 isc_result_t result; 157 158 REQUIRE(style->tab_width != 0); 159 160 ctx->style = *style; 161 ctx->class_printed = ISC_FALSE; 162 163 dns_fixedname_init(&ctx->origin_fixname); 164 165 /* 166 * Set up the line break string if needed. 167 */ 168 if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) { 169 isc_buffer_t buf; 170 isc_region_t r; 171 unsigned int col = 0; 172 173 isc_buffer_init(&buf, ctx->linebreak_buf, 174 sizeof(ctx->linebreak_buf)); 175 176 isc_buffer_availableregion(&buf, &r); 177 if (r.length < 1) 178 return (DNS_R_TEXTTOOLONG); 179 r.base[0] = '\n'; 180 isc_buffer_add(&buf, 1); 181 182 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) { 183 isc_buffer_availableregion(&buf, &r); 184 if (r.length < 1) 185 return (DNS_R_TEXTTOOLONG); 186 r.base[0] = ';'; 187 isc_buffer_add(&buf, 1); 188 } 189 190 result = indent(&col, ctx->style.rdata_column, 191 ctx->style.tab_width, &buf); 192 /* 193 * Do not return ISC_R_NOSPACE if the line break string 194 * buffer is too small, because that would just make 195 * dump_rdataset() retry indefinitely with ever 196 * bigger target buffers. That's a different buffer, 197 * so it won't help. Use DNS_R_TEXTTOOLONG as a substitute. 198 */ 199 if (result == ISC_R_NOSPACE) 200 return (DNS_R_TEXTTOOLONG); 201 if (result != ISC_R_SUCCESS) 202 return (result); 203 204 isc_buffer_availableregion(&buf, &r); 205 if (r.length < 1) 206 return (DNS_R_TEXTTOOLONG); 207 r.base[0] = '\0'; 208 isc_buffer_add(&buf, 1); 209 ctx->linebreak = ctx->linebreak_buf; 210 } else { 211 ctx->linebreak = NULL; 212 } 213 214 ctx->origin = NULL; 215 ctx->neworigin = NULL; 216 ctx->current_ttl = 0; 217 ctx->current_ttl_valid = ISC_FALSE; 218 219 return (ISC_R_SUCCESS); 220 } 221 222 #define INDENT_TO(col) \ 223 do { \ 224 if ((result = indent(&column, ctx->style.col, \ 225 ctx->style.tab_width, target)) \ 226 != ISC_R_SUCCESS) \ 227 return (result); \ 228 } while (0) 229 230 /* 231 * Convert 'rdataset' to master file text format according to 'ctx', 232 * storing the result in 'target'. If 'owner_name' is NULL, it 233 * is omitted; otherwise 'owner_name' must be valid and have at least 234 * one label. 235 */ 236 237 static isc_result_t 238 rdataset_totext(dns_rdataset_t *rdataset, 239 dns_name_t *owner_name, 240 dns_totext_ctx_t *ctx, 241 isc_boolean_t omit_final_dot, 242 isc_buffer_t *target) 243 { 244 isc_result_t result; 245 unsigned int column; 246 isc_boolean_t first = ISC_TRUE; 247 uint32_t current_ttl; 248 isc_boolean_t current_ttl_valid; 249 dns_rdatatype_t type; 250 unsigned int type_start; 251 252 rdataset->attributes |= DNS_RDATASETATTR_LOADORDER; 253 result = dns_rdataset_first(rdataset); 254 255 current_ttl = ctx->current_ttl; 256 current_ttl_valid = ctx->current_ttl_valid; 257 258 while (result == ISC_R_SUCCESS) { 259 column = 0; 260 261 /* 262 * Comment? 263 */ 264 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) 265 RETERR(isc_str_tobuffer(";", target)); 266 267 /* 268 * Owner name. 269 */ 270 if (owner_name != NULL && 271 ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 && 272 !first)) 273 { 274 unsigned int name_start = target->used; 275 RETERR(dns_name_totext(owner_name, 276 omit_final_dot, 277 target)); 278 column += target->used - name_start; 279 } 280 281 /* 282 * TTL. 283 */ 284 if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 && 285 !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 && 286 current_ttl_valid && 287 rdataset->ttl == current_ttl)) 288 { 289 char ttlbuf[64]; 290 isc_region_t r; 291 unsigned int length; 292 293 INDENT_TO(ttl_column); 294 length = snprintf(ttlbuf, sizeof(ttlbuf), "%u", 295 rdataset->ttl); 296 INSIST(length <= sizeof(ttlbuf)); 297 isc_buffer_availableregion(target, &r); 298 if (r.length < length) 299 return (ISC_R_NOSPACE); 300 memmove(r.base, ttlbuf, length); 301 isc_buffer_add(target, length); 302 column += length; 303 304 /* 305 * If the $TTL directive is not in use, the TTL we 306 * just printed becomes the default for subsequent RRs. 307 */ 308 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) { 309 current_ttl = rdataset->ttl; 310 current_ttl_valid = ISC_TRUE; 311 } 312 } 313 314 /* 315 * Class. 316 */ 317 if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 && 318 ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 || 319 ctx->class_printed == ISC_FALSE)) 320 { 321 unsigned int class_start; 322 INDENT_TO(class_column); 323 class_start = target->used; 324 result = dns_rdataclass_totext(rdataset->rdclass, 325 target); 326 if (result != ISC_R_SUCCESS) 327 return (result); 328 column += (target->used - class_start); 329 } 330 331 /* 332 * Type. 333 */ 334 335 type = rdataset->type; 336 337 INDENT_TO(type_column); 338 type_start = target->used; 339 switch (type) { 340 case dns_rdatatype_keydata: 341 #define KEYDATA "KEYDATA" 342 if ((ctx->style.flags & DNS_STYLEFLAG_KEYDATA) != 0) { 343 if (isc_buffer_availablelength(target) < 344 (sizeof(KEYDATA) - 1)) 345 return (ISC_R_NOSPACE); 346 isc_buffer_putstr(target, KEYDATA); 347 break; 348 } 349 /* FALLTHROUGH */ 350 default: 351 result = dns_rdatatype_totext(type, target); 352 if (result != ISC_R_SUCCESS) 353 return (result); 354 } 355 column += (target->used - type_start); 356 357 /* 358 * Rdata. 359 */ 360 INDENT_TO(rdata_column); 361 { 362 dns_rdata_t rdata = DNS_RDATA_INIT; 363 isc_region_t r; 364 365 dns_rdataset_current(rdataset, &rdata); 366 367 RETERR(dns_rdata_tofmttext(&rdata, 368 ctx->origin, 369 (unsigned int) 370 ctx->style.flags, 371 ctx->style.line_length - 372 ctx->style.rdata_column, 373 ctx->style.split_width, 374 ctx->linebreak, 375 target)); 376 377 isc_buffer_availableregion(target, &r); 378 if (r.length < 1) 379 return (ISC_R_NOSPACE); 380 r.base[0] = '\n'; 381 isc_buffer_add(target, 1); 382 } 383 384 first = ISC_FALSE; 385 result = dns_rdataset_next(rdataset); 386 } 387 388 if (result != ISC_R_NOMORE) 389 return (result); 390 391 /* 392 * Update the ctx state to reflect what we just printed. 393 * This is done last, only when we are sure we will return 394 * success, because this function may be called multiple 395 * times with increasing buffer sizes until it succeeds, 396 * and failed attempts must not update the state prematurely. 397 */ 398 ctx->class_printed = ISC_TRUE; 399 ctx->current_ttl= current_ttl; 400 ctx->current_ttl_valid = current_ttl_valid; 401 402 return (ISC_R_SUCCESS); 403 } 404 405 /* 406 * Print the name, type, and class of an empty rdataset, 407 * such as those used to represent the question section 408 * of a DNS message. 409 */ 410 static isc_result_t 411 question_totext(dns_rdataset_t *rdataset, 412 dns_name_t *owner_name, 413 dns_totext_ctx_t *ctx, 414 isc_boolean_t omit_final_dot, 415 isc_buffer_t *target) 416 { 417 unsigned int column; 418 isc_result_t result; 419 isc_region_t r; 420 421 result = dns_rdataset_first(rdataset); 422 REQUIRE(result == ISC_R_NOMORE); 423 424 column = 0; 425 426 /* Owner name */ 427 { 428 unsigned int name_start = target->used; 429 RETERR(dns_name_totext(owner_name, 430 omit_final_dot, 431 target)); 432 column += target->used - name_start; 433 } 434 435 /* Class */ 436 { 437 unsigned int class_start; 438 INDENT_TO(class_column); 439 class_start = target->used; 440 result = dns_rdataclass_totext(rdataset->rdclass, target); 441 if (result != ISC_R_SUCCESS) 442 return (result); 443 column += (target->used - class_start); 444 } 445 446 /* Type */ 447 { 448 unsigned int type_start; 449 INDENT_TO(type_column); 450 type_start = target->used; 451 result = dns_rdatatype_totext(rdataset->type, target); 452 if (result != ISC_R_SUCCESS) 453 return (result); 454 column += (target->used - type_start); 455 } 456 457 isc_buffer_availableregion(target, &r); 458 if (r.length < 1) 459 return (ISC_R_NOSPACE); 460 r.base[0] = '\n'; 461 isc_buffer_add(target, 1); 462 463 return (ISC_R_SUCCESS); 464 } 465 466 isc_result_t 467 dns_rdataset_totext(dns_rdataset_t *rdataset, 468 dns_name_t *owner_name, 469 isc_boolean_t omit_final_dot, 470 isc_boolean_t question, 471 isc_buffer_t *target) 472 { 473 dns_totext_ctx_t ctx; 474 isc_result_t result; 475 result = totext_ctx_init(&dns_master_style_debug, &ctx); 476 if (result != ISC_R_SUCCESS) { 477 UNEXPECTED_ERROR(__FILE__, __LINE__, 478 "could not set master file style"); 479 return (ISC_R_UNEXPECTED); 480 } 481 482 /* 483 * The caller might want to give us an empty owner 484 * name (e.g. if they are outputting into a master 485 * file and this rdataset has the same name as the 486 * previous one.) 487 */ 488 if (dns_name_countlabels(owner_name) == 0) 489 owner_name = NULL; 490 491 if (question) 492 return (question_totext(rdataset, owner_name, &ctx, 493 omit_final_dot, target)); 494 else 495 return (rdataset_totext(rdataset, owner_name, &ctx, 496 omit_final_dot, target)); 497 } 498 499 isc_result_t 500 dns_master_rdatasettotext(dns_name_t *owner_name, 501 dns_rdataset_t *rdataset, 502 const dns_master_style_t *style, 503 isc_buffer_t *target) 504 { 505 dns_totext_ctx_t ctx; 506 isc_result_t result; 507 result = totext_ctx_init(style, &ctx); 508 if (result != ISC_R_SUCCESS) { 509 UNEXPECTED_ERROR(__FILE__, __LINE__, 510 "could not set master file style"); 511 return (ISC_R_UNEXPECTED); 512 } 513 514 return (rdataset_totext(rdataset, owner_name, &ctx, 515 ISC_FALSE, target)); 516 } 517 518 isc_result_t 519 dns_master_questiontotext(dns_name_t *owner_name, 520 dns_rdataset_t *rdataset, 521 const dns_master_style_t *style, 522 isc_buffer_t *target) 523 { 524 dns_totext_ctx_t ctx; 525 isc_result_t result; 526 result = totext_ctx_init(style, &ctx); 527 if (result != ISC_R_SUCCESS) { 528 UNEXPECTED_ERROR(__FILE__, __LINE__, 529 "could not set master file style"); 530 return (ISC_R_UNEXPECTED); 531 } 532 533 return (question_totext(rdataset, owner_name, &ctx, 534 ISC_FALSE, target)); 535 } 536 537 isc_result_t 538 dns_master_stylecreate2(dns_master_style_t **stylep, unsigned int flags, 539 unsigned int ttl_column, unsigned int class_column, 540 unsigned int type_column, unsigned int rdata_column, 541 unsigned int line_length, unsigned int tab_width, 542 unsigned int split_width) 543 { 544 dns_master_style_t *style; 545 546 REQUIRE(stylep != NULL && *stylep == NULL); 547 style = malloc(sizeof(*style)); 548 if (style == NULL) 549 return (ISC_R_NOMEMORY); 550 551 style->flags = flags; 552 style->ttl_column = ttl_column; 553 style->class_column = class_column; 554 style->type_column = type_column; 555 style->rdata_column = rdata_column; 556 style->line_length = line_length; 557 style->tab_width = tab_width; 558 style->split_width = split_width; 559 560 *stylep = style; 561 return (ISC_R_SUCCESS); 562 } 563 564 void 565 dns_master_styledestroy(dns_master_style_t **stylep) { 566 dns_master_style_t *style; 567 568 REQUIRE(stylep != NULL && *stylep != NULL); 569 style = *stylep; 570 *stylep = NULL; 571 free(style); 572 } 573