masterdump.c revision 2674e1a455d4f71de09b2b60e7a8304b9a305588
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews/*
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
a7038d1a0513c8e804937ebc95fc9cb3a46c04f5Mark Andrews * Copyright (C) 1999-2003 Internet Software Consortium.
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence *
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater * Permission to use, copy, modify, and distribute this software for any
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * purpose with or without fee is hereby granted, provided that the above
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * copyright notice and this permission notice appear in all copies.
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence *
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * PERFORMANCE OF THIS SOFTWARE.
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews */
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
3d17a3ba61a303d5c4d9867068d0fbe9f24d2988Mark Andrews/* $Id: masterdump.c,v 1.83 2005/11/30 03:33:49 marka Exp $ */
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff
f31f0b63cbe841720f154c570bcdede9d79e64b8Michael Graff/*! \file */
3761c433912beabe43abeed2c3513b6201c59f64Mark Andrews
854d0238dbc2908490197984b3b9d558008a53dfMark Andrews#include <config.h>
854d0238dbc2908490197984b3b9d558008a53dfMark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <stdlib.h>
6324997211a5e2d82528dcde98e8981190a35faeMichael Graff
6324997211a5e2d82528dcde98e8981190a35faeMichael Graff#include <isc/event.h>
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff#include <isc/file.h>
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence#include <isc/magic.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <isc/mem.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <isc/print.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <isc/stdio.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <isc/string.h>
90880946803188d7c6b3ca7dea69761eb21241c2Mark Andrews#include <isc/task.h>
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews#include <isc/time.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <isc/util.h>
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews#include <dns/db.h>
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington#include <dns/dbiterator.h>
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington#include <dns/events.h>
bddfe77128b0f16af263ff149db40f0d885f43d0Mark Andrews#include <dns/fixedname.h>
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington#include <dns/lib.h>
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff#include <dns/log.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <dns/master.h>
f1b68725503ff3e46001eee5a1751e29a43a09d1Andreas Gustafsson#include <dns/masterdump.h>
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington#include <dns/rdata.h>
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington#include <dns/rdataclass.h>
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington#include <dns/rdataset.h>
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#include <dns/rdatasetiter.h>
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews#include <dns/rdatatype.h>
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence#include <dns/result.h>
9ac7076ebad044afb15e9e2687e3696868778538Mark Andrews#include <dns/time.h>
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews#include <dns/ttl.h>
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews#define DNS_DCTX_MAGIC ISC_MAGIC('D', 'c', 't', 'x')
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews#define DNS_DCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews#define RETERR(x) do { \
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews isc_result_t _r = (x); \
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews if (_r != ISC_R_SUCCESS) \
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews return (_r); \
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews } while (0)
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrewsstruct dns_master_style {
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews unsigned int flags; /* DNS_STYLEFLAG_* */
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews unsigned int ttl_column;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews unsigned int class_column;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews unsigned int type_column;
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews unsigned int rdata_column;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews unsigned int line_length;
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington unsigned int tab_width;
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington};
90880946803188d7c6b3ca7dea69761eb21241c2Mark Andrews
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff/*%
90880946803188d7c6b3ca7dea69761eb21241c2Mark Andrews * The maximum length of the newline+indentation that is output
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington * when inserting a line break in an RR. This effectively puts an
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington * upper limits on the value of "rdata_column", because if it is
add4043305ca411202ed9cf1929a4179016515ceBrian Wellington * very large, the tabs and spaces needed to reach it will not fit.
9ac7076ebad044afb15e9e2687e3696868778538Mark Andrews */
90880946803188d7c6b3ca7dea69761eb21241c2Mark Andrews#define DNS_TOTEXT_LINEBREAK_MAXLEN 100
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson/*%
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff * Context structure for a masterfile dump in progress.
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews */
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewstypedef struct dns_totext_ctx {
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence dns_master_style_t style;
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson isc_boolean_t class_printed;
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff char * linebreak;
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff dns_name_t * origin;
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence dns_name_t * neworigin;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_fixedname_t origin_fixname;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_uint32_t current_ttl;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_boolean_t current_ttl_valid;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews} dns_totext_ctx_t;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark AndrewsLIBDNS_EXTERNAL_DATA const dns_master_style_t
9ce72fc748cc7c2b738147f9736e00a96474be1bBrian Wellingtondns_master_style_default = {
9f139761ca06977d1db8051842efc620c15b8199Andreas Gustafsson DNS_STYLEFLAG_OMIT_OWNER |
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_OMIT_CLASS |
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews DNS_STYLEFLAG_REL_OWNER |
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews DNS_STYLEFLAG_REL_DATA |
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_OMIT_TTL |
9f139761ca06977d1db8051842efc620c15b8199Andreas Gustafsson DNS_STYLEFLAG_TTL |
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews DNS_STYLEFLAG_COMMENT |
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews DNS_STYLEFLAG_MULTILINE,
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews 24, 24, 24, 32, 80, 8
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews};
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater
9ce72fc748cc7c2b738147f9736e00a96474be1bBrian WellingtonLIBDNS_EXTERNAL_DATA const dns_master_style_t
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsdns_master_style_full = {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_COMMENT,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews 46, 46, 46, 64, 120, 8
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews};
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark AndrewsLIBDNS_EXTERNAL_DATA const dns_master_style_t
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsdns_master_style_explicitttl = {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_OMIT_OWNER |
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_OMIT_CLASS |
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_REL_OWNER |
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_REL_DATA |
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_COMMENT |
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson DNS_STYLEFLAG_MULTILINE,
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews 24, 32, 32, 40, 80, 8
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence};
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark AndrewsLIBDNS_EXTERNAL_DATA const dns_master_style_t
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafssondns_master_style_cache = {
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews DNS_STYLEFLAG_OMIT_OWNER |
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews DNS_STYLEFLAG_OMIT_CLASS |
9ce72fc748cc7c2b738147f9736e00a96474be1bBrian Wellington DNS_STYLEFLAG_MULTILINE |
9ce72fc748cc7c2b738147f9736e00a96474be1bBrian Wellington DNS_STYLEFLAG_TRUST |
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson DNS_STYLEFLAG_NCACHE,
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson 24, 32, 32, 40, 80, 8
f1b68725503ff3e46001eee5a1751e29a43a09d1Andreas Gustafsson};
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark AndrewsLIBDNS_EXTERNAL_DATA const dns_master_style_t
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsdns_master_style_simple = {
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff 0,
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews 24, 32, 32, 40, 80, 8
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews};
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson/*%
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson * A style suitable for dns_rdataset_totext().
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson */
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas GustafssonLIBDNS_EXTERNAL_DATA const dns_master_style_t
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafssondns_master_style_debug = {
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff DNS_STYLEFLAG_REL_OWNER,
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson 24, 32, 40, 48, 80, 8
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson};
9ce72fc748cc7c2b738147f9736e00a96474be1bBrian Wellington
e502b133d630bda0ee64c1e2ce6729d96750d8abMark Andrews
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson#define N_SPACES 10
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic char spaces[N_SPACES+1] = " ";
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson
9ce72fc748cc7c2b738147f9736e00a96474be1bBrian Wellington#define N_TABS 10
9f139761ca06977d1db8051842efc620c15b8199Andreas Gustafssonstatic char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t";
5fc7ba3e1ac5d72239e9971e0f469dd5796738f9Andreas Gustafsson
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graffstruct dns_dumpctx {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews unsigned int magic;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_mem_t *mctx;
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff isc_mutex_t lock;
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence unsigned int references;
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater isc_boolean_t canceled;
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater isc_boolean_t first;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_boolean_t do_date;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_stdtime_t now;
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews FILE *f;
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff dns_db_t *db;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_dbversion_t *version;
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington dns_dbiterator_t *dbiter;
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington dns_totext_ctx_t tctx;
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington isc_task_t *task;
94a08e09db3dc844b6ee4841c368a2d7074a9c3fAndreas Gustafsson dns_dumpdonefunc_t done;
1ef8965366d91e02a4672c35a187d30aa4a4c72cMark Andrews void *done_arg;
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater unsigned int nodes;
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater /* dns_master_dumpinc() */
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews char *file;
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater char *tmpfile;
bf33eb0b522801792a6663b0360bc94b9e9b77c2Automatic Updater dns_masterformat_t format;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_result_t (*dumpsets)(isc_mem_t *mctx, dns_name_t *name,
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence dns_rdatasetiter_t *rdsiter,
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence dns_totext_ctx_t *ctx,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_buffer_t *buffer, FILE *f);
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews};
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews#define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews/*%
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * Output tabs and spaces to go from column '*current' to
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * column 'to', and update '*current' to reflect the new
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * current column.
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff */
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graffstatic isc_result_t
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsindent(unsigned int *current, unsigned int to, int tabwidth,
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_buffer_t *target)
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff{
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence isc_region_t r;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews unsigned char *p;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews unsigned int from;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews int ntabs, nspaces, t;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington from = *current;
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (to < from + 1)
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews to = from + 1;
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews ntabs = to / tabwidth - from / tabwidth;
94a08e09db3dc844b6ee4841c368a2d7074a9c3fAndreas Gustafsson if (ntabs < 0)
52637f592f705ca93fadc218e403fd55e8ce4aeaMark Andrews ntabs = 0;
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington
cdc50af0bff41accc02c613b9c6d8cd41b171ffeBrian Wellington if (ntabs > 0) {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_buffer_availableregion(target, &r);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (r.length < (unsigned) ntabs)
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff return (ISC_R_NOSPACE);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews p = r.base;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews t = ntabs;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews while (t) {
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews int n = t;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (n > N_TABS)
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews n = N_TABS;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews memcpy(p, tabs, n);
6e49e91bd08778d7eae45a2229dcf41ed97cc636David Lawrence p += n;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews t -= n;
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff }
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_buffer_add(target, ntabs);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews from = (to / tabwidth) * tabwidth;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews }
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews nspaces = to - from;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews INSIST(nspaces >= 0);
4529cdaedaf1a0a5f8ff89aeca510b7a4475446cBob Halley
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence isc_buffer_availableregion(target, &r);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (r.length < (unsigned) nspaces)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews return (ISC_R_NOSPACE);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews p = r.base;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
e6c22f37d8df55a9f66b479a22717e179bcf79a3Andreas Gustafsson t = nspaces;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews while (t) {
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews int n = t;
d981ca645597116d227a48bf37cc5edc061c854dBob Halley if (n > N_SPACES)
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews n = N_SPACES;
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews memcpy(p, spaces, n);
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews p += n;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews t -= n;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews }
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_buffer_add(target, nspaces);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews *current = to;
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews return (ISC_R_SUCCESS);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews}
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsstatic isc_result_t
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewstotext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) {
e6c22f37d8df55a9f66b479a22717e179bcf79a3Andreas Gustafsson isc_result_t result;
e6c22f37d8df55a9f66b479a22717e179bcf79a3Andreas Gustafsson
e6c22f37d8df55a9f66b479a22717e179bcf79a3Andreas Gustafsson REQUIRE(style->tab_width != 0);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews ctx->style = *style;
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews ctx->class_printed = ISC_FALSE;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews dns_fixedname_init(&ctx->origin_fixname);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews /*
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews * Set up the line break string if needed.
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews */
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
e6c22f37d8df55a9f66b479a22717e179bcf79a3Andreas Gustafsson isc_buffer_t buf;
e6c22f37d8df55a9f66b479a22717e179bcf79a3Andreas Gustafsson isc_region_t r;
e6c22f37d8df55a9f66b479a22717e179bcf79a3Andreas Gustafsson unsigned int col = 0;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews isc_buffer_init(&buf, ctx->linebreak_buf,
deaaf94332abbfdb3aff53675546acfed16e5eb6Mark Andrews sizeof(ctx->linebreak_buf));
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews
90e303b114e56db5809fdd19805243457fa43cd9Olafur Gudmundsson isc_buffer_availableregion(&buf, &r);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews if (r.length < 1)
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews return (DNS_R_TEXTTOOLONG);
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff r.base[0] = '\n';
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence isc_buffer_add(&buf, 1);
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews result = indent(&col, ctx->style.rdata_column,
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews ctx->style.tab_width, &buf);
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff /*
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews * Do not return ISC_R_NOSPACE if the line break string
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews * buffer is too small, because that would just make
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews * dump_rdataset() retry indenfinitely with ever
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews * bigger target buffers. That's a different buffer,
ae114ded82e773a4d9058f833f964a17514712a8Brian Wellington * so it won't help. Use DNS_R_TEXTTOOLONG as a substitute.
63cef8bde8b92aeb30ccdcf21d4e44c9be9cc6e3Andreas Gustafsson */
63cef8bde8b92aeb30ccdcf21d4e44c9be9cc6e3Andreas Gustafsson if (result == ISC_R_NOSPACE)
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews return (DNS_R_TEXTTOOLONG);
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews if (result != ISC_R_SUCCESS)
c38b92000c0f1a95daaad5468777e165b8047de9Mark Andrews return (result);
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews isc_buffer_availableregion(&buf, &r);
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews if (r.length < 1)
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews return (DNS_R_TEXTTOOLONG);
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews r.base[0] = '\0';
373ce67419680a398ba3dc51a14a486caaf0afb0Mark Andrews isc_buffer_add(&buf, 1);
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews ctx->linebreak = ctx->linebreak_buf;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews } else {
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff ctx->linebreak = NULL;
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence }
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews ctx->origin = NULL;
05f90cac85760b4edef2962209df49ea019c180fMark Andrews ctx->neworigin = NULL;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews ctx->current_ttl = 0;
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff ctx->current_ttl_valid = ISC_FALSE;
3d5cad69ec20157912e95cf3b79316dfb0a314f3Mark Andrews
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews return (ISC_R_SUCCESS);
b589e90689c6e87bf9608424ca8d99571c18bc61Mark Andrews}
f7b99290c31abeb20c55fc55391510450ce60423Mark Andrews
d981ca645597116d227a48bf37cc5edc061c854dBob Halley#define INDENT_TO(col) \
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews do { \
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews if ((result = indent(&column, ctx->style.col, \
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews ctx->style.tab_width, target)) \
05f90cac85760b4edef2962209df49ea019c180fMark Andrews != ISC_R_SUCCESS) \
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews return (result); \
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff } while (0)
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff
05f90cac85760b4edef2962209df49ea019c180fMark Andrews
05f90cac85760b4edef2962209df49ea019c180fMark Andrewsstatic isc_result_t
05f90cac85760b4edef2962209df49ea019c180fMark Andrewsstr_totext(const char *source, isc_buffer_t *target) {
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews unsigned int l;
05f90cac85760b4edef2962209df49ea019c180fMark Andrews isc_region_t region;
05f90cac85760b4edef2962209df49ea019c180fMark Andrews
05f90cac85760b4edef2962209df49ea019c180fMark Andrews isc_buffer_availableregion(target, &region);
c38b92000c0f1a95daaad5468777e165b8047de9Mark Andrews l = strlen(source);
c38b92000c0f1a95daaad5468777e165b8047de9Mark Andrews
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews if (l > region.length)
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews return (ISC_R_NOSPACE);
05f90cac85760b4edef2962209df49ea019c180fMark Andrews
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews memcpy(region.base, source, l);
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews isc_buffer_add(target, l);
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff return (ISC_R_SUCCESS);
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews}
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff/*
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews * Convert 'rdataset' to master file text format according to 'ctx',
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews * storing the result in 'target'. If 'owner_name' is NULL, it
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff * is omitted; otherwise 'owner_name' must be valid and have at least
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews * one label.
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrews */
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff
a560a0bfb2fd48ddd1900f61a655397a5c4f7343Mark Andrewsstatic isc_result_t
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrewsrdataset_totext(dns_rdataset_t *rdataset,
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews dns_name_t *owner_name,
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff dns_totext_ctx_t *ctx,
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews isc_boolean_t omit_final_dot,
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews isc_buffer_t *target)
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews{
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews isc_result_t result;
9281e7aa775026dc47c01745fdcc438645146877Mark Andrews unsigned int column;
5d51e67c3b4f35c1be742574aacc1d88fe6ed444Mark Andrews isc_boolean_t first = ISC_TRUE;
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews isc_uint32_t current_ttl;
4529cdaedaf1a0a5f8ff89aeca510b7a4475446cBob Halley isc_boolean_t current_ttl_valid;
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence dns_rdatatype_t type;
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews REQUIRE(DNS_RDATASET_VALID(rdataset));
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews result = dns_rdataset_first(rdataset);
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff REQUIRE(result == ISC_R_SUCCESS);
206c71aae4cca0c94d67e271196b6658d293886bMark Andrews
206c71aae4cca0c94d67e271196b6658d293886bMark Andrews current_ttl = ctx->current_ttl;
206c71aae4cca0c94d67e271196b6658d293886bMark Andrews current_ttl_valid = ctx->current_ttl_valid;
05f90cac85760b4edef2962209df49ea019c180fMark Andrews
c38b92000c0f1a95daaad5468777e165b8047de9Mark Andrews do {
05f90cac85760b4edef2962209df49ea019c180fMark Andrews column = 0;
94a3bcd132e515b4baa0884ba9dd0f361d2e17bcMark Andrews
d981ca645597116d227a48bf37cc5edc061c854dBob Halley /*
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff * Owner name.
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence */
90f9d00f087ddb4442d1ebf628c1bcaff18226a0Andreas Gustafsson if (owner_name != NULL &&
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff !first))
d981ca645597116d227a48bf37cc5edc061c854dBob Halley {
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff unsigned int name_start = target->used;
d981ca645597116d227a48bf37cc5edc061c854dBob Halley RETERR(dns_name_totext(owner_name,
419590499823ce15b5d2ad4fe71eaf04bd5a86c0Michael Graff omit_final_dot,
d981ca645597116d227a48bf37cc5edc061c854dBob Halley target));
d981ca645597116d227a48bf37cc5edc061c854dBob Halley column += target->used - name_start;
3ddd814a97de1d152ba0913c592d6e6dc83d38a6Michael Graff }
6d12fdf96621801e80f3f4c2a8a569fe48766a20David Lawrence
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley /*
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley * TTL.
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley */
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley current_ttl_valid &&
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff rdataset->ttl == current_ttl))
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley {
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley char ttlbuf[64];
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff isc_region_t r;
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley unsigned int length;
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley INDENT_TO(ttl_column);
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
8dd2e6e7c1328ba00d734ce939777e06d9a15493Michael Graff rdataset->ttl);
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley INSIST(length <= sizeof(ttlbuf));
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley isc_buffer_availableregion(target, &r);
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley if (r.length < length)
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley return (ISC_R_NOSPACE);
e27a69f8bd9538e08f775265167ba6cc5f47c587Bob Halley memcpy(r.base, ttlbuf, length);
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews isc_buffer_add(target, length);
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews column += length;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews /*
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews * If the $TTL directive is not in use, the TTL we
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews * just printed becomes the default for subsequent RRs.
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews */
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews current_ttl = rdataset->ttl;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews current_ttl_valid = ISC_TRUE;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews }
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews }
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews /*
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews * Class.
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews */
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews ctx->class_printed == ISC_FALSE))
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews {
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews unsigned int class_start;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews INDENT_TO(class_column);
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews class_start = target->used;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews result = dns_rdataclass_totext(rdataset->rdclass,
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews target);
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews if (result != ISC_R_SUCCESS)
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews return (result);
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews column += (target->used - class_start);
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews }
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews /*
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews * Type.
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews */
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews if (rdataset->type == 0) {
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews type = rdataset->covers;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews } else {
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews type = rdataset->type;
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews }
2047977ce2dfcfe3a0fa2d638c3242841310fad3Mark Andrews
3d17a3ba61a303d5c4d9867068d0fbe9f24d2988Mark Andrews {
3d17a3ba61a303d5c4d9867068d0fbe9f24d2988Mark Andrews unsigned int type_start;
3d17a3ba61a303d5c4d9867068d0fbe9f24d2988Mark Andrews INDENT_TO(type_column);
3d17a3ba61a303d5c4d9867068d0fbe9f24d2988Mark Andrews type_start = target->used;
3d17a3ba61a303d5c4d9867068d0fbe9f24d2988Mark Andrews if (rdataset->type == 0)
854d0238dbc2908490197984b3b9d558008a53dfMark Andrews RETERR(str_totext("\\-", target));
result = dns_rdatatype_totext(type, target);
if (result != ISC_R_SUCCESS)
return (result);
column += (target->used - type_start);
}
/*
* Rdata.
*/
INDENT_TO(rdata_column);
if (rdataset->type == 0) {
if (NXDOMAIN(rdataset))
RETERR(str_totext(";-$NXDOMAIN\n", target));
else
RETERR(str_totext(";-$NXRRSET\n", target));
} else {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_region_t r;
dns_rdataset_current(rdataset, &rdata);
RETERR(dns_rdata_tofmttext(&rdata,
ctx->origin,
ctx->style.flags,
ctx->style.line_length -
ctx->style.rdata_column,
ctx->linebreak,
target));
isc_buffer_availableregion(target, &r);
if (r.length < 1)
return (ISC_R_NOSPACE);
r.base[0] = '\n';
isc_buffer_add(target, 1);
}
first = ISC_FALSE;
result = dns_rdataset_next(rdataset);
} while (result == ISC_R_SUCCESS);
if (result != ISC_R_NOMORE)
return (result);
/*
* Update the ctx state to reflect what we just printed.
* This is done last, only when we are sure we will return
* success, because this function may be called multiple
* times with increasing buffer sizes until it succeeds,
* and failed attempts must not update the state prematurely.
*/
ctx->class_printed = ISC_TRUE;
ctx->current_ttl= current_ttl;
ctx->current_ttl_valid = current_ttl_valid;
return (ISC_R_SUCCESS);
}
/*
* Print the name, type, and class of an empty rdataset,
* such as those used to represent the question section
* of a DNS message.
*/
static isc_result_t
question_totext(dns_rdataset_t *rdataset,
dns_name_t *owner_name,
dns_totext_ctx_t *ctx,
isc_boolean_t omit_final_dot,
isc_buffer_t *target)
{
unsigned int column;
isc_result_t result;
isc_region_t r;
REQUIRE(DNS_RDATASET_VALID(rdataset));
result = dns_rdataset_first(rdataset);
REQUIRE(result == ISC_R_NOMORE);
column = 0;
/* Owner name */
{
unsigned int name_start = target->used;
RETERR(dns_name_totext(owner_name,
omit_final_dot,
target));
column += target->used - name_start;
}
/* Class */
{
unsigned int class_start;
INDENT_TO(class_column);
class_start = target->used;
result = dns_rdataclass_totext(rdataset->rdclass, target);
if (result != ISC_R_SUCCESS)
return (result);
column += (target->used - class_start);
}
/* Type */
{
unsigned int type_start;
INDENT_TO(type_column);
type_start = target->used;
result = dns_rdatatype_totext(rdataset->type, target);
if (result != ISC_R_SUCCESS)
return (result);
column += (target->used - type_start);
}
isc_buffer_availableregion(target, &r);
if (r.length < 1)
return (ISC_R_NOSPACE);
r.base[0] = '\n';
isc_buffer_add(target, 1);
return (ISC_R_SUCCESS);
}
isc_result_t
dns_rdataset_totext(dns_rdataset_t *rdataset,
dns_name_t *owner_name,
isc_boolean_t omit_final_dot,
isc_boolean_t question,
isc_buffer_t *target)
{
dns_totext_ctx_t ctx;
isc_result_t result;
result = totext_ctx_init(&dns_master_style_debug, &ctx);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
/*
* The caller might want to give us an empty owner
* name (e.g. if they are outputting into a master
* file and this rdataset has the same name as the
* previous one.)
*/
if (dns_name_countlabels(owner_name) == 0)
owner_name = NULL;
if (question)
return (question_totext(rdataset, owner_name, &ctx,
omit_final_dot, target));
else
return (rdataset_totext(rdataset, owner_name, &ctx,
omit_final_dot, target));
}
isc_result_t
dns_master_rdatasettotext(dns_name_t *owner_name,
dns_rdataset_t *rdataset,
const dns_master_style_t *style,
isc_buffer_t *target)
{
dns_totext_ctx_t ctx;
isc_result_t result;
result = totext_ctx_init(style, &ctx);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
return (rdataset_totext(rdataset, owner_name, &ctx,
ISC_FALSE, target));
}
isc_result_t
dns_master_questiontotext(dns_name_t *owner_name,
dns_rdataset_t *rdataset,
const dns_master_style_t *style,
isc_buffer_t *target)
{
dns_totext_ctx_t ctx;
isc_result_t result;
result = totext_ctx_init(style, &ctx);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
return (question_totext(rdataset, owner_name, &ctx,
ISC_FALSE, target));
}
/*
* Print an rdataset. 'buffer' is a scratch buffer, which must have been
* dynamically allocated by the caller. It must be large enough to
* hold the result from dns_ttl_totext(). If more than that is needed,
* the buffer will be grown automatically.
*/
static isc_result_t
dump_rdataset(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
dns_totext_ctx_t *ctx,
isc_buffer_t *buffer, FILE *f)
{
isc_region_t r;
isc_result_t result;
REQUIRE(buffer->length > 0);
/*
* Output a $TTL directive if needed.
*/
if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
if (ctx->current_ttl_valid == ISC_FALSE ||
ctx->current_ttl != rdataset->ttl)
{
if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
{
isc_buffer_clear(buffer);
result = dns_ttl_totext(rdataset->ttl,
ISC_TRUE, buffer);
INSIST(result == ISC_R_SUCCESS);
isc_buffer_usedregion(buffer, &r);
fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
(int) r.length, (char *) r.base);
} else {
fprintf(f, "$TTL %u\n", rdataset->ttl);
}
ctx->current_ttl = rdataset->ttl;
ctx->current_ttl_valid = ISC_TRUE;
}
}
isc_buffer_clear(buffer);
/*
* Generate the text representation of the rdataset into
* the buffer. If the buffer is too small, grow it.
*/
for (;;) {
int newlength;
void *newmem;
result = rdataset_totext(rdataset, name, ctx,
ISC_FALSE, buffer);
if (result != ISC_R_NOSPACE)
break;
newlength = buffer->length * 2;
newmem = isc_mem_get(mctx, newlength);
if (newmem == NULL)
return (ISC_R_NOMEMORY);
isc_mem_put(mctx, buffer->base, buffer->length);
isc_buffer_init(buffer, newmem, newlength);
}
if (result != ISC_R_SUCCESS)
return (result);
/*
* Write the buffer contents to the master file.
*/
isc_buffer_usedregion(buffer, &r);
result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"master file write failed: %s",
isc_result_totext(result));
return (result);
}
return (ISC_R_SUCCESS);
}
/*
* Define the order in which rdatasets should be printed in zone
* files. We will print SOA and NS records before others, SIGs
* immediately following the things they sign, and order everything
* else by RR number. This is all just for aesthetics and
* compatibility with buggy software that expects the SOA to be first;
* the DNS specifications allow any order.
*/
static int
dump_order(const dns_rdataset_t *rds) {
int t;
int sig;
if (rds->type == dns_rdatatype_rrsig) {
t = rds->covers;
sig = 1;
} else {
t = rds->type;
sig = 0;
}
switch (t) {
case dns_rdatatype_soa:
t = 0;
break;
case dns_rdatatype_ns:
t = 1;
break;
default:
t += 2;
break;
}
return (t << 1) + sig;
}
static int
dump_order_compare(const void *a, const void *b) {
return (dump_order(*((const dns_rdataset_t * const *) a)) -
dump_order(*((const dns_rdataset_t * const *) b)));
}
/*
* Dump all the rdatasets of a domain name to a master file. We make
* a "best effort" attempt to sort the RRsets in a nice order, but if
* there are more than MAXSORT RRsets, we punt and only sort them in
* groups of MAXSORT. This is not expected to ever happen in practice
* since much less than 64 RR types have been registered with the
* IANA, so far, and the output will be correct (though not
* aesthetically pleasing) even if it does happen.
*/
#define MAXSORT 64
static const char *trustnames[] = {
"none",
"pending",
"additional",
"glue",
"answer",
"authauthority",
"authanswer",
"secure",
"local" /* aka ultimate */
};
static isc_result_t
dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name,
dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
isc_buffer_t *buffer, FILE *f)
{
isc_result_t itresult, dumpresult;
isc_region_t r;
dns_rdataset_t rdatasets[MAXSORT];
dns_rdataset_t *sorted[MAXSORT];
int i, n;
itresult = dns_rdatasetiter_first(rdsiter);
dumpresult = ISC_R_SUCCESS;
if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
isc_buffer_clear(buffer);
itresult = dns_name_totext(ctx->neworigin, ISC_FALSE, buffer);
RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
isc_buffer_usedregion(buffer, &r);
fprintf(f, "$ORIGIN %.*s\n", (int) r.length, (char *) r.base);
ctx->neworigin = NULL;
}
again:
for (i = 0;
itresult == ISC_R_SUCCESS && i < MAXSORT;
itresult = dns_rdatasetiter_next(rdsiter), i++) {
dns_rdataset_init(&rdatasets[i]);
dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
sorted[i] = &rdatasets[i];
}
n = i;
INSIST(n <= MAXSORT);
qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
for (i = 0; i < n; i++) {
dns_rdataset_t *rds = sorted[i];
if (ctx->style.flags & DNS_STYLEFLAG_TRUST) {
unsigned int trust = rds->trust;
INSIST(trust < (sizeof(trustnames) /
sizeof(trustnames[0])));
fprintf(f, "; %s\n", trustnames[trust]);
}
if (rds->type == 0 &&
(ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
/* Omit negative cache entries */
} else {
isc_result_t result =
dump_rdataset(mctx, name, rds, ctx,
buffer, f);
if (result != ISC_R_SUCCESS)
dumpresult = result;
if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
name = NULL;
}
dns_rdataset_disassociate(rds);
}
if (dumpresult != ISC_R_SUCCESS)
return (dumpresult);
/*
* If we got more data than could be sorted at once,
* go handle the rest.
*/
if (itresult == ISC_R_SUCCESS)
goto again;
if (itresult == ISC_R_NOMORE)
itresult = ISC_R_SUCCESS;
return (itresult);
}
/*
* Dump given RRsets in the "raw" format.
*/
static isc_result_t
dump_rdataset_raw(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
isc_buffer_t *buffer, FILE *f)
{
isc_result_t result;
isc_uint32_t totallen;
isc_uint16_t dlen;
isc_region_t r, r_hdr;
REQUIRE(buffer->length > 0);
REQUIRE(DNS_RDATASET_VALID(rdataset));
restart:
totallen = 0;
result = dns_rdataset_first(rdataset);
REQUIRE(result == ISC_R_SUCCESS);
isc_buffer_clear(buffer);
/*
* Common header and owner name (length followed by name)
* These fields should be in a moderate length, so we assume we
* can store all of them in the initial buffer.
*/
isc_buffer_availableregion(buffer, &r_hdr);
INSIST(r_hdr.length >= sizeof(dns_masterrawrdataset_t));
isc_buffer_putuint32(buffer, totallen); /* XXX: leave space */
isc_buffer_putuint16(buffer, rdataset->rdclass); /* 16-bit class */
isc_buffer_putuint16(buffer, rdataset->type); /* 16-bit type */
isc_buffer_putuint16(buffer, rdataset->covers); /* same as type */
isc_buffer_putuint32(buffer, rdataset->ttl); /* 32-bit TTL */
isc_buffer_putuint32(buffer, dns_rdataset_count(rdataset));
totallen = isc_buffer_usedlength(buffer);
INSIST(totallen <= sizeof(dns_masterrawrdataset_t));
dns_name_toregion(name, &r);
INSIST(isc_buffer_availablelength(buffer) >=
(sizeof(dlen) + r.length));
dlen = (isc_uint16_t)r.length;
isc_buffer_putuint16(buffer, dlen);
isc_buffer_copyregion(buffer, &r);
totallen += sizeof(dlen) + r.length;
do {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_region_t r;
dns_rdataset_current(rdataset, &rdata);
dns_rdata_toregion(&rdata, &r);
INSIST(r.length <= 0xffffU);
dlen = (isc_uint16_t)r.length;
/*
* Copy the rdata into the buffer. If the buffer is too small,
* grow it. This should be rare, so we'll simply restart the
* entire procedure (or should we copy the old data and
* continue?).
*/
if (isc_buffer_availablelength(buffer) <
sizeof(dlen) + r.length) {
int newlength;
void *newmem;
newlength = buffer->length * 2;
newmem = isc_mem_get(mctx, newlength);
if (newmem == NULL)
return (ISC_R_NOMEMORY);
isc_mem_put(mctx, buffer->base, buffer->length);
isc_buffer_init(buffer, newmem, newlength);
goto restart;
}
isc_buffer_putuint16(buffer, dlen);
isc_buffer_copyregion(buffer, &r);
totallen += sizeof(dlen) + r.length;
result = dns_rdataset_next(rdataset);
} while (result == ISC_R_SUCCESS);
if (result != ISC_R_NOMORE)
return (result);
/*
* Fill in the total length field.
* XXX: this is a bit tricky. Since we have already "used" the space
* for the total length in the buffer, we first remember the entire
* buffer length in the region, "rewind", and then write the value.
*/
isc_buffer_usedregion(buffer, &r);
isc_buffer_clear(buffer);
isc_buffer_putuint32(buffer, totallen);
INSIST(isc_buffer_usedlength(buffer) < totallen);
/*
* Write the buffer contents to the raw master file.
*/
result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"raw master file write failed: %s",
isc_result_totext(result));
return (result);
}
return (result);
}
static isc_result_t
dump_rdatasets_raw(isc_mem_t *mctx, dns_name_t *name,
dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
isc_buffer_t *buffer, FILE *f)
{
isc_result_t result;
dns_rdataset_t rdataset;
for (result = dns_rdatasetiter_first(rdsiter);
result == ISC_R_SUCCESS;
result = dns_rdatasetiter_next(rdsiter)) {
dns_rdataset_init(&rdataset);
dns_rdatasetiter_current(rdsiter, &rdataset);
if (rdataset.type == 0 &&
(ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
/* Omit negative cache entries */
} else {
result = dump_rdataset_raw(mctx, name, &rdataset,
buffer, f);
}
dns_rdataset_disassociate(&rdataset);
}
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
return (result);
}
/*
* Initial size of text conversion buffer. The buffer is used
* for several purposes: converting origin names, rdatasets,
* $DATE timestamps, and comment strings for $TTL directives.
*
* When converting rdatasets, it is dynamically resized, but
* when converting origins, timestamps, etc it is not. Therefore,
* the initial size must large enough to hold the longest possible
* text representation of any domain name (for $ORIGIN).
*/
static const int initial_buffer_length = 1200;
static isc_result_t
dumptostreaminc(dns_dumpctx_t *dctx);
static void
dumpctx_destroy(dns_dumpctx_t *dctx) {
dctx->magic = 0;
DESTROYLOCK(&dctx->lock);
if (dctx->version != NULL)
dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
dns_dbiterator_destroy(&dctx->dbiter);
dns_db_detach(&dctx->db);
if (dctx->task != NULL)
isc_task_detach(&dctx->task);
if (dctx->file != NULL)
isc_mem_free(dctx->mctx, dctx->file);
if (dctx->tmpfile != NULL)
isc_mem_free(dctx->mctx, dctx->tmpfile);
isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
}
void
dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
REQUIRE(DNS_DCTX_VALID(source));
REQUIRE(target != NULL && *target == NULL);
LOCK(&source->lock);
INSIST(source->references > 0);
source->references++;
INSIST(source->references != 0); /* Overflow? */
UNLOCK(&source->lock);
*target = source;
}
void
dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
dns_dumpctx_t *dctx;
isc_boolean_t need_destroy = ISC_FALSE;
REQUIRE(dctxp != NULL);
dctx = *dctxp;
REQUIRE(DNS_DCTX_VALID(dctx));
*dctxp = NULL;
LOCK(&dctx->lock);
INSIST(dctx->references != 0);
dctx->references--;
if (dctx->references == 0)
need_destroy = ISC_TRUE;
UNLOCK(&dctx->lock);
if (need_destroy)
dumpctx_destroy(dctx);
}
dns_dbversion_t *
dns_dumpctx_version(dns_dumpctx_t *dctx) {
REQUIRE(DNS_DCTX_VALID(dctx));
return (dctx->version);
}
dns_db_t *
dns_dumpctx_db(dns_dumpctx_t *dctx) {
REQUIRE(DNS_DCTX_VALID(dctx));
return (dctx->db);
}
void
dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
REQUIRE(DNS_DCTX_VALID(dctx));
LOCK(&dctx->lock);
dctx->canceled = ISC_TRUE;
UNLOCK(&dctx->lock);
}
static isc_result_t
closeandrename(FILE *f, isc_result_t result, const char *temp, const char *file)
{
isc_result_t tresult;
isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS);
if (result == ISC_R_SUCCESS)
result = isc_stdio_sync(f);
if (result != ISC_R_SUCCESS && logit) {
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
"dumping master file: %s: fsync: %s",
temp, isc_result_totext(result));
logit = ISC_FALSE;
}
tresult = isc_stdio_close(f);
if (result == ISC_R_SUCCESS)
result = tresult;
if (result != ISC_R_SUCCESS && logit) {
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
"dumping master file: %s: fclose: %s",
temp, isc_result_totext(result));
logit = ISC_FALSE;
}
if (result == ISC_R_SUCCESS)
result = isc_file_rename(temp, file);
else
(void)isc_file_remove(temp);
if (result != ISC_R_SUCCESS && logit) {
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
"dumping master file: rename: %s: %s",
file, isc_result_totext(result));
}
return (result);
}
static void
dump_quantum(isc_task_t *task, isc_event_t *event) {
isc_result_t result;
isc_result_t tresult;
dns_dumpctx_t *dctx;
REQUIRE(event != NULL);
dctx = event->ev_arg;
REQUIRE(DNS_DCTX_VALID(dctx));
if (dctx->canceled)
result = ISC_R_CANCELED;
else
result = dumptostreaminc(dctx);
if (result == DNS_R_CONTINUE) {
event->ev_arg = dctx;
isc_task_send(task, &event);
return;
}
if (dctx->file != NULL) {
tresult = closeandrename(dctx->f, result,
dctx->tmpfile, dctx->file);
if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
result = tresult;
}
(dctx->done)(dctx->done_arg, result);
isc_event_free(&event);
dns_dumpctx_detach(&dctx);
}
static isc_result_t
task_send(dns_dumpctx_t *dctx) {
isc_event_t *event;
event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM,
dump_quantum, dctx, sizeof(*event));
if (event == NULL)
return (ISC_R_NOMEMORY);
isc_task_send(dctx->task, &event);
return (ISC_R_SUCCESS);
}
static isc_result_t
dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp,
dns_masterformat_t format)
{
dns_dumpctx_t *dctx;
isc_result_t result;
isc_boolean_t relative;
dctx = isc_mem_get(mctx, sizeof(*dctx));
if (dctx == NULL)
return (ISC_R_NOMEMORY);
dctx->mctx = NULL;
dctx->f = f;
dctx->dbiter = NULL;
dctx->db = NULL;
dctx->version = NULL;
dctx->done = NULL;
dctx->done_arg = NULL;
dctx->task = NULL;
dctx->nodes = 0;
dctx->first = ISC_TRUE;
dctx->canceled = ISC_FALSE;
dctx->file = NULL;
dctx->tmpfile = NULL;
dctx->format = format;
switch (format) {
case dns_masterformat_text:
dctx->dumpsets = dump_rdatasets_text;
break;
case dns_masterformat_raw:
dctx->dumpsets = dump_rdatasets_raw;
break;
default:
INSIST(0);
break;
}
result = totext_ctx_init(style, &dctx->tctx);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"could not set master file style");
goto cleanup;
}
isc_stdtime_get(&dctx->now);
dns_db_attach(db, &dctx->db);
dctx->do_date = dns_db_iscache(dctx->db);
if (dctx->format == dns_masterformat_text &&
(dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) {
relative = ISC_TRUE;
} else
relative = ISC_FALSE;
result = dns_db_createiterator(dctx->db, relative, &dctx->dbiter);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = isc_mutex_init(&dctx->lock);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (version != NULL)
dns_db_attachversion(dctx->db, version, &dctx->version);
else if (!dns_db_iscache(db))
dns_db_currentversion(dctx->db, &dctx->version);
isc_mem_attach(mctx, &dctx->mctx);
dctx->references = 1;
dctx->magic = DNS_DCTX_MAGIC;
*dctxp = dctx;
return (ISC_R_SUCCESS);
cleanup:
if (dctx->dbiter != NULL)
dns_dbiterator_destroy(&dctx->dbiter);
if (dctx->db != NULL)
dns_db_detach(&dctx->db);
if (dctx != NULL)
isc_mem_put(mctx, dctx, sizeof(*dctx));
return (result);
}
static isc_result_t
dumptostreaminc(dns_dumpctx_t *dctx) {
isc_result_t result;
isc_buffer_t buffer;
char *bufmem;
isc_region_t r;
dns_name_t *name;
dns_fixedname_t fixname;
unsigned int nodes;
dns_masterrawheader_t rawheader;
isc_uint32_t now32;
isc_time_t start;
bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
if (bufmem == NULL)
return (ISC_R_NOMEMORY);
isc_buffer_init(&buffer, bufmem, initial_buffer_length);
dns_fixedname_init(&fixname);
name = dns_fixedname_name(&fixname);
if (dctx->first) {
switch (dctx->format) {
case dns_masterformat_text:
/*
* If the database has cache semantics, output an
* RFC2540 $DATE directive so that the TTLs can be
* adjusted when it is reloaded. For zones it is not
* really needed, and it would make the file
* incompatible with pre-RFC2540 software, so we omit
* it in the zone case.
*/
if (dctx->do_date) {
result = dns_time32_totext(dctx->now, &buffer);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
isc_buffer_usedregion(&buffer, &r);
fprintf(dctx->f, "$DATE %.*s\n",
(int) r.length, (char *) r.base);
}
break;
case dns_masterformat_raw:
r.base = (unsigned char *)&rawheader;
r.length = sizeof(rawheader);
isc_buffer_region(&buffer, &r);
isc_buffer_putuint32(&buffer, dns_masterformat_raw);
isc_buffer_putuint32(&buffer, DNS_RAWFORMAT_VERSION);
if (sizeof(now32) != sizeof(dctx->now)) {
/*
* We assume isc_stdtime_t is a 32-bit integer,
* which should be the case on most cases.
* If it turns out to be uncommon, we'll need
* to bump the version number and revise the
* header format.
*/
isc_log_write(dns_lctx,
ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP,
ISC_LOG_INFO,
"dumping master file in raw "
"format: stdtime is not 32bits");
now32 = 0;
} else
now32 = dctx->now;
isc_buffer_putuint32(&buffer, now32);
INSIST(isc_buffer_usedlength(&buffer) <=
sizeof(rawheader));
result = isc_stdio_write(buffer.base, 1,
isc_buffer_usedlength(&buffer),
dctx->f, NULL);
if (result != ISC_R_SUCCESS)
return (result);
isc_buffer_clear(&buffer);
break;
default:
INSIST(0);
}
result = dns_dbiterator_first(dctx->dbiter);
dctx->first = ISC_FALSE;
} else
result = ISC_R_SUCCESS;
nodes = dctx->nodes;
isc_time_now(&start);
while (result == ISC_R_SUCCESS && (dctx->nodes == 0 || nodes--)) {
dns_rdatasetiter_t *rdsiter = NULL;
dns_dbnode_t *node = NULL;
result = dns_dbiterator_current(dctx->dbiter, &node, name);
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
break;
if (result == DNS_R_NEWORIGIN) {
dns_name_t *origin =
dns_fixedname_name(&dctx->tctx.origin_fixname);
result = dns_dbiterator_origin(dctx->dbiter, origin);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) != 0)
dctx->tctx.origin = origin;
dctx->tctx.neworigin = origin;
}
result = dns_db_allrdatasets(dctx->db, node, dctx->version,
dctx->now, &rdsiter);
if (result != ISC_R_SUCCESS) {
dns_db_detachnode(dctx->db, &node);
goto fail;
}
result = (dctx->dumpsets)(dctx->mctx, name, rdsiter,
&dctx->tctx, &buffer, dctx->f);
dns_rdatasetiter_destroy(&rdsiter);
if (result != ISC_R_SUCCESS) {
dns_db_detachnode(dctx->db, &node);
goto fail;
}
dns_db_detachnode(dctx->db, &node);
result = dns_dbiterator_next(dctx->dbiter);
}
/*
* Work out how many nodes can be written in the time between
* two requests to the nameserver. Smooth the resulting number and
* use it as a estimate for the number of nodes to be written in the
* next iteration.
*/
if (dctx->nodes != 0 && result == ISC_R_SUCCESS) {
unsigned int pps = dns_pps; /* packets per second */
unsigned int interval;
isc_uint64_t usecs;
isc_time_t end;
isc_time_now(&end);
if (pps < 100)
pps = 100;
interval = 1000000 / pps; /* interval in usecs */
if (interval == 0)
interval = 1;
usecs = isc_time_microdiff(&end, &start);
if (usecs == 0) {
dctx->nodes = dctx->nodes * 2;
if (dctx->nodes > 1000)
dctx->nodes = 1000;
} else {
nodes = dctx->nodes * interval;
nodes /= (unsigned int)usecs;
if (nodes == 0)
nodes = 1;
else if (nodes > 1000)
nodes = 1000;
/* Smooth and assign. */
dctx->nodes = (nodes + dctx->nodes * 7) / 8;
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP, ISC_LOG_INFO,
"dumptostreaminc(%p) new nodes -> %d\n",
dctx, dctx->nodes);
}
dns_dbiterator_pause(dctx->dbiter);
result = DNS_R_CONTINUE;
} else if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
fail:
isc_mem_put(dctx->mctx, buffer.base, buffer.length);
return (result);
}
isc_result_t
dns_master_dumptostreaminc(isc_mem_t *mctx, dns_db_t *db,
dns_dbversion_t *version,
const dns_master_style_t *style,
FILE *f, isc_task_t *task,
dns_dumpdonefunc_t done, void *done_arg,
dns_dumpctx_t **dctxp)
{
dns_dumpctx_t *dctx = NULL;
isc_result_t result;
REQUIRE(task != NULL);
REQUIRE(f != NULL);
REQUIRE(done != NULL);
result = dumpctx_create(mctx, db, version, style, f, &dctx,
dns_masterformat_text);
if (result != ISC_R_SUCCESS)
return (result);
isc_task_attach(task, &dctx->task);
dctx->done = done;
dctx->done_arg = done_arg;
dctx->nodes = 100;
result = task_send(dctx);
if (result == ISC_R_SUCCESS) {
dns_dumpctx_attach(dctx, dctxp);
return (DNS_R_CONTINUE);
}
dns_dumpctx_detach(&dctx);
return (result);
}
/*
* Dump an entire database into a master file.
*/
isc_result_t
dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db,
dns_dbversion_t *version,
const dns_master_style_t *style,
FILE *f)
{
return (dns_master_dumptostream2(mctx, db, version, style,
dns_masterformat_text, f));
}
isc_result_t
dns_master_dumptostream2(isc_mem_t *mctx, dns_db_t *db,
dns_dbversion_t *version,
const dns_master_style_t *style,
dns_masterformat_t format, FILE *f)
{
dns_dumpctx_t *dctx = NULL;
isc_result_t result;
result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
if (result != ISC_R_SUCCESS)
return (result);
result = dumptostreaminc(dctx);
INSIST(result != DNS_R_CONTINUE);
dns_dumpctx_detach(&dctx);
return (result);
}
static isc_result_t
opentmp(isc_mem_t *mctx, const char *file, char **tempp, FILE **fp) {
FILE *f = NULL;
isc_result_t result;
char *tempname = NULL;
int tempnamelen;
tempnamelen = strlen(file) + 20;
tempname = isc_mem_allocate(mctx, tempnamelen);
if (tempname == NULL)
return (ISC_R_NOMEMORY);
result = isc_file_mktemplate(file, tempname, tempnamelen);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = isc_file_openunique(tempname, &f);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
"dumping master file: %s: open: %s",
tempname, isc_result_totext(result));
goto cleanup;
}
*tempp = tempname;
*fp = f;
return (ISC_R_SUCCESS);
cleanup:
isc_mem_free(mctx, tempname);
return (result);
}
isc_result_t
dns_master_dumpinc(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
const dns_master_style_t *style, const char *filename,
isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
dns_dumpctx_t **dctxp)
{
return (dns_master_dumpinc2(mctx, db, version, style, filename, task,
done, done_arg, dctxp,
dns_masterformat_text));
}
isc_result_t
dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
const dns_master_style_t *style, const char *filename,
isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
dns_dumpctx_t **dctxp, dns_masterformat_t format)
{
FILE *f = NULL;
isc_result_t result;
char *tempname = NULL;
char *file = NULL;
dns_dumpctx_t *dctx = NULL;
file = isc_mem_strdup(mctx, filename);
if (file == NULL)
return (ISC_R_NOMEMORY);
result = opentmp(mctx, filename, &tempname, &f);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
if (result != ISC_R_SUCCESS) {
(void)isc_stdio_close(f);
(void)isc_file_remove(tempname);
goto cleanup;
}
isc_task_attach(task, &dctx->task);
dctx->done = done;
dctx->done_arg = done_arg;
dctx->nodes = 100;
dctx->file = file;
file = NULL;
dctx->tmpfile = tempname;
tempname = NULL;
result = task_send(dctx);
if (result == ISC_R_SUCCESS) {
dns_dumpctx_attach(dctx, dctxp);
return (DNS_R_CONTINUE);
}
cleanup:
if (dctx != NULL)
dns_dumpctx_detach(&dctx);
if (file != NULL)
isc_mem_free(mctx, file);
if (tempname != NULL)
isc_mem_free(mctx, tempname);
return (result);
}
isc_result_t
dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
const dns_master_style_t *style, const char *filename)
{
return (dns_master_dump2(mctx, db, version, style, filename,
dns_masterformat_text));
}
isc_result_t
dns_master_dump2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
const dns_master_style_t *style, const char *filename,
dns_masterformat_t format)
{
FILE *f = NULL;
isc_result_t result;
char *tempname;
dns_dumpctx_t *dctx = NULL;
result = opentmp(mctx, filename, &tempname, &f);
if (result != ISC_R_SUCCESS)
return (result);
result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dumptostreaminc(dctx);
INSIST(result != DNS_R_CONTINUE);
dns_dumpctx_detach(&dctx);
result = closeandrename(f, result, tempname, filename);
cleanup:
isc_mem_free(mctx, tempname);
return (result);
}
/*
* Dump a database node into a master file.
* XXX: this function assumes the text format.
*/
isc_result_t
dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
dns_dbversion_t *version,
dns_dbnode_t *node, dns_name_t *name,
const dns_master_style_t *style,
FILE *f)
{
isc_result_t result;
isc_buffer_t buffer;
char *bufmem;
isc_stdtime_t now;
dns_totext_ctx_t ctx;
dns_rdatasetiter_t *rdsiter = NULL;
result = totext_ctx_init(style, &ctx);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
isc_stdtime_get(&now);
bufmem = isc_mem_get(mctx, initial_buffer_length);
if (bufmem == NULL)
return (ISC_R_NOMEMORY);
isc_buffer_init(&buffer, bufmem, initial_buffer_length);
result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
if (result != ISC_R_SUCCESS)
goto failure;
result = dump_rdatasets_text(mctx, name, rdsiter, &ctx, &buffer, f);
if (result != ISC_R_SUCCESS)
goto failure;
dns_rdatasetiter_destroy(&rdsiter);
result = ISC_R_SUCCESS;
failure:
isc_mem_put(mctx, buffer.base, buffer.length);
return (result);
}
isc_result_t
dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
dns_dbnode_t *node, dns_name_t *name,
const dns_master_style_t *style, const char *filename)
{
FILE *f = NULL;
isc_result_t result;
result = isc_stdio_open(filename, "w", &f);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
"dumping node to file: %s: open: %s", filename,
isc_result_totext(result));
return (ISC_R_UNEXPECTED);
}
result = dns_master_dumpnodetostream(mctx, db, version, node, name,
style, f);
result = isc_stdio_close(f);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
"dumping master file: %s: close: %s", filename,
isc_result_totext(result));
return (ISC_R_UNEXPECTED);
}
return (result);
}
isc_result_t
dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags,
unsigned int ttl_column, unsigned int class_column,
unsigned int type_column, unsigned int rdata_column,
unsigned int line_length, unsigned int tab_width,
isc_mem_t *mctx)
{
dns_master_style_t *style;
REQUIRE(stylep != NULL && *stylep == NULL);
style = isc_mem_get(mctx, sizeof(*style));
if (style == NULL)
return (ISC_R_NOMEMORY);
style->flags = flags;
style->ttl_column = ttl_column;
style->class_column = class_column;
style->type_column = type_column;
style->rdata_column = rdata_column;
style->line_length = line_length;
style->tab_width = tab_width;
*stylep = style;
return (ISC_R_SUCCESS);
}
void
dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
dns_master_style_t *style;
REQUIRE(stylep != NULL && *stylep != NULL);
style = *stylep;
*stylep = NULL;
isc_mem_put(mctx, style, sizeof(*style));
}