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 40struct 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 */ 62typedef struct dns_totext_ctx { 63 dns_master_style_t style; 64 int 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 int current_ttl_valid; 72} dns_totext_ctx_t; 73 74/*% 75 * A style suitable for dns_rdataset_totext(). 76 */ 77const dns_master_style_t 78dns_master_style_debug = { 79 DNS_STYLEFLAG_REL_OWNER, 80 24, 32, 40, 48, 80, 8, UINT_MAX 81}; 82 83#define N_SPACES 10 84static char spaces[N_SPACES+1] = " "; 85 86#define N_TABS 10 87static 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 */ 94static isc_result_t 95indent(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 154static isc_result_t 155totext_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 = 0; 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 = 0; 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 237static isc_result_t 238rdataset_totext(dns_rdataset_t *rdataset, 239 dns_name_t *owner_name, 240 dns_totext_ctx_t *ctx, 241 int omit_final_dot, 242 isc_buffer_t *target) 243{ 244 isc_result_t result; 245 unsigned int column; 246 int first = 1; 247 uint32_t current_ttl; 248 int current_ttl_valid; 249 dns_rdatatype_t type; 250 unsigned int type_start; 251 252 result = dns_rdataset_first(rdataset); 253 254 current_ttl = ctx->current_ttl; 255 current_ttl_valid = ctx->current_ttl_valid; 256 257 while (result == ISC_R_SUCCESS) { 258 column = 0; 259 260 /* 261 * Comment? 262 */ 263 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) 264 RETERR(isc_str_tobuffer(";", target)); 265 266 /* 267 * Owner name. 268 */ 269 if (owner_name != NULL && 270 ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 && 271 !first)) 272 { 273 unsigned int name_start = target->used; 274 RETERR(dns_name_totext(owner_name, 275 omit_final_dot, 276 target)); 277 column += target->used - name_start; 278 } 279 280 /* 281 * TTL. 282 */ 283 if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 && 284 !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 && 285 current_ttl_valid && 286 rdataset->ttl == current_ttl)) 287 { 288 char ttlbuf[64]; 289 isc_region_t r; 290 unsigned int length; 291 292 INDENT_TO(ttl_column); 293 length = snprintf(ttlbuf, sizeof(ttlbuf), "%u", 294 rdataset->ttl); 295 INSIST(length <= sizeof(ttlbuf)); 296 isc_buffer_availableregion(target, &r); 297 if (r.length < length) 298 return (ISC_R_NOSPACE); 299 memmove(r.base, ttlbuf, length); 300 isc_buffer_add(target, length); 301 column += length; 302 303 /* 304 * If the $TTL directive is not in use, the TTL we 305 * just printed becomes the default for subsequent RRs. 306 */ 307 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) { 308 current_ttl = rdataset->ttl; 309 current_ttl_valid = 1; 310 } 311 } 312 313 /* 314 * Class. 315 */ 316 if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 && 317 ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 || 318 !ctx->class_printed)) 319 { 320 unsigned int class_start; 321 INDENT_TO(class_column); 322 class_start = target->used; 323 result = dns_rdataclass_totext(rdataset->rdclass, 324 target); 325 if (result != ISC_R_SUCCESS) 326 return (result); 327 column += (target->used - class_start); 328 } 329 330 /* 331 * Type. 332 */ 333 334 type = rdataset->type; 335 336 INDENT_TO(type_column); 337 type_start = target->used; 338 switch (type) { 339 case dns_rdatatype_keydata: 340#define KEYDATA "KEYDATA" 341 if ((ctx->style.flags & DNS_STYLEFLAG_KEYDATA) != 0) { 342 if (isc_buffer_availablelength(target) < 343 (sizeof(KEYDATA) - 1)) 344 return (ISC_R_NOSPACE); 345 isc_buffer_putstr(target, KEYDATA); 346 break; 347 } 348 /* FALLTHROUGH */ 349 default: 350 result = dns_rdatatype_totext(type, target); 351 if (result != ISC_R_SUCCESS) 352 return (result); 353 } 354 column += (target->used - type_start); 355 356 /* 357 * Rdata. 358 */ 359 INDENT_TO(rdata_column); 360 { 361 dns_rdata_t rdata = DNS_RDATA_INIT; 362 isc_region_t r; 363 364 dns_rdataset_current(rdataset, &rdata); 365 366 RETERR(dns_rdata_tofmttext(&rdata, 367 ctx->origin, 368 (unsigned int) 369 ctx->style.flags, 370 ctx->style.line_length - 371 ctx->style.rdata_column, 372 ctx->style.split_width, 373 ctx->linebreak, 374 target)); 375 376 isc_buffer_availableregion(target, &r); 377 if (r.length < 1) 378 return (ISC_R_NOSPACE); 379 r.base[0] = '\n'; 380 isc_buffer_add(target, 1); 381 } 382 383 first = 0; 384 result = dns_rdataset_next(rdataset); 385 } 386 387 if (result != ISC_R_NOMORE) 388 return (result); 389 390 /* 391 * Update the ctx state to reflect what we just printed. 392 * This is done last, only when we are sure we will return 393 * success, because this function may be called multiple 394 * times with increasing buffer sizes until it succeeds, 395 * and failed attempts must not update the state prematurely. 396 */ 397 ctx->class_printed = 1; 398 ctx->current_ttl= current_ttl; 399 ctx->current_ttl_valid = current_ttl_valid; 400 401 return (ISC_R_SUCCESS); 402} 403 404/* 405 * Print the name, type, and class of an empty rdataset, 406 * such as those used to represent the question section 407 * of a DNS message. 408 */ 409static isc_result_t 410question_totext(dns_rdataset_t *rdataset, 411 dns_name_t *owner_name, 412 dns_totext_ctx_t *ctx, 413 int omit_final_dot, 414 isc_buffer_t *target) 415{ 416 unsigned int column; 417 isc_result_t result; 418 isc_region_t r; 419 420 result = dns_rdataset_first(rdataset); 421 REQUIRE(result == ISC_R_NOMORE); 422 423 column = 0; 424 425 /* Owner name */ 426 { 427 unsigned int name_start = target->used; 428 RETERR(dns_name_totext(owner_name, 429 omit_final_dot, 430 target)); 431 column += target->used - name_start; 432 } 433 434 /* Class */ 435 { 436 unsigned int class_start; 437 INDENT_TO(class_column); 438 class_start = target->used; 439 result = dns_rdataclass_totext(rdataset->rdclass, target); 440 if (result != ISC_R_SUCCESS) 441 return (result); 442 column += (target->used - class_start); 443 } 444 445 /* Type */ 446 { 447 unsigned int type_start; 448 INDENT_TO(type_column); 449 type_start = target->used; 450 result = dns_rdatatype_totext(rdataset->type, target); 451 if (result != ISC_R_SUCCESS) 452 return (result); 453 column += (target->used - type_start); 454 } 455 456 isc_buffer_availableregion(target, &r); 457 if (r.length < 1) 458 return (ISC_R_NOSPACE); 459 r.base[0] = '\n'; 460 isc_buffer_add(target, 1); 461 462 return (ISC_R_SUCCESS); 463} 464 465isc_result_t 466dns_rdataset_totext(dns_rdataset_t *rdataset, 467 dns_name_t *owner_name, 468 int omit_final_dot, 469 int question, 470 isc_buffer_t *target) 471{ 472 dns_totext_ctx_t ctx; 473 isc_result_t result; 474 result = totext_ctx_init(&dns_master_style_debug, &ctx); 475 if (result != ISC_R_SUCCESS) { 476 UNEXPECTED_ERROR(__FILE__, __LINE__, 477 "could not set master file style"); 478 return (ISC_R_UNEXPECTED); 479 } 480 481 /* 482 * The caller might want to give us an empty owner 483 * name (e.g. if they are outputting into a master 484 * file and this rdataset has the same name as the 485 * previous one.) 486 */ 487 if (dns_name_countlabels(owner_name) == 0) 488 owner_name = NULL; 489 490 if (question) 491 return (question_totext(rdataset, owner_name, &ctx, 492 omit_final_dot, target)); 493 else 494 return (rdataset_totext(rdataset, owner_name, &ctx, 495 omit_final_dot, target)); 496} 497 498isc_result_t 499dns_master_rdatasettotext(dns_name_t *owner_name, 500 dns_rdataset_t *rdataset, 501 const dns_master_style_t *style, 502 isc_buffer_t *target) 503{ 504 dns_totext_ctx_t ctx; 505 isc_result_t result; 506 result = totext_ctx_init(style, &ctx); 507 if (result != ISC_R_SUCCESS) { 508 UNEXPECTED_ERROR(__FILE__, __LINE__, 509 "could not set master file style"); 510 return (ISC_R_UNEXPECTED); 511 } 512 513 return (rdataset_totext(rdataset, owner_name, &ctx, 514 0, target)); 515} 516 517isc_result_t 518dns_master_questiontotext(dns_name_t *owner_name, 519 dns_rdataset_t *rdataset, 520 const dns_master_style_t *style, 521 isc_buffer_t *target) 522{ 523 dns_totext_ctx_t ctx; 524 isc_result_t result; 525 result = totext_ctx_init(style, &ctx); 526 if (result != ISC_R_SUCCESS) { 527 UNEXPECTED_ERROR(__FILE__, __LINE__, 528 "could not set master file style"); 529 return (ISC_R_UNEXPECTED); 530 } 531 532 return (question_totext(rdataset, owner_name, &ctx, 533 0, target)); 534} 535 536isc_result_t 537dns_master_stylecreate2(dns_master_style_t **stylep, unsigned int flags, 538 unsigned int ttl_column, unsigned int class_column, 539 unsigned int type_column, unsigned int rdata_column, 540 unsigned int line_length, unsigned int tab_width, 541 unsigned int split_width) 542{ 543 dns_master_style_t *style; 544 545 REQUIRE(stylep != NULL && *stylep == NULL); 546 style = malloc(sizeof(*style)); 547 if (style == NULL) 548 return (ISC_R_NOMEMORY); 549 550 style->flags = flags; 551 style->ttl_column = ttl_column; 552 style->class_column = class_column; 553 style->type_column = type_column; 554 style->rdata_column = rdata_column; 555 style->line_length = line_length; 556 style->tab_width = tab_width; 557 style->split_width = split_width; 558 559 *stylep = style; 560 return (ISC_R_SUCCESS); 561} 562 563void 564dns_master_styledestroy(dns_master_style_t **stylep) { 565 dns_master_style_t *style; 566 567 REQUIRE(stylep != NULL && *stylep != NULL); 568 style = *stylep; 569 *stylep = NULL; 570 free(style); 571} 572