1 /**************************************************************************************************
2 $Id: export.c,v 1.29 2006/01/18 22:45:51 bboy Exp $
3
4 Outputs zone data in various formats.
5
6 Copyright (C) 2002-2005 Don Moore <bboy@bboy.net>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at Your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 **************************************************************************************************/
22
23 #include "util.h"
24
25 char zone[DNS_MAXNAMELEN]; /* Zone name to dump */
26 unsigned int zones_out = 0; /* Number of zones output */
27 enum _output_format { /* Output format types */
28 OUTPUT_BIND,
29 OUTPUT_TINYDNS
30 } output_format = OUTPUT_BIND;
31
32 char hostname[256]; /* Hostname of local machine */
33 char *command_line = NULL; /* Full command line */
34
35 #define DNSBUFLEN DNS_MAXNAMELEN+1
36
37
38 /**************************************************************************************************
39 USAGE
40 Display program usage information.
41 **************************************************************************************************/
42 static void
usage(status)43 usage(status)
44 int status;
45 {
46 if (status != EXIT_SUCCESS)
47 {
48 fprintf(stderr, _("Try `%s --help' for more information."), progname);
49 fputs("\n", stderr);
50 }
51 else
52 {
53 printf(_("Usage: %s [ZONE]..."), progname);
54 puts("");
55 puts(_("Output MyDNS zone in formats understood by other DNS servers."));
56 puts("");
57 /* puts("----------------------------------------------------------------------------78"); */
58 puts(_(" -b, --bind output in BIND format (the default)"));
59 puts(_(" -t, --tinydns-data output in tinydns-data format"));
60 puts("");
61 puts(_(" -D, --database=DB database name to use"));
62 puts(_(" -h, --host=HOST connect to SQL server at HOST"));
63 puts(_(" -p, --password=PASS password for SQL server (or prompt from tty)"));
64 puts(_(" -u, --user=USER username for SQL server if not current user"));
65 puts("");
66 #if DEBUG_ENABLED
67 puts(_(" -d, --debug enable debug output"));
68 #endif
69 puts(_(" -v, --verbose be more verbose while running"));
70 puts(_(" --help display this help and exit"));
71 puts(_(" --version output version information and exit"));
72 puts("");
73 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
74 }
75 exit(status);
76 }
77 /*--- usage() -----------------------------------------------------------------------------------*/
78
79
80 /**************************************************************************************************
81 CMDLINE
82 Process command line options.
83 **************************************************************************************************/
84 static void
cmdline(int argc,char ** argv)85 cmdline(int argc, char **argv)
86 {
87 char *optstr;
88 int optc, optindex, n;
89 struct option const longopts[] =
90 {
91 {"bind", no_argument, NULL, 'b'},
92 {"database", required_argument, NULL, 'D'},
93 {"host", required_argument, NULL, 'h'},
94 {"password", optional_argument, NULL, 'p'},
95 {"tinydns-data", no_argument, NULL, 't'},
96 {"user", required_argument, NULL, 'u'},
97
98 {"debug", no_argument, NULL, 'd'},
99 {"verbose", no_argument, NULL, 'v'},
100 {"help", no_argument, NULL, 0},
101 {"version", no_argument, NULL, 0},
102
103 {NULL, 0, NULL, 0}
104 };
105
106 /* Copy full command line into 'command_line' */
107 for (n = 0; n < argc; n++)
108 {
109 if (!command_line)
110 {
111 if (!(command_line = strdup(argv[n])))
112 Err("strdup");
113 }
114 else
115 {
116 if (!(command_line = realloc(command_line, strlen(command_line) + strlen(argv[n]) + 2)))
117 Err("realloc");
118 strcat(command_line, " ");
119 strcat(command_line, argv[n]);
120 }
121 }
122
123 gethostname(hostname, sizeof(hostname)-1);
124
125 err_file = stdout;
126 error_init(argv[0], LOG_USER); /* Init output routines */
127 optstr = getoptstr(longopts);
128 while ((optc = getopt_long(argc, argv, optstr, longopts, &optindex)) != -1)
129 {
130 switch (optc)
131 {
132 case 0:
133 {
134 const char *opt = longopts[optindex].name;
135
136 if (!strcmp(opt, "version")) /* --version */
137 {
138 printf("%s ("PACKAGE_NAME") "PACKAGE_VERSION" ("SQL_VERSION_STR")\n", progname);
139 puts("\n" PACKAGE_COPYRIGHT);
140 puts(_("This is free software; see the source for copying conditions. There is NO"));
141 puts(_("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."));
142 exit(EXIT_SUCCESS);
143 }
144 else if (!strcmp(opt, "help")) /* --help */
145 usage(EXIT_SUCCESS);
146 }
147 break;
148
149 case 'd': /* -d, --debug */
150 #if DEBUG_ENABLED
151 err_verbose = err_debug = 1;
152 #endif
153 break;
154 case 'D': /* -D, --database=DB */
155 conf_set(&Conf, "database", optarg, 0);
156 break;
157 case 'h': /* -h, --host=HOST */
158 conf_set(&Conf, "db-host", optarg, 0);
159 break;
160 case 'p': /* -p, --password=PASS */
161 if (optarg)
162 {
163 conf_set(&Conf, "db-password", optarg, 0);
164 memset(optarg, 'X', strlen(optarg));
165 }
166 else
167 conf_set(&Conf, "db-password", passinput(_("Enter password")), 0);
168 break;
169 case 'u': /* -u, --user=USER */
170 conf_set(&Conf, "db-user", optarg, 0);
171 break;
172
173 case 'b': /* -b, --bind */
174 output_format = OUTPUT_BIND;
175 break;
176 case 't': /* -t, --tinydns-data */
177 output_format = OUTPUT_TINYDNS;
178 break;
179
180 case 'v': /* -v, --verbose */
181 err_verbose = 1;
182 break;
183 default:
184 usage(EXIT_FAILURE);
185 }
186 }
187 }
188 /*--- cmdline() ---------------------------------------------------------------------------------*/
189
190
191 /**************************************************************************************************
192 DUMP_HEADER
193 Dumps a header. Currently only writes a header for tinydns-data format, since BIND format
194 puts a header at the start of each "file".
195 **************************************************************************************************/
196 static void
dump_header(void)197 dump_header(void)
198 {
199 time_t now = time(NULL);
200
201 switch (output_format)
202 {
203 case OUTPUT_BIND:
204 break;
205 case OUTPUT_TINYDNS:
206 printf("#\n");
207 printf("# Created by \"%s\"\n", command_line);
208 printf("# %.24s\n", ctime(&now));
209 printf("#\n");
210 break;
211 }
212 }
213 /*--- dump_header() -----------------------------------------------------------------------------*/
214
215
216 /**************************************************************************************************
217 BIND_DUMP_SOA
218 Output SOA, BIND format.
219 **************************************************************************************************/
220 static void
bind_dump_soa(MYDNS_SOA * soa)221 bind_dump_soa(MYDNS_SOA *soa)
222 {
223 time_t now = time(NULL);
224
225 if (zones_out > 0)
226 puts("\f");
227
228 printf("$TTL %u\n", soa->ttl);
229 printf("; Zone: %s (#%u)\n", soa->origin, soa->id);
230 printf("; Created by \"%s\"\n", command_line);
231 printf("; %.24s\n", ctime(&now));
232 printf("$ORIGIN %s\n\n", soa->origin);
233
234 printf("@\tIN SOA\t%s\t%s (\n", soa->ns, soa->mbox);
235 printf("\t%-10u\t ; Serial\n", soa->serial);
236 printf("\t%-10u\t ; Refresh\n", soa->refresh);
237 printf("\t%-10u\t ; Retry\n", soa->retry);
238 printf("\t%-10u\t ; Expire\n", soa->expire);
239 printf("\t%-10u\t) ; Minimum\n\n", soa->minimum);
240 }
241 /*--- bind_dump_soa() ---------------------------------------------------------------------------*/
242
243
244 /**************************************************************************************************
245 BIND_DUMP_RR
246 Output resource record, BIND format.
247 **************************************************************************************************/
248 static void
bind_dump_rr(MYDNS_SOA * soa,MYDNS_RR * rr,int maxlen)249 bind_dump_rr(MYDNS_SOA *soa, MYDNS_RR *rr, int maxlen)
250 {
251 char *type = mydns_qtype_str(
252 #if ALIAS_ENABLED
253 (rr->alias == 0) ?
254 #endif /* ALIAS_ENABLED */
255 rr->type
256 #if ALIAS_ENABLED
257 : DNS_QTYPE_CNAME
258 #endif /* ALIAS_ENABLED */
259 );
260
261 printf("%-*s", maxlen, rr->name);
262
263 printf("\t%u\tIN %-5s\t", (rr->ttl < soa->minimum) ? soa->minimum : rr->ttl, type);
264
265 if (rr->type == DNS_QTYPE_MX)
266 printf("%u %s\n", (uint32_t)rr->aux, rr->data);
267 else if (rr->type == DNS_QTYPE_SRV)
268 printf("%u %u %u %s\n", (uint32_t)rr->aux, rr->srv_weight, rr->srv_port, rr->data);
269 else if (rr->type == DNS_QTYPE_RP)
270 printf("%s %s\n", rr->data, rr->rp_txt);
271 else if (rr->type == DNS_QTYPE_TXT)
272 {
273 register unsigned char *c;
274 putc('"', stdout);
275 for (c = rr->data; *c; c++)
276 {
277 if (*c == '"')
278 putc('\\', stdout);
279 putc(*c, stdout);
280 }
281 putc('"', stdout);
282 putc('\n', stdout);
283 }
284 else
285 printf("%s\n", rr->data);
286 }
287 /*--- bind_dump_rr() ----------------------------------------------------------------------------*/
288
289
290 #define TINYDNS_NAMEFIX(str) \
291 if (!(str)[0] || LASTCHAR((str)) != '.') \
292 { \
293 if ((str)[0]) strncat((str), ".", sizeof((str))-strlen((str))-1); \
294 strncat((str), soa->origin, sizeof((str))-strlen((str))-1); \
295 } \
296 if (LASTCHAR((str)) == '.') LASTCHAR((str)) = '\0';
297
298
299 /**************************************************************************************************
300 TINYDNS_DUMP_SOA
301 **************************************************************************************************/
302 static void
tinydns_dump_soa(MYDNS_SOA * soa)303 tinydns_dump_soa(MYDNS_SOA *soa)
304 {
305 char origin[DNSBUFLEN], ns[DNSBUFLEN], mbox[DNSBUFLEN];
306
307 strncpy(origin, soa->origin, sizeof(origin)-1);
308 strncpy(ns, soa->ns, sizeof(ns)-1);
309 strncpy(mbox, soa->mbox, sizeof(mbox)-1);
310
311 if (LASTCHAR(origin) == '.') LASTCHAR(origin) = '\0';
312 if (LASTCHAR(ns) == '.') LASTCHAR(ns) = '\0';
313 if (LASTCHAR(mbox) == '.') LASTCHAR(mbox) = '\0';
314
315 printf("Z%s:%s:%s:%u:%u:%u:%u:%u:%u\n",
316 origin, ns, mbox,
317 soa->serial, soa->refresh, soa->retry, soa->expire, soa->minimum, soa->ttl);
318 }
319 /*--- tinydns_dump_soa() ------------------------------------------------------------------------*/
320
321
322 /**************************************************************************************************
323 TINYDNS_DUMP_RR
324 Output resource record, BIND format.
325 **************************************************************************************************/
326 static void
tinydns_dump_rr(MYDNS_SOA * soa,MYDNS_RR * rr,int maxlen)327 tinydns_dump_rr(MYDNS_SOA *soa, MYDNS_RR *rr, int maxlen)
328 {
329 char name[DNSBUFLEN], data[DNSBUFLEN];
330
331 strncpy(name, rr->name, sizeof(name));
332 TINYDNS_NAMEFIX(name);
333
334 switch (rr->type)
335 {
336 case DNS_QTYPE_A:
337 printf("=%s:%s:%u\n", name, rr->data, rr->ttl);
338 break;
339
340 case DNS_QTYPE_AAAA:
341 /* Not supported by tinydns (?) */
342 break;
343
344 case DNS_QTYPE_CNAME:
345 strncpy(data, rr->data, sizeof(data));
346 TINYDNS_NAMEFIX(data);
347 printf("C%s:%s:%u\n", name, data, rr->ttl);
348 break;
349
350 case DNS_QTYPE_MX:
351 strncpy(data, rr->data, sizeof(data));
352 TINYDNS_NAMEFIX(data);
353 printf("@%s::%s:%u:%u\n", name, data, rr->aux, rr->ttl);
354 break;
355
356 case DNS_QTYPE_NS:
357 strncpy(data, rr->data, sizeof(data));
358 TINYDNS_NAMEFIX(data);
359 printf(".%s::%s:%u\n", name, data, rr->ttl);
360 break;
361
362 /* tinydns does not natively support SRV; However, there's a patch
363 (http://tinydns.org/srv-patch) to support it. This code complies with
364 its format, which is "Sfqdn:ip:x:port:weight:priority:ttl:timestamp" */
365 case DNS_QTYPE_SRV:
366 strncpy(data, rr->data, sizeof(data));
367 TINYDNS_NAMEFIX(data);
368 printf("S%s::%s:%u:%u:%u:%u\n", name, data, rr->srv_port, rr->srv_weight, rr->aux, rr->ttl);
369 break;
370
371 case DNS_QTYPE_TXT:
372 {
373 char databuf[DNSBUFLEN * 4], *c, *d;
374
375 /* Need to output colons as octal - also any other wierd chars */
376 for (c = rr->data, d = databuf; *c; c++)
377 {
378 if (*c == ':' || !isprint((int)(*c)))
379 d += sprintf(d, "\\%03o", *c);
380 else
381 *(d++) = *c;
382 }
383 *d = '\0';
384 printf("'%s:%s:%u\n", name, databuf, rr->ttl);
385 }
386 break;
387
388 default:
389 break;
390 }
391 }
392 /*--- tinydns_dump_rr() -------------------------------------------------------------------------*/
393
394
395 /**************************************************************************************************
396 DUMP_SOA
397 **************************************************************************************************/
398 static MYDNS_SOA *
dump_soa(void)399 dump_soa(void)
400 {
401 MYDNS_SOA *soa;
402
403 if (mydns_soa_load(sql, &soa, zone) != 0)
404 ErrSQL(sql, "%s: %s", zone, _("error loading SOA record for zone"));
405 if (!soa)
406 Errx("%s: %s", zone, _("zone not found"));
407
408 switch (output_format)
409 {
410 case OUTPUT_BIND:
411 bind_dump_soa(soa);
412 break;
413 case OUTPUT_TINYDNS:
414 tinydns_dump_soa(soa);
415 break;
416 }
417
418 return (soa);
419 }
420 /*--- dump_soa() --------------------------------------------------------------------------------*/
421
422
423 /**************************************************************************************************
424 DUMP_RR
425 **************************************************************************************************/
426 static void
dump_rr(MYDNS_SOA * soa,MYDNS_RR * rr,int maxlen)427 dump_rr(MYDNS_SOA *soa, MYDNS_RR *rr, int maxlen)
428 {
429 switch (output_format)
430 {
431 case OUTPUT_BIND:
432 bind_dump_rr(soa, rr, maxlen);
433 break;
434 case OUTPUT_TINYDNS:
435 tinydns_dump_rr(soa, rr, maxlen);
436 break;
437 }
438 }
439 /*--- dump_rr() ---------------------------------------------------------------------------------*/
440
441
442 /**************************************************************************************************
443 DUMP_RR_LONG
444 **************************************************************************************************/
445 static void
dump_rr_long(MYDNS_SOA * soa)446 dump_rr_long(MYDNS_SOA *soa)
447 {
448 int maxlen = 0;
449 char query[BUFSIZ];
450 size_t querylen;
451 SQL_RES *res;
452 SQL_ROW row;
453
454 /* No records in zone - return immediately */
455 if (!sql_count(sql, "SELECT COUNT(*) FROM %s WHERE zone=%u", mydns_rr_table_name, soa->id))
456 {
457 if (output_format == OUTPUT_BIND)
458 puts("");
459 return;
460 }
461
462 if (output_format == OUTPUT_BIND)
463 maxlen = sql_count(sql, "SELECT MAX(LENGTH(name)) FROM %s WHERE zone=%u", mydns_rr_table_name, soa->id) + 1;
464
465 if (!maxlen)
466 maxlen = DNS_MAXNAMELEN;
467
468 querylen = snprintf(query, sizeof(query),
469 "SELECT "MYDNS_RR_FIELDS" FROM %s WHERE zone=%u ORDER BY name,type,aux",
470 mydns_rr_table_name, soa->id);
471
472 /* Submit query */
473 if (!(res = sql_query(sql, query, querylen)))
474 return;
475
476 /* Add results to list */
477 while ((row = sql_getrow(res)))
478 {
479 MYDNS_RR *rr;
480
481 if (!(rr = mydns_rr_parse(row, soa->origin)))
482 continue;
483 dump_rr(soa, rr, maxlen);
484 mydns_rr_free(rr);
485 }
486 sql_free(res);
487
488 if (output_format == OUTPUT_BIND)
489 puts("");
490 }
491 /*--- dump_rr_long() ----------------------------------------------------------------------------*/
492
493
494 /**************************************************************************************************
495 DUMP_ZONE
496 **************************************************************************************************/
497 static void
dump_zone(char * zone_name)498 dump_zone(char *zone_name)
499 {
500 MYDNS_SOA *soa;
501
502 strncpy(zone, zone_name, sizeof(zone)-2);
503 if (LASTCHAR(zone) != '.')
504 strcat(zone, ".");
505 if ((soa = dump_soa()))
506 {
507 dump_rr_long(soa);
508 mydns_soa_free(soa);
509 }
510 zones_out++;
511 }
512 /*--- dump_zone() -------------------------------------------------------------------------------*/
513
514
515 /**************************************************************************************************
516 MAIN
517 **************************************************************************************************/
518 int
main(int argc,char ** argv)519 main(int argc, char **argv)
520 {
521 setlocale(LC_ALL, ""); /* Internationalization */
522 bindtextdomain(PACKAGE, LOCALEDIR);
523 textdomain(PACKAGE);
524 cmdline(argc, argv);
525 load_config();
526 db_connect();
527
528 dump_header();
529 if (optind >= argc)
530 {
531 SQL_RES *res;
532 SQL_ROW row;
533 char query[256];
534 size_t querylen;
535
536 querylen = snprintf(query, sizeof(query), "SELECT origin FROM %s", mydns_soa_table_name);
537 if (!(res = sql_query(sql, query, querylen)))
538 return (0);
539
540 while ((row = sql_getrow(res)))
541 dump_zone(row[0]);
542 sql_free(res);
543 }
544
545 while (optind < argc)
546 dump_zone((char *)argv[optind++]);
547
548 return (0);
549 }
550 /*--- main() ------------------------------------------------------------------------------------*/
551
552 /* vi:set ts=3: */
553 /* NEED_PO */
554