/**************************************************************************************************
$Id: export.c,v 1.29 2006/01/18 22:45:51 bboy Exp $
Outputs zone data in various formats.
Copyright (C) 2002-2005 Don Moore <bboy@bboy.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at Your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**************************************************************************************************/
#include "util.h"
char zone[DNS_MAXNAMELEN]; /* Zone name to dump */
unsigned int zones_out = 0; /* Number of zones output */
enum _output_format { /* Output format types */
OUTPUT_BIND,
OUTPUT_TINYDNS
} output_format = OUTPUT_BIND;
char hostname[256]; /* Hostname of local machine */
char *command_line = NULL; /* Full command line */
#define DNSBUFLEN DNS_MAXNAMELEN+1
/**************************************************************************************************
USAGE
Display program usage information.
**************************************************************************************************/
static void
usage(status)
int status;
{
if (status != EXIT_SUCCESS)
{
fprintf(stderr, _("Try `%s --help' for more information."), progname);
fputs("\n", stderr);
}
else
{
printf(_("Usage: %s [ZONE]..."), progname);
puts("");
puts(_("Output MyDNS zone in formats understood by other DNS servers."));
puts("");
/* puts("----------------------------------------------------------------------------78"); */
puts(_(" -b, --bind output in BIND format (the default)"));
puts(_(" -t, --tinydns-data output in tinydns-data format"));
puts("");
puts(_(" -D, --database=DB database name to use"));
puts(_(" -h, --host=HOST connect to SQL server at HOST"));
puts(_(" -p, --password=PASS password for SQL server (or prompt from tty)"));
puts(_(" -u, --user=USER username for SQL server if not current user"));
puts("");
#if DEBUG_ENABLED
puts(_(" -d, --debug enable debug output"));
#endif
puts(_(" -v, --verbose be more verbose while running"));
puts(_(" --help display this help and exit"));
puts(_(" --version output version information and exit"));
puts("");
printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}
exit(status);
}
/*--- usage() -----------------------------------------------------------------------------------*/
/**************************************************************************************************
CMDLINE
Process command line options.
**************************************************************************************************/
static void
cmdline(int argc, char **argv)
{
char *optstr;
int optc, optindex, n;
struct option const longopts[] =
{
{"bind", no_argument, NULL, 'b'},
{"database", required_argument, NULL, 'D'},
{"host", required_argument, NULL, 'h'},
{"password", optional_argument, NULL, 'p'},
{"tinydns-data", no_argument, NULL, 't'},
{"user", required_argument, NULL, 'u'},
{"debug", no_argument, NULL, 'd'},
{"verbose", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 0},
{"version", no_argument, NULL, 0},
{NULL, 0, NULL, 0}
};
/* Copy full command line into 'command_line' */
for (n = 0; n < argc; n++)
{
if (!command_line)
{
if (!(command_line = strdup(argv[n])))
Err("strdup");
}
else
{
if (!(command_line = realloc(command_line, strlen(command_line) + strlen(argv[n]) + 2)))
Err("realloc");
strcat(command_line, " ");
strcat(command_line, argv[n]);
}
}
gethostname(hostname, sizeof(hostname)-1);
err_file = stdout;
error_init(argv[0], LOG_USER); /* Init output routines */
optstr = getoptstr(longopts);
while ((optc = getopt_long(argc, argv, optstr, longopts, &optindex)) != -1)
{
switch (optc)
{
case 0:
{
const char *opt = longopts[optindex].name;
if (!strcmp(opt, "version")) /* --version */
{
printf("%s ("PACKAGE_NAME") "PACKAGE_VERSION" ("SQL_VERSION_STR")\n", progname);
puts("\n" PACKAGE_COPYRIGHT);
puts(_("This is free software; see the source for copying conditions. There is NO"));
puts(_("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."));
exit(EXIT_SUCCESS);
}
else if (!strcmp(opt, "help")) /* --help */
usage(EXIT_SUCCESS);
}
break;
case 'd': /* -d, --debug */
#if DEBUG_ENABLED
err_verbose = err_debug = 1;
#endif
break;
case 'D': /* -D, --database=DB */
conf_set(&Conf, "database", optarg, 0);
break;
case 'h': /* -h, --host=HOST */
conf_set(&Conf, "db-host", optarg, 0);
break;
case 'p': /* -p, --password=PASS */
if (optarg)
{
conf_set(&Conf, "db-password", optarg, 0);
memset(optarg, 'X', strlen(optarg));
}
else
conf_set(&Conf, "db-password", passinput(_("Enter password")), 0);
break;
case 'u': /* -u, --user=USER */
conf_set(&Conf, "db-user", optarg, 0);
break;
case 'b': /* -b, --bind */
output_format = OUTPUT_BIND;
break;
case 't': /* -t, --tinydns-data */
output_format = OUTPUT_TINYDNS;
break;
case 'v': /* -v, --verbose */
err_verbose = 1;
break;
default:
usage(EXIT_FAILURE);
}
}
}
/*--- cmdline() ---------------------------------------------------------------------------------*/
/**************************************************************************************************
DUMP_HEADER
Dumps a header. Currently only writes a header for tinydns-data format, since BIND format
puts a header at the start of each "file".
**************************************************************************************************/
static void
dump_header(void)
{
time_t now = time(NULL);
switch (output_format)
{
case OUTPUT_BIND:
break;
case OUTPUT_TINYDNS:
printf("#\n");
printf("# Created by \"%s\"\n", command_line);
printf("# %.24s\n", ctime(&now));
printf("#\n");
break;
}
}
/*--- dump_header() -----------------------------------------------------------------------------*/
/**************************************************************************************************
BIND_DUMP_SOA
Output SOA, BIND format.
**************************************************************************************************/
static void
bind_dump_soa(MYDNS_SOA *soa)
{
time_t now = time(NULL);
if (zones_out > 0)
puts("\f");
printf("$TTL %u\n", soa->ttl);
printf("; Zone: %s (#%u)\n", soa->origin, soa->id);
printf("; Created by \"%s\"\n", command_line);
printf("; %.24s\n", ctime(&now));
printf("$ORIGIN %s\n\n", soa->origin);
printf("@\tIN SOA\t%s\t%s (\n", soa->ns, soa->mbox);
printf("\t%-10u\t ; Serial\n", soa->serial);
printf("\t%-10u\t ; Refresh\n", soa->refresh);
printf("\t%-10u\t ; Retry\n", soa->retry);
printf("\t%-10u\t ; Expire\n", soa->expire);
printf("\t%-10u\t) ; Minimum\n\n", soa->minimum);
}
/*--- bind_dump_soa() ---------------------------------------------------------------------------*/
/**************************************************************************************************
BIND_DUMP_RR
Output resource record, BIND format.
**************************************************************************************************/
static void
bind_dump_rr(MYDNS_SOA *soa, MYDNS_RR *rr, int maxlen)
{
char *type = mydns_qtype_str(
#if ALIAS_ENABLED
(rr->alias == 0) ?
#endif /* ALIAS_ENABLED */
rr->type
#if ALIAS_ENABLED
: DNS_QTYPE_CNAME
#endif /* ALIAS_ENABLED */
);
printf("%-*s", maxlen, rr->name);
printf("\t%u\tIN %-5s\t", (rr->ttl < soa->minimum) ? soa->minimum : rr->ttl, type);
if (rr->type == DNS_QTYPE_MX)
printf("%u %s\n", (uint32_t)rr->aux, rr->data);
else if (rr->type == DNS_QTYPE_SRV)
printf("%u %u %u %s\n", (uint32_t)rr->aux, rr->srv_weight, rr->srv_port, rr->data);
else if (rr->type == DNS_QTYPE_RP)
printf("%s %s\n", rr->data, rr->rp_txt);
else if (rr->type == DNS_QTYPE_TXT)
{
register unsigned char *c;
putc('"', stdout);
for (c = rr->data; *c; c++)
{
if (*c == '"')
putc('\\', stdout);
putc(*c, stdout);
}
putc('"', stdout);
putc('\n', stdout);
}
else
printf("%s\n", rr->data);
}
/*--- bind_dump_rr() ----------------------------------------------------------------------------*/
#define TINYDNS_NAMEFIX(str) \
if (!(str)[0] || LASTCHAR((str)) != '.') \
{ \
if ((str)[0]) strncat((str), ".", sizeof((str))-strlen((str))-1); \
strncat((str), soa->origin, sizeof((str))-strlen((str))-1); \
} \
if (LASTCHAR((str)) == '.') LASTCHAR((str)) = '\0';
/**************************************************************************************************
TINYDNS_DUMP_SOA
**************************************************************************************************/
static void
tinydns_dump_soa(MYDNS_SOA *soa)
{
char origin[DNSBUFLEN], ns[DNSBUFLEN], mbox[DNSBUFLEN];
strncpy(origin, soa->origin, sizeof(origin)-1);
strncpy(ns, soa->ns, sizeof(ns)-1);
strncpy(mbox, soa->mbox, sizeof(mbox)-1);
if (LASTCHAR(origin) == '.') LASTCHAR(origin) = '\0';
if (LASTCHAR(ns) == '.') LASTCHAR(ns) = '\0';
if (LASTCHAR(mbox) == '.') LASTCHAR(mbox) = '\0';
printf("Z%s:%s:%s:%u:%u:%u:%u:%u:%u\n",
origin, ns, mbox,
soa->serial, soa->refresh, soa->retry, soa->expire, soa->minimum, soa->ttl);
}
/*--- tinydns_dump_soa() ------------------------------------------------------------------------*/
/**************************************************************************************************
TINYDNS_DUMP_RR
Output resource record, BIND format.
**************************************************************************************************/
static void
tinydns_dump_rr(MYDNS_SOA *soa, MYDNS_RR *rr, int maxlen)
{
char name[DNSBUFLEN], data[DNSBUFLEN];
strncpy(name, rr->name, sizeof(name));
TINYDNS_NAMEFIX(name);
switch (rr->type)
{
case DNS_QTYPE_A:
printf("=%s:%s:%u\n", name, rr->data, rr->ttl);
break;
case DNS_QTYPE_AAAA:
/* Not supported by tinydns (?) */
break;
case DNS_QTYPE_CNAME:
strncpy(data, rr->data, sizeof(data));
TINYDNS_NAMEFIX(data);
printf("C%s:%s:%u\n", name, data, rr->ttl);
break;
case DNS_QTYPE_MX:
strncpy(data, rr->data, sizeof(data));
TINYDNS_NAMEFIX(data);
printf("@%s::%s:%u:%u\n", name, data, rr->aux, rr->ttl);
break;
case DNS_QTYPE_NS:
strncpy(data, rr->data, sizeof(data));
TINYDNS_NAMEFIX(data);
printf(".%s::%s:%u\n", name, data, rr->ttl);
break;
/* tinydns does not natively support SRV; However, there's a patch
(http://tinydns.org/srv-patch) to support it. This code complies with
its format, which is "Sfqdn:ip:x:port:weight:priority:ttl:timestamp" */
case DNS_QTYPE_SRV:
strncpy(data, rr->data, sizeof(data));
TINYDNS_NAMEFIX(data);
printf("S%s::%s:%u:%u:%u:%u\n", name, data, rr->srv_port, rr->srv_weight, rr->aux, rr->ttl);
break;
case DNS_QTYPE_TXT:
{
char databuf[DNSBUFLEN * 4], *c, *d;
/* Need to output colons as octal - also any other wierd chars */
for (c = rr->data, d = databuf; *c; c++)
{
if (*c == ':' || !isprint((int)(*c)))
d += sprintf(d, "\\%03o", *c);
else
*(d++) = *c;
}
*d = '\0';
printf("'%s:%s:%u\n", name, databuf, rr->ttl);
}
break;
default:
break;
}
}
/*--- tinydns_dump_rr() -------------------------------------------------------------------------*/
/**************************************************************************************************
DUMP_SOA
**************************************************************************************************/
static MYDNS_SOA *
dump_soa(void)
{
MYDNS_SOA *soa;
if (mydns_soa_load(sql, &soa, zone) != 0)
ErrSQL(sql, "%s: %s", zone, _("error loading SOA record for zone"));
if (!soa)
Errx("%s: %s", zone, _("zone not found"));
switch (output_format)
{
case OUTPUT_BIND:
bind_dump_soa(soa);
break;
case OUTPUT_TINYDNS:
tinydns_dump_soa(soa);
break;
}
return (soa);
}
/*--- dump_soa() --------------------------------------------------------------------------------*/
/**************************************************************************************************
DUMP_RR
**************************************************************************************************/
static void
dump_rr(MYDNS_SOA *soa, MYDNS_RR *rr, int maxlen)
{
switch (output_format)
{
case OUTPUT_BIND:
bind_dump_rr(soa, rr, maxlen);
break;
case OUTPUT_TINYDNS:
tinydns_dump_rr(soa, rr, maxlen);
break;
}
}
/*--- dump_rr() ---------------------------------------------------------------------------------*/
/**************************************************************************************************
DUMP_RR_LONG
**************************************************************************************************/
static void
dump_rr_long(MYDNS_SOA *soa)
{
int maxlen = 0;
char query[BUFSIZ];
size_t querylen;
SQL_RES *res;
SQL_ROW row;
/* No records in zone - return immediately */
if (!sql_count(sql, "SELECT COUNT(*) FROM %s WHERE zone=%u", mydns_rr_table_name, soa->id))
{
if (output_format == OUTPUT_BIND)
puts("");
return;
}
if (output_format == OUTPUT_BIND)
maxlen = sql_count(sql, "SELECT MAX(LENGTH(name)) FROM %s WHERE zone=%u", mydns_rr_table_name, soa->id) + 1;
if (!maxlen)
maxlen = DNS_MAXNAMELEN;
querylen = snprintf(query, sizeof(query),
"SELECT "MYDNS_RR_FIELDS" FROM %s WHERE zone=%u ORDER BY name,type,aux",
mydns_rr_table_name, soa->id);
/* Submit query */
if (!(res = sql_query(sql, query, querylen)))
return;
/* Add results to list */
while ((row = sql_getrow(res)))
{
MYDNS_RR *rr;
if (!(rr = mydns_rr_parse(row, soa->origin)))
continue;
dump_rr(soa, rr, maxlen);
mydns_rr_free(rr);
}
sql_free(res);
if (output_format == OUTPUT_BIND)
puts("");
}
/*--- dump_rr_long() ----------------------------------------------------------------------------*/
/**************************************************************************************************
DUMP_ZONE
**************************************************************************************************/
static void
dump_zone(char *zone_name)
{
MYDNS_SOA *soa;
strncpy(zone, zone_name, sizeof(zone)-2);
if (LASTCHAR(zone) != '.')
strcat(zone, ".");
if ((soa = dump_soa()))
{
dump_rr_long(soa);
mydns_soa_free(soa);
}
zones_out++;
}
/*--- dump_zone() -------------------------------------------------------------------------------*/
/**************************************************************************************************
MAIN
**************************************************************************************************/
int
main(int argc, char **argv)
{
setlocale(LC_ALL, ""); /* Internationalization */
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
cmdline(argc, argv);
load_config();
db_connect();
dump_header();
if (optind >= argc)
{
SQL_RES *res;
SQL_ROW row;
char query[256];
size_t querylen;
querylen = snprintf(query, sizeof(query), "SELECT origin FROM %s", mydns_soa_table_name);
if (!(res = sql_query(sql, query, querylen)))
return (0);
while ((row = sql_getrow(res)))
dump_zone(row[0]);
sql_free(res);
}
while (optind < argc)
dump_zone((char *)argv[optind++]);
return (0);
}
/*--- main() ------------------------------------------------------------------------------------*/
/* vi:set ts=3: */
/* NEED_PO */
syntax highlighted by Code2HTML, v. 0.9.1