1 /*****************************************************************
2 **
3 **	@(#) gen6dns.c	(c) Feb 2009 - Nov 2015 by hznet H.Zuleger
4 **
5 *****************************************************************/
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <assert.h>
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
17 # include <getopt.h>
18 #endif
19 
20 #include "debug.h"
21 
22 #include "misc.h"
23 #include "ip6.h"
24 #include "hostid.h"
25 #include "subnet.h"
26 #include "scope.h"
27 #include "filep.h"
28 #include "parse.h"
29 #include "squeezev6.h"
30 #include "hostptr.h"
31 #define extern
32 #include "gen6dns.h"
33 #undef extern
34 
35 extern	int	optopt;
36 extern	int	opterr;
37 extern	int	optind;
38 extern	char	*optarg;
39 
40 /* option vars */
41 const	char	*version = VERSION;
42 
43 #if MAXFILEP <= MAXSNET
44 # error "The maximum number of file pointers must be larger than the # of subnets\n"
45 # error "(please change MAXFILEP and/or MAXSNET)\n"
46 #endif
47 
48 #define	BINDCONFIG	0	/* for later version */
49 
50 static	int	revzone = 0;
51 
52 #if defined(BINDCONFIG) && BINDCONFIG
53 int	bindconfig;
54 const char	*multiline;
55 #endif
56 
57 #if defined(DYNUPDATE) && DYNUPDATE
58 # if defined(BINDCONFIG) && BINDCONFIG
59 	/* don't remove leading ':' (see man getopt_long) */
60 #  define	short_options	":6:aBdl:C::D::frb:hmo:p:P:RsSt:vVw"
61 # else
62 #  define	short_options	":6:adl:C::D::frb:ho:p:P:RsSt:vVw"
63 #endif
64 #else
65 # if defined(BINDCONFIG) && BINDCONFIG
66 #  define	short_options	":6:aBC::D::frb:hmo:p:RsSt:vVw"
67 # else
68 #  define	short_options	":6:aC::D::frb:ho:p:RsSt:vVw"
69 # endif
70 #endif
71 
72 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
73 static	struct	option	long_options[] = {
74 	{ "help",		no_argument,		NULL,	'h' },
75 	{ "version",		no_argument,		NULL,	'V' },
76 	{ "revzone",		no_argument,		NULL,	'R' },
77 	{ "verbose",		no_argument,		NULL,	'v' },
78 	{ "comment",		optional_argument,	NULL,	'C' },
79 	{ "write",		no_argument,		NULL,	'w' },
80 	{ "append",		no_argument,		NULL,	'a' },
81 #if defined(BINDCONFIG) && BINDCONFIG
82 	{ "bind-config",	no_argument,		NULL,	'B' },
83 	{ "multiline",		no_argument,		NULL,	'm' },
84 #endif
85 	{ "forward",		no_argument,		NULL,	'f' },
86 	{ "reverse",		no_argument,		NULL,	'r' },
87 	{ "bits",		required_argument,	NULL,	'b' },
88 	{ "ttl",		required_argument,	NULL,	't' },
89 	{ "6to4",		required_argument,	NULL,	'6' },
90 	{ "origin",		required_argument,	NULL,	'o' },
91 	{ "delim",		optional_argument,	NULL,	'D' },
92 	{ "prefix",		required_argument,	NULL,	'p' },	/* lower p */
93 	{ "prefix-add",		required_argument,	NULL,	'p' },	/* lower p */
94 	{ "add-prefix",		required_argument,	NULL,	'p' },	/* lower p */
95 #if defined(DYNUPDATE) && DYNUPDATE
96 	{ "prefix-remove",	required_argument,	NULL,	'P' },	/* upper P */
97 	{ "prefix-del",		required_argument,	NULL,	'P' },	/* upper P */
98 	{ "del-prefix",		required_argument,	NULL,	'P' },	/* upper P */
99 	{ "ddns-update",	no_argument,		NULL,	'd' },
100 	{ "lookup",		required_argument,	NULL,	'l' },
101 #endif
102 	{ "squeeze",		no_argument,		NULL,	's' },
103 	{ "full-squeeze",	no_argument,		NULL,	'S' },
104 };
105 #endif
106 
107 /* static function declaration */
108 static	void	usage (const char *mesg);
109 static	int	gen_dns (int type, const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid, const char *extra_rr);
110 static	int	gen_forw_upd (int del, const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid, const char *extra_rr);
111 static	int	gen_forw_dns (const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid, const char *extra_rr);
112 static	int	gen_rev_dns (const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid);
113 static	int	print_forw_upd (FILE *fp, int del, const char *host, const ip6_t *ipv6, long ttl);
114 static	int	print_forw_dns (FILE *fp, const char *host, const ip6_t *ipv6, long ttl);
115 static	int	print_rev_dns (FILE *fp, const char *host, const char *origin, const ip6_t *ip, int nibbles, long ttl, unsigned long subnetid);
116 #if defined(BINDCONFIG) && BINDCONFIG
117 static	void	print_zoneconfig (const char *zone, const char *fname, const char *multiline);
118 #endif
119 
main(int argc,char * argv[])120 int	main (int argc, char*argv[])
121 {
122 	FILE	*outfp;
123 	int	opt_index;
124 	int	c;
125 	char	tmpstr[127+1];
126 
127 	if ( (progname = strrchr (*argv, '/')) == NULL )
128 		progname = *argv;
129 	else
130 		progname++;
131 
132 	opterr = 0;	/* prevent the printing of the getopt() build-in error messages */
133 
134 	parm.forw = parm.rev = 0;
135 	parm.revbits = 0;
136 	parm.filemode = 0;
137 #if defined(BINDCONFIG) && BINDCONFIG
138 	bindconfig = 0;
139 	multiline = "";
140 #endif
141 
142 #if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG
143 	while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 )
144 #else
145 	while ( (c = getopt (argc, argv, short_options)) != -1 )
146 #endif
147 	{
148 		switch ( c )
149 		{
150 		case 'V':	fprintf (stderr, "%s version %s\n", progname, version);
151 				fprintf (stderr, "based on but not compatible with gen6dns v0.3 (c) Feb 2009 - Apr 2010\n");
152 				fprintf (stderr, "compiled for a maximum of \n");
153 				fprintf (stderr, "\t%5d different prefixes\n", MAXPREFIXES);
154 				fprintf (stderr, "\t%5d different scopes\n", MAXSCOPES);
155 				fprintf (stderr, "\t%5d subnets\n", MAXSNET);
156 				fprintf (stderr, "\t%5d file handles\n", MAXFILEP);
157 				exit (0);
158 				break;
159 		case 'v':	parm.verbose++;
160 				break;
161 		case 'w':	parm.filemode = 1;
162 				break;
163 		case 'a':	parm.filemode = 2;
164 				break;
165 #if defined(BINDCONFIG) && BINDCONFIG
166 		case 'B':	bindconfig = 1;
167 				break;
168 		case 'm':	multiline = "\n";
169 				break;
170 #endif
171 		case 'f':	parm.forw = 1;
172 				break;
173 		case 'r':	parm.rev = 1;
174 				break;
175 		case 'b':	parm.revbits = atoi (optarg);
176 				if ( parm.revbits < 0 || parm.revbits > 128 )
177 					usage ("illegal reverse mask");
178 				if ( parm.revbits % 4 != 0 )
179 					usage ("Option -b: reverse mask must be on nibble boundary");
180 				break;
181 		case 'R':	revzone = 1;
182 				break;
183 		case 's':	parm.squeeze++;
184 				break;
185 		case 'S':	parm.squeeze = 2;
186 				break;
187 		case 'h':	usage (NULL);
188 				break;
189 		case 't':	ttlfromstr (optarg, &parm.ttl);
190 				break;
191 		case 'C':	if ( optarg )
192 				{
193 					if ( *optarg == parm.delim || isspace (*optarg))
194 					{
195 						error ("Option -C \'%c\' would change the comment char to the delimiter char. Option skipped\n", *optarg);
196 					}
197 					else
198 						parm.comment_char = *optarg;
199 				}
200 				break;
201 		case 'D':	if ( optarg )
202 					parm.delim = *optarg;
203 				else
204 					parm.delim = ';';
205 				break;
206 		case 'o':	{
207 				int	len;
208 				len = strlen (optarg);
209 				parm.origin = malloc (len + 1 + 1);
210 				if ( !parm.origin )
211 					usage ("out of memory");
212 				if ( optarg[len-1] != '.' )
213 					snprintf (parm.origin, len+2, "%s.", optarg);
214 				else
215 					snprintf (parm.origin, len+2, "%s", optarg);
216 				}
217 				break;
218 		case 'p':	{		/* a prefix to add */
219 					ip6_t	prfx;
220 
221 					if ( ip6fromstr (&prfx, optarg) < 0 )
222 						usage ("illegal prefix");
223 					prfxput (&prfx, 0);
224 				}
225 				break;
226 		case 'P':	{		/* a prefix to remove */
227 					ip6_t	prfx;
228 
229 					if ( *optarg )
230 					{
231 						if ( ip6fromstr (&prfx, optarg) < 0 )
232 							usage ("illegal prefix");
233 						prfxput (&prfx, 1);
234 					}
235 					else
236 						prfxput (NULL, 1);
237 				}
238 				break;
239 		case 'd':	parm.ddns = 1;		/* dynamic update mode ... */
240 				parm.forw = 1;		/* ... is only defined in forward mode ... */
241 				if ( !parm.ttl )	/* ... and needs a ttl */
242 					parm.ttl = 86400;
243 				break;
244 		case 'l':	parm.lookup = optarg;
245 				break;
246 		case '6':	{
247 					ip6_t	prfx;
248 					int	ip[4];
249 
250 					if ( sscanf (optarg, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]) != 4 )
251 					{
252 						snprintf (tmpstr, sizeof (tmpstr), "Illegal ipv4 address %s\n", optarg);
253 						usage (tmpstr);
254 					}
255 					snprintf (tmpstr, sizeof (tmpstr), "2002:%02x%02x:%02x%02x::/48\n",
256 										ip[0], ip[1], ip[2], ip[3]);
257 					if ( ip6fromstr (&prfx, tmpstr) < 0 )
258 						usage ("illegal 6to4 prefix");
259 					prfxput (&prfx, 0);
260 				}
261 				break;
262 		case ':':	snprintf (tmpstr, sizeof (tmpstr), "option \"-%c\" requires an argument\n", optopt);
263 				usage (tmpstr);
264 				break;
265 		case '?':	if ( isprint (optopt) )
266 					snprintf (tmpstr, sizeof (tmpstr), "%s: unknown option \"-%c\"\n", progname, optopt);
267 				else
268 					snprintf (tmpstr, sizeof (tmpstr), "%s: unknown option char \\x%x\n", progname, optopt);
269 				usage (tmpstr);
270 				break;
271 		default:	abort();
272 		}
273 	}
274 	argc -= optind;
275 	argv += optind;
276 
277 	if ( revzone )	/* Option -R given ? */
278 	{
279 		while ( argc-- > 0 )
280 			hostptr (parm.revbits, *argv++);
281 
282 		exit (0);
283 	}
284 
285 	if ( parm.forw == 0 && parm.rev == 0 )
286 		parm.forw = parm.rev = 1;	/* default is to generate forward and reverse */
287 
288 	/* some error checking */
289 #if defined(BINDCONFIG) && BINDCONFIG
290 	if ( bindconfig && !parm.filemode )
291 		usage ("-B is only supported in filemode (-w or -a)");
292 #endif
293 
294 	if ( parm.rev )		/* check reverse bits for reasonable value */
295 	{
296 		prfx_t	*prf;
297 		int	prefmask;
298 
299 		prf = prfxstart ();
300 		if ( prf == NULL )	/* no prefix list available */
301 			usage ("option -p needed in reverse mode");
302 		prefmask = 128;
303 		while ( prf )
304 		{
305 			int	pmask;
306 
307 			pmask = ip6getmask (prf->prfx);
308 			prefmask = min (prefmask, pmask);
309 			prf = prfxnext ();
310 		}
311 		dbg_val2 ("revbits = %d; min of prefixmasks %d\n", parm.revbits, prefmask);
312 		if ( parm.revbits < prefmask )
313 #if 1
314 			parm.revbits = prefmask;	/* set it to a reasonable value ... */
315 #else
316 						/* ...instead of printing an error message */
317 			usage ("reverse mask must be greater or equal than the network prefix");
318 #endif
319 	}
320 
321 	/* open outputfiles if we are in file mode */
322 	if ( parm.filemode )
323 	{
324 		int	n;
325 
326 		n = filep_init (NULL, parm.filemode == 1 ? "w": "a");
327 		if ( parm.verbose )
328 			fprintf (stderr, "No of concurrent open files is %d while %d files in total can be handled\n", n, MAXFILEP);
329 		outfp = NULL;
330 	}
331 	else
332 		outfp = stdout;
333 
334 	/* loop through file arguments */
335 	if ( argc <= 0 )
336 		parsefile ("stdin", outfp, gen_dns);
337 	else
338 		while ( argc-- > 0 )
339 		{
340 			parsefile (*argv++, outfp, gen_dns);
341 		}
342 	if ( parm.ddns )
343 	{
344 		if ( parm.verbose )
345 			fprintf (outfp, "debug\n");
346 		fprintf (outfp, "send\n");
347 	}
348 
349 
350 	if ( parm.filemode )
351 	{
352 #if defined(BINDCONFIG) && BINDCONFIG
353 		if ( bindconfig )
354 		{
355 			int	i;
356 			int	eol;
357 			long	id;
358 			char	fname[20+1];
359 
360 			id = -1L;
361 			eol = filep_first (&i);
362 			while ( !eol )
363 			{
364 				//snprintf (fname, sizeof (fname), "%0*lx", (parm.revbits - prefmask) / 4, id);
365 				print_zoneconfig (filep_getzone (&i), filep_getname (&i), multiline);
366 				eol = filep_next (&i);
367 			}
368 		}
369 #endif
370 		filep_close ();
371 	}
372 
373 	return 0;
374 }
375 
376 /*****************************************************************
377 **	gendns() generic rr output function
378 *****************************************************************/
gen_dns(int type,const ip6_t * prfx,const scope_t * scope,const host_t * host,const ip6_t * hid,ip6_t * sid,const char * extra_rr)379 static	int	gen_dns (int type, const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid, const char *extra_rr)
380 {
381 	switch ( type )
382 	{
383 	case 'D':	/* dynamic update del */
384 	case 'd':	/* dynamic update add */
385 		return gen_forw_upd (type == 'D', prfx, scope, host, hid, sid, extra_rr);
386 	case 'f':
387 		return gen_forw_dns (prfx, scope, host, hid, sid, extra_rr);
388 	case 'r':
389 		return gen_rev_dns (prfx, scope, host, hid, sid);
390 	default:
391 		fatal ("fatal error: illegal gen_dns type %d\n",  type);
392 	}
393 	/* NOTREACHED */
394 	return -1;
395 }
396 
397 /*****************************************************************
398 **
399 **	gen_forw_upd ()
400 **
401 **	Generate a forward DNS update request
402 **	address build from "prfx", sid, and hid.
403 **	The scope is used to build the filename to print the AAAA
404 **	record.
405 **
406 *****************************************************************/
gen_forw_upd(int del,const ip6_t * prfx,const scope_t * scope,const host_t * host,const ip6_t * hid,ip6_t * sid,const char * extra_rr)407 static	int	gen_forw_upd (int del, const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid, const char *extra_rr)
408 {
409 	const	scope_t	*scp;
410 	FILE	*outfp;
411 	ip6_t	ipv6;
412 	int	ret;
413 	char	fqdn[255+1];
414 
415 	ip6copy (&ipv6, prfx);
416 	ip6setsbit (sid, ip6getebit (&ipv6));
417 	ip6append (&ipv6, sid);
418 	ip6append (&ipv6, hid);
419 
420 	if ( scope )
421 		scp = scope;
422 	else
423 		scp = &parm.defscope;
424 
425 	if ( parm.filemode )
426 	{
427 #if 0
428 		char	fname[MAXFNAME+1];
429 
430 			/* do we have a view field, and it's not empty or "none" */
431 		if ( scp->view && scp->view[0] && strcmp (scp->view, "none") != 0 )
432 			snprintf (fname, sizeof (fname), FORW_FILE_VIEW_TMPL,
433 				scp->view, scp->domain ? scp->domain: parm.origin);
434 		else
435 			snprintf (fname, sizeof (fname), FORW_FILE_TMPL,
436 					scp->domain ? scp->domain: parm.origin);
437 		outfp = filep_open (fname);
438 #else
439 	fatal ("Views are actually not supported in dynamic update mode\n");
440 #endif
441 	}
442 	else
443 		outfp = stdout;
444 
445 	if ( parm.lookup == NULL || strcmp (parm.lookup, host->name) == 0 )
446 	{
447 		snprintf (fqdn, sizeof (fqdn), "%s.%s", host->name, scp->domain ? scp->domain: parm.origin);
448 		ret = print_forw_upd (outfp, del, fqdn, &ipv6, host->ttl ? host->ttl: parm.ttl);
449 		if ( extra_rr && *extra_rr )
450 #if 0
451 			fprintf (outfp, "%s", extra_rr);
452 #else
453 		fatal ("The extra RR records are  actually not supported in dynamic update mode\n");
454 #endif
455 	}
456 	else
457 		ret = 1;
458 
459 	return ret;
460 }
461 
print_forw_upd(FILE * fp,int del,const char * host,const ip6_t * ipv6,long ttl)462 static	int	print_forw_upd (FILE *fp, int del, const char *host, const ip6_t *ipv6, long ttl)
463 {
464 	char	str[127+1];
465 	ip6_t	ip;
466 
467 	fprintf (fp, "update %s ", del ? "del": "add");
468 	fprintf (fp, "%s \t", host);		/* the label */
469 
470 	if ( del )
471 		fprintf (fp, "\t");
472 	else
473 		fprintf (fp, "%lu\t", ttl);	/* optional ttl */
474 
475 	ip6copy (&ip, ipv6);
476 	ip6tostr (str, sizeof (str), &ip);	/* address to string */
477 	squeezev6 (str, str, 1);
478 
479 	fprintf (fp, "IN AAAA %s", str);	/* the AAAA record */
480 
481 	fprintf (fp, "\n");
482 
483 	return 1;
484 }
485 
486 /*****************************************************************
487 **
488 **	gen_forw_dns ()
489 **
490 **	Generate a forward DNS record for "host" with an IPv6
491 **	address build from "prfx", sid, and hid.
492 **	The scope is used to build the filename to print the AAAA
493 **	record.
494 **
495 *****************************************************************/
gen_forw_dns(const ip6_t * prfx,const scope_t * scope,const host_t * host,const ip6_t * hid,ip6_t * sid,const char * extra_rr)496 static	int	gen_forw_dns (const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid, const char *extra_rr)
497 {
498 	const	scope_t	*scp;
499 	FILE	*outfp;
500 	ip6_t	ipv6;
501 	int	ret;
502 	char	fname[MAXFNAME+1];
503 
504 	ip6copy (&ipv6, prfx);
505 	ip6setsbit (sid, ip6getebit (&ipv6));
506 	ip6append (&ipv6, sid);
507 	ip6append (&ipv6, hid);
508 
509 	if ( scope )
510 		scp = scope;
511 	else
512 		scp = &parm.defscope;
513 
514 	if ( parm.filemode )
515 	{
516 			/* do we have a view field, and it's not empty or "none" */
517 		if ( scp->view && scp->view[0] && strcmp (scp->view, "none") != 0 )
518 			snprintf (fname, sizeof (fname), FORW_FILE_VIEW_TMPL,
519 				scp->view, scp->domain ? scp->domain: parm.origin);
520 		else
521 			snprintf (fname, sizeof (fname), FORW_FILE_TMPL,
522 					scp->domain ? scp->domain: parm.origin);
523 		outfp = filep_open (fname);
524 	}
525 	else
526 		outfp = stdout;
527 
528 	ret = print_forw_dns (outfp, host->name, &ipv6, host->ttl ? host->ttl: parm.ttl);
529 	if ( extra_rr && *extra_rr )
530 		fprintf (outfp, "%s", extra_rr);
531 	return ret;
532 }
533 
print_forw_dns(FILE * fp,const char * host,const ip6_t * ipv6,long ttl)534 static	int	print_forw_dns (FILE *fp, const char *host, const ip6_t *ipv6, long ttl)
535 {
536 	char	str[127+1];
537 	ip6_t	ip;
538 
539 	fprintf (fp, "%-21s\t", host);		/* the label */
540 
541 	if ( ttl )
542 		fprintf (fp, "%7lu\t", ttl);	/* optional ttl */
543 	else
544 		fprintf (fp, "\t");
545 
546 	ip6copy (&ip, ipv6);
547 	ip6tostr (str, sizeof (str), &ip);	/* address to string */
548 	if ( parm.squeeze )
549 		squeezev6 (str, str, parm.squeeze >= 2);
550 
551 	fprintf (fp, "IN  AAAA\t%s ", str);	/* the AAAA record */
552 
553 	fprintf (fp, "\n");
554 
555 	return 1;
556 }
557 
558 /*****************************************************************
559 **
560 **	gen_rev_dns ()
561 **
562 **	Generate a reverse DNS record for the ipv6 address build
563 **	out of the "prfx", sid and hid.
564 **	The target of the ptr record is "host" plus the origin
565 **	from the scope or from the command line parameter.
566 **	The scope is used to build the filename to print the PTR
567 **	record to.
568 **
569 *****************************************************************/
gen_rev_dns(const ip6_t * prfx,const scope_t * scope,const host_t * host,const ip6_t * hid,ip6_t * sid)570 static	int	gen_rev_dns (const ip6_t *prfx, const scope_t *scope, const host_t *host, const ip6_t *hid, ip6_t *sid)
571 {
572 	int	nibbles;
573 	int	mask_nibbles;
574 	int	size;
575 	int	i;
576 	FILE	*outfp;
577 	ip6_t	ipv6;
578 	unsigned long	subnetid;
579 	const scope_t	*scp;
580 	char	fname[MAXFNAME+1];
581 
582 	ip6copy (&ipv6, prfx);
583 	ip6setsbit (sid, ip6getebit (&ipv6));
584 	ip6append (&ipv6, sid);
585 	ip6append (&ipv6, hid);
586 
587 	if ( scope )
588 		scp = scope;
589 	else
590 		scp = &parm.defscope;
591 
592 	/* we are looking only for nibbles */
593 	nibbles = parm.revbits / 4;
594 
595 	mask_nibbles = ip6getmask (prfx);
596 	assert ( mask_nibbles > 0 );
597 	mask_nibbles /= 4;
598 	size = nibbles - mask_nibbles;		/* size of subnetid in nibbles */
599 	if ( size < 0 )
600 		size = 0;
601 
602 	subnetid = 0L;		/* which subnet is this ? */
603 	i = mask_nibbles;		/* start at prefmask nibbles! */
604 	while ( i < nibbles  )
605 	{
606 		subnetid += ip6getnibble (&ipv6, i++);
607 		if ( i < nibbles )
608 			subnetid <<= 4;
609 	}
610 
611 	if ( parm.filemode )	/* in filemode lookup the fp for the reverse file */
612 	{
613 			/* do we have a view field, and it's not empty or "none" */
614 		if ( scp->view && scp->view[0] && strcmp (scp->view, "none") != 0 )
615 			snprintf (fname, sizeof (fname), REV_FILE_VIEW_TMPL, scp->view, size, subnetid);
616 		else
617 			snprintf (fname, sizeof (fname), REV_FILE_TMPL, size, subnetid);
618 
619 		if ( (outfp = filep_open (fname)) == NULL )
620 			fatal ("error in opening reverse outputfile for subnet %0*lx\n", size, subnetid);
621 	}
622 	else
623 		outfp = stdout;
624 
625 	return print_rev_dns (outfp, host->name, scp->domain ? scp->domain: parm.origin,
626 				&ipv6, nibbles, host->ttl ? host->ttl : parm.ttl, subnetid);
627 }
628 
print_rev_dns(FILE * fp,const char * host,const char * origin,const ip6_t * ip,int nibbles,long ttl,unsigned long subnetid)629 static	int	print_rev_dns (FILE *fp, const char *host, const char *origin, const ip6_t *ip, int nibbles, long ttl, unsigned long subnetid)
630 {
631 	int	i;
632 
633 	assert ( fp != NULL );
634 	assert ( host != NULL );
635 	assert ( origin != NULL );
636 	assert ( ip != NULL && ip6isset (ip) );
637 
638 	/* for a PTR record the label is a subdomain for each nibble. */
639 	/* print out nibbles starting with the right most one */
640 	i = 32;
641 	while ( i-- > nibbles )
642 		fprintf (fp, "%01x%c", ip6getnibble (ip, i), i > nibbles ? '.': ' ');
643 	fprintf (fp, " ");
644 
645 	if ( ttl )
646 		fprintf (fp, "%7lu ", ttl);	/* optional the ttl */
647 	else
648 		fprintf (fp, "%7s ", "");
649 
650 	fprintf (fp, "IN  PTR\t%s.%s ", host, origin);	/* the PTR record */
651 	if ( subnetid )
652 		fprintf (fp, "; subnetid: %0*lx", 4, subnetid);	/* comment indicating which subnet this PTR is in */
653 	fprintf (fp, "\n");
654 
655 	return 1;
656 }
657 
658 #if defined(BINDCONFIG) && BINDCONFIG
print_zoneconfig(const char * zone,const char * fname,const char * multiline)659 void	print_zoneconfig (const char *zone, const char *fname, const char *multiline)
660 {
661 	printf ("zone \"%s\" IN {%s", zone, multiline);
662 	printf ("\ttype master;%s", multiline);
663 	printf ("\tfile \"%s\";\t%s", fname, multiline);
664 	printf ("};\n");
665 }
666 #endif
667 
668 #if defined(BINDCONFIG) && BINDCONFIG
669 # define	bconfigstr	" [-B [-m]]"
670 #else
671 # define	bconfigstr	""
672 #endif
673 #define	pdconfigstr	"{-p prefix} {-P prefix}"
674 #define	pconfigstr	"{-p prefix |-6 ipv4addr }"
usage(const char * mesg)675 static	void	usage (const char *mesg)
676 {
677 	char	str[127+1];
678 
679 	if ( mesg && *mesg )
680 	{
681 		fprintf (stderr, "%s error: ", progname);
682 		fprintf (stderr, "%s\n", mesg);
683 		fprintf (stderr, "\n");
684 	}
685 	fprintf (stderr, "usage: %s -h|-V\n", progname);
686 	fprintf (stderr, "usage: %s -R [-b mask] <prefix>|<hostname> [...]\n", progname);
687 	fprintf (stderr, "usage: %s [-f] [-s|-S] [-w|-a" bconfigstr "] [-t ttl] [-D<char>] [-C<char>] " pconfigstr " [file ...]\n", progname);
688 	fprintf (stderr, "usage: %s [-r] [-b mask] [-w|-a" bconfigstr "] [-t ttl] [-D<char>] [-C<char>] [-o origin] " pconfigstr " [file ...]\n", progname);
689 #if defined(DYNUPDATE) && DYNUPDATE
690 	fprintf (stderr, "usage: %s -d [-l label] [-t ttl] [-D<char>] [-C<char>] [-o origin] " pdconfigstr " [file ...]\n", progname);
691 #endif
692 	fprintf (stderr, "\t-h, --help\t\t print out this help message \n");
693 	fprintf (stderr, "\t-V, --version\t\t print out version and exit \n");
694 	fprintf (stderr, "\t-R, --revzone\t\t print the ip6.arpa zone of the given prefix \n");
695 	fprintf (stderr, "\t-v, --verbose\t\t give some hints about what's going on in the background \n");
696 	fprintf (stderr, "\t\t\t\t use -v more often to increase the verbosity (up to level 2)\n");
697 	fprintf (stderr, "\t-D, --delim[=char]\t use <char> additionally to white space as delimiter (default is ';')\n");
698 	fprintf (stderr, "\t-C, --comment[=char]\t use <char> as comment character (default is '#')\n");
699 	fprintf (stderr, "\t-s, --squeeze\t\t print ipv6 addresses w/o leading zeros (like 2001:db8:0:0:0:0:0:1/128)\n");
700 	fprintf (stderr, "\t-S, --full-squeeze\t compress ipv6 address slightly more (like 2001:db8::1/128)\n");
701 	fprintf (stderr, "\t-t, --ttl <ttlspec>\t specify ttl of RR (default is none or the host specific one)\n");
702 	fprintf (stderr, "\t\t\t\t known ttl units are s(ecs), m(ins), h(ours), d(ays) or w(eeks) \n");
703 #if defined(DYNUPDATE) && DYNUPDATE
704 	fprintf (stderr, "\t-d, --ddns-update\t print dynamic update messages to stdout (this implies -f) \n");
705 	fprintf (stderr, "\t-l, --lookup=label\t The update add/del is done only for the host matching \"label\" \n");
706 #endif
707 	fprintf (stderr, "\t-f, --forward\t\t generate AAAA records for forward zone only \n");
708 	fprintf (stderr, "\t-r, --reverse\t\t generate PTR records for reverse zone only\n");
709 	fprintf (stderr, "\t\t\t\t The default is to generate forward and reverse zone entries\n");
710 	fprintf (stderr, "\t\t\t\t (The use of the -w switch is highly recommended then)\n");
711 	fprintf (stderr, "\t-b, --bits[=mask]\t split zone files on <mask> boundary (default is prefix size)\n");
712 //fprintf (stderr, "\n sizeof str = %lu\n", sizeof (str));
713 	fprintf (stderr, "\t-w, --write\t\t write output to file instead of stdout\n");
714 	snprintf (str, sizeof (str), FORW_FILE_TMPL, "<origin>");
715 	fprintf (stderr, "\t\t\t\t filename is %s for the forward or ", str);
716 	snprintf (str, sizeof (str), REV_FILE_TMPL, 4, 0L);
717 	fprintf (stderr, "%s for the reverse zone\n", str);
718 	//fprintf (stderr, "\t\t\t\t %s for the reverse zones\n", str);
719 	fprintf (stderr, "\t\t\t\t This option potentially generates a lot of files (up to the\n");
720 	fprintf (stderr, "\t\t\t\t compiled in # of %d)\n", MAXFILEP);
721 	fprintf (stderr, "\t-a, --append\t\t same as -w but append to file instead of overwriting \n");
722 	fprintf (stderr, "\t-o, --origin=zone\t specify forward domain (default is %s)\n", parm.origin);
723 	fprintf (stderr, "\t-p, --add-prefix=prefix\t network prefix to add (default is %s)\n", DEF_PREFIX);
724 #if defined(DYNUPDATE) && DYNUPDATE
725 	fprintf (stderr, "\t-P, --del-prefix=prefix\t network prefix to delete (default is none)\n");
726 #endif
727 	fprintf (stderr, "\t-6, --6to4=ipv4\t\t same as -p but argument is an ipv4 address\n");
728 	fprintf (stderr, "\t\t\t\t resulting prefix is a 6to4 prefix (2002:ipv4:addr::/48)\n");
729 
730 	exit (0);
731 }
732