1/*****************************************************************
2**
3**	@(#) misc.c -- helper functions for the dnssec zone key tools
4**
5**	Copyright (c) Jan 2005, Holger Zuleger HZnet. All rights reserved.
6**
7**	This software is open source.
8**
9**	Redistribution and use in source and binary forms, with or without
10**	modification, are permitted provided that the following conditions
11**	are met:
12**
13**	Redistributions of source code must retain the above copyright notice,
14**	this list of conditions and the following disclaimer.
15**
16**	Redistributions in binary form must reproduce the above copyright notice,
17**	this list of conditions and the following disclaimer in the documentation
18**	and/or other materials provided with the distribution.
19**
20**	Neither the name of Holger Zuleger HZnet nor the names of its contributors may
21**	be used to endorse or promote products derived from this software without
22**	specific prior written permission.
23**
24**	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25**	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26**	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27**	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
28**	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29**	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30**	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31**	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32**	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33**	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34**	POSSIBILITY OF SUCH DAMAGE.
35**
36*****************************************************************/
37# include <stdio.h>
38# include <string.h>
39# include <stdlib.h>
40# include <unistd.h>	/* for link(), unlink() */
41# include <ctype.h>
42# include <sys/types.h>
43# include <sys/stat.h>
44# include <time.h>
45# include <utime.h>
46# include <assert.h>
47# include <errno.h>
48# include <fcntl.h>
49#ifdef HAVE_CONFIG_H
50# include <config.h>
51#endif
52# include "config_zkt.h"
53# include "zconf.h"
54# include "log.h"
55# include "debug.h"
56#define extern
57# include "misc.h"
58#undef extern
59
60# define	TAINTEDCHARS	"`$@;&<>|"
61
62extern	const	char	*progname;
63
64/*****************************************************************
65**	getnameappendix (progname, basename)
66**	return a pointer to the substring in progname subsequent
67**	following "<basename>-".
68*****************************************************************/
69const	char	*getnameappendix (const char *progname, const char *basename)
70{
71	const	char	*p;
72	int	baselen;
73
74	assert (progname != NULL);
75	assert (basename != NULL);
76
77	if ( (p = strrchr (progname, '/')) != NULL )
78		p++;
79	else
80		p = progname;
81
82	baselen = strlen (basename);
83	if ( strncmp (p, basename, baselen-1) == 0 && *(p+baselen) == '-' )
84	{
85		p += baselen + 1;
86		if ( *p )
87			return p;
88	}
89
90	return NULL;
91}
92
93/*****************************************************************
94**	getdefconfname (view)
95**	returns a pointer to a dynamic string containing the
96**	default configuration file name
97*****************************************************************/
98const	char	*getdefconfname (const char *view)
99{
100	char	*p;
101	char	*file;
102	char	*buf;
103	int	size;
104
105	if ( (file = getenv ("ZKT_CONFFILE")) == NULL )
106		file = CONFIG_FILE;
107	dbg_val2 ("getdefconfname (%s) file = %s\n", view ? view : "NULL", file);
108
109	if ( view == NULL || *view == '\0' || (p = strrchr (file, '.')) == NULL )
110		return strdup (file);
111
112	size = strlen (file) + strlen (view) + 1 + 1;
113	if ( (buf = malloc (size)) == NULL )
114		return strdup (file);
115
116	dbg_val1 ("0123456789o123456789o123456789\tsize=%d\n", size);
117	dbg_val4 ("%.*s-%s%s\n", p - file, file, view, p);
118
119	snprintf (buf, size, "%.*s-%s%s", p - file, file, view, p);
120	return buf;
121}
122
123/*****************************************************************
124**	domain_canonicdup (s)
125**	returns NULL or a pointer to a dynamic string containing the
126**	canonic (all lower case letters and ending with a '.')
127**	domain name
128*****************************************************************/
129char	*domain_canonicdup (const char *s)
130{
131	char	*new;
132	char	*p;
133	int	len;
134	int	add_dot;
135
136	if ( s == NULL )
137		return NULL;
138
139	add_dot = 0;
140	len = strlen (s);
141	if ( len > 0 && s[len-1] != '.' )
142		add_dot = len++;
143
144	if ( (new = p = malloc (len + 1)) == NULL )
145		return NULL;
146
147	while ( *s )
148		*p++ = tolower (*s++);
149	if ( add_dot )
150		*p++ = '.';
151	*p = '\0';
152
153	return new;
154}
155#if 0		/* replaced by domain_canonicdup */
156/*****************************************************************
157**	str_tolowerdup (s)
158*****************************************************************/
159char	*str_tolowerdup (const char *s)
160{
161	char	*new;
162	char	*p;
163
164	if ( s == NULL || (new = p = malloc (strlen (s) + 1)) == NULL )
165		return NULL;
166
167	while ( *s )
168		*p++ = tolower (*s++);
169	*p = '\0';
170
171	return new;
172}
173#endif
174
175/*****************************************************************
176**	str_delspace (s)
177**	Remove in string 's' all white space char
178*****************************************************************/
179char	*str_delspace (char *s)
180{
181	char	*start;
182	char	*p;
183
184	if ( !s )	/* no string present ? */
185		return NULL;
186
187	start = s;
188	for ( p = s; *p; p++ )
189		if ( !isspace (*p) )
190			*s++ = *p;	/* copy each nonspace */
191
192	*s = '\0';	/* terminate string */
193
194	return start;
195}
196
197/*****************************************************************
198**	in_strarr (str, arr, cnt)
199**	check if string array 'arr' contains the string 'str'
200**	return 1 if true or 'arr' or 'str' is empty, otherwise 0
201*****************************************************************/
202int	in_strarr (const char *str, char *const arr[], int cnt)
203{
204	if ( arr == NULL || cnt <= 0 )
205		return 1;
206
207	if ( str == NULL || *str == '\0' )
208		return 0;
209
210	while ( --cnt >= 0 )
211		if ( strcmp (str, arr[cnt]) == 0 )
212			return 1;
213
214	return 0;
215}
216
217/*****************************************************************
218**	str_untaint (s)
219**	Remove in string 's' all TAINTED chars
220*****************************************************************/
221char	*str_untaint (char *str)
222{
223	char	*p;
224
225	assert (str != NULL);
226
227	for ( p = str; *p; p++ )
228		if ( strchr (TAINTEDCHARS, *p) )
229			*p = ' ';
230	return str;
231}
232
233/*****************************************************************
234**	str_chop (str, c)
235**	delete all occurrences of char 'c' at the end of string 's'
236*****************************************************************/
237char	*str_chop (char *str, char c)
238{
239	int	len;
240
241	assert (str != NULL);
242
243	len = strlen (str) - 1;
244	while ( len >= 0 && str[len] == c )
245		str[len--] = '\0';
246
247	return str;
248}
249
250/*****************************************************************
251**	parseurl (url, &proto, &host, &port, &para )
252**	parses the given url (e.g. "proto://host.with.domain:port/para")
253**	and set the pointer variables to the corresponding part of the string.
254*****************************************************************/
255void	parseurl (char *url, char **proto, char **host, char **port, char **para)
256{
257	char	*start;
258	char	*p;
259
260	assert ( url != NULL );
261
262	/* parse protocol */
263	if ( (p = strchr (url, ':')) == NULL )	/* no protocol string given ? */
264		p = url;
265	else					/* looks like a protocol string */
266		if ( p[1] == '/' && p[2] == '/' )	/* protocol string ? */
267		{
268			*p = '\0';
269			p += 3;
270			if ( proto )
271				*proto = url;
272		}
273		else				/* no protocol string found ! */
274			p = url;
275
276	/* parse host */
277	if ( *p == '[' )	/* ipv6 address as hostname ? */
278	{
279		for ( start = ++p; *p && *p != ']'; p++ )
280			;
281		if ( *p )
282			*p++ = '\0';
283	}
284	else
285		for ( start = p; *p && *p != ':' && *p != '/'; p++ )
286			;
287	if ( host )
288		*host = start;
289
290	/* parse port */
291	if ( *p == ':' )
292	{
293		*p++ = '\0';
294		for ( start = p; *p && isdigit (*p); p++ )
295			;
296		if ( *p )
297			*p++ = '\0';
298		if ( port )
299			*port = start;
300	}
301
302	if ( *p == '/' )
303		*p++ = '\0';
304
305	if ( *p && para )
306		*para = p;
307}
308
309/*****************************************************************
310**	splitpath (path, pathsize, filename)
311**	if filename is build of "path/file" then copy filename to path
312**	and split of the filename part.
313**	return pointer to filename part in path or NULL if path is too
314**	small to hold "path+filename"
315*****************************************************************/
316const	char	*splitpath (char *path, size_t psize, const char *filename)
317{
318	char 	*p;
319
320	if ( !path )
321		return NULL;
322
323	*path = '\0';
324	if ( !filename )
325		return filename;
326
327	if ( (p = strrchr (filename, '/')) )	/* file arg contains path ? */
328	{
329		if ( strlen (filename) + 1 > psize )
330			return filename;
331
332		strcpy (path, filename);	/* copy whole filename to path */
333		path[p-filename] = '\0';	/* split of the file part */
334		filename = ++p;
335	}
336	return filename;
337}
338
339/*****************************************************************
340**	pathname (path, size, dir, file, ext)
341**	Concatenate 'dir', 'file' and 'ext' (if not null) to build
342**	a pathname, and store the result in the character array
343**	with length 'size' pointed to by 'path'.
344*****************************************************************/
345char	*pathname (char *path, size_t size, const char *dir, const char *file, const char *ext)
346{
347	int	len;
348
349	if ( path == NULL || file == NULL )
350		return path;
351
352	len = strlen (file) + 1;
353	if ( dir )
354		len += strlen (dir);
355	if ( ext )
356		len += strlen (ext);
357	if ( len > size )
358		return path;
359
360	*path = '\0';
361	if ( dir && *dir )
362	{
363		len = sprintf (path, "%s", dir);
364		if ( path[len-1] != '/' )
365		{
366			path[len++] = '/';
367			path[len] = '\0';
368		}
369	}
370	strcat (path, file);
371	if ( ext )
372		strcat (path, ext);
373	return path;
374}
375
376/*****************************************************************
377**	is_directory (name)
378**	Check if the given pathname 'name' exists and is a directory.
379**	returns 0 | 1
380*****************************************************************/
381int	is_directory (const char *name)
382{
383	struct	stat	st;
384
385	if ( !name || !*name )
386		return 0;
387
388	return ( stat (name, &st) == 0 && S_ISDIR (st.st_mode) );
389}
390
391/*****************************************************************
392**	fileexist (name)
393**	Check if a file with the given pathname 'name' exists.
394**	returns 0 | 1
395*****************************************************************/
396int	fileexist (const char *name)
397{
398	struct	stat	st;
399	return ( stat (name, &st) == 0 && S_ISREG (st.st_mode) );
400}
401
402/*****************************************************************
403**	filesize (name)
404**	return the size of the file with the given pathname 'name'.
405**	returns -1 if the file not exist
406*****************************************************************/
407size_t	filesize (const char *name)
408{
409	struct	stat	st;
410	if  ( stat (name, &st) == -1 )
411		return -1L;
412	return ( st.st_size );
413}
414
415/*****************************************************************
416**	is_keyfilename (name)
417**	Check if the given name looks like a dnssec (public)
418**	keyfile name. Returns 0 | 1
419*****************************************************************/
420int	is_keyfilename (const char *name)
421{
422	int	len;
423
424	if ( name == NULL || *name != 'K' )
425		return 0;
426
427	len = strlen (name);
428	if ( len > 4 && strcmp (&name[len - 4], ".key") == 0 )
429		return 1;
430
431	return 0;
432}
433
434/*****************************************************************
435**	is_dotfilename (name)
436**	Check if the given pathname 'name' looks like "." or "..".
437**	Returns 0 | 1
438*****************************************************************/
439int	is_dotfilename (const char *name)
440{
441	if ( name && (
442	     (name[0] == '.' && name[1] == '\0') ||
443	     (name[0] == '.' && name[1] == '.' && name[2] == '\0')) )
444		return 1;
445
446	return 0;
447}
448
449/*****************************************************************
450**	touch (name, sec)
451**	Set the modification time of the given pathname 'fname' to
452**	'sec'.	Returns 0 on success.
453*****************************************************************/
454int	touch (const char *fname, time_t sec)
455{
456	struct	utimbuf	utb;
457
458	utb.actime = utb.modtime = sec;
459	return utime (fname, &utb);
460}
461
462/*****************************************************************
463**	linkfile (fromfile, tofile)
464*****************************************************************/
465int	linkfile (const char *fromfile, const char *tofile)
466{
467	int	ret;
468
469	/* fprintf (stderr, "linkfile (%s, %s)\n", fromfile, tofile); */
470	if ( (ret = link (fromfile, tofile)) == -1 && errno == EEXIST )
471		if ( unlink (tofile) == 0 )
472			ret = link (fromfile, tofile);
473
474	return ret;
475}
476
477/*****************************************************************
478**	copyfile (fromfile, tofile, dnskeyfile)
479**	copy fromfile into tofile.
480**	Add (optional) the content of dnskeyfile to tofile.
481*****************************************************************/
482int	copyfile (const char *fromfile, const char *tofile, const char *dnskeyfile)
483{
484	FILE	*infp;
485	FILE	*outfp;
486	int	c;
487
488	/* fprintf (stderr, "copyfile (%s, %s)\n", fromfile, tofile); */
489	if ( (infp = fopen (fromfile, "r")) == NULL )
490		return -1;
491	if ( (outfp = fopen (tofile, "w")) == NULL )
492	{
493		fclose (infp);
494		return -2;
495	}
496	while ( (c = getc (infp)) != EOF )
497		putc (c, outfp);
498
499	fclose (infp);
500	if ( dnskeyfile && *dnskeyfile && (infp = fopen (dnskeyfile, "r")) != NULL )
501	{
502		while ( (c = getc (infp)) != EOF )
503			putc (c, outfp);
504		fclose (infp);
505	}
506	fclose (outfp);
507
508	return 0;
509}
510
511/*****************************************************************
512**	copyzonefile (fromfile, tofile, dnskeyfile)
513**	copy a already signed zonefile and replace all zone DNSKEY
514**	resource records by one "$INCLUDE dnskey.db" line
515*****************************************************************/
516int	copyzonefile (const char *fromfile, const char *tofile, const char *dnskeyfile)
517{
518	FILE	*infp;
519	FILE	*outfp;
520	int	len;
521	int	dnskeys;
522	int	multi_line_dnskey;
523	int	bufoverflow;
524	char	buf[1024];
525	char	*p;
526
527	if ( fromfile == NULL )
528		infp = stdin;
529	else
530		if ( (infp = fopen (fromfile, "r")) == NULL )
531			return -1;
532	if ( tofile == NULL )
533		outfp = stdout;
534	else
535		if ( (outfp = fopen (tofile, "w")) == NULL )
536		{
537			if ( fromfile )
538				fclose (infp);
539			return -2;
540		}
541
542	multi_line_dnskey = 0;
543	dnskeys = 0;
544	bufoverflow = 0;
545	while ( fgets (buf, sizeof buf, infp) != NULL )
546	{
547		p = buf;
548		if ( !bufoverflow && !multi_line_dnskey && (*p == '@' || isspace (*p)) )	/* check if DNSKEY RR */
549		{
550			do
551				p++;
552			while ( isspace (*p) ) ;
553
554			/* skip TTL */
555			while ( isdigit (*p) )
556				p++;
557
558			while ( isspace (*p) )
559				p++;
560
561			/* skip Class */
562			if ( strncasecmp (p, "IN", 2) == 0 )
563			{
564				p += 2;
565				while ( isspace (*p) )
566					p++;
567			}
568
569			if ( strncasecmp (p, "DNSKEY", 6) == 0 )	/* bingo! */
570			{
571				dnskeys++;
572				p += 6;
573				while ( *p )
574				{
575					if ( *p == '(' )
576						multi_line_dnskey = 1;
577					if ( *p == ')' )
578						multi_line_dnskey = 0;
579					p++;
580				}
581				if ( dnskeys == 1 )
582					fprintf (outfp, "$INCLUDE %s\n", dnskeyfile);
583			}
584			else
585				fputs (buf, outfp);
586		}
587		else
588		{
589			if ( bufoverflow )
590				fprintf (stderr, "!! buffer overflow in copyzonefile() !!\n");
591			if ( !multi_line_dnskey )
592				fputs (buf, outfp);
593			else
594			{
595				while ( *p && *p != ')' )
596					p++;
597				if ( *p == ')' )
598					multi_line_dnskey = 0;
599			}
600		}
601
602		len = strlen (buf);
603		bufoverflow = buf[len-1] != '\n';	/* line too long ? */
604	}
605
606	if ( fromfile )
607		fclose (infp);
608	if ( tofile )
609		fclose (outfp);
610
611	return 0;
612}
613
614/*****************************************************************
615**	cmpfile (file1, file2)
616**	returns -1 on error, 1 if the files differ and 0 if they
617**	are identical.
618*****************************************************************/
619int	cmpfile (const char *file1, const char *file2)
620{
621	FILE	*fp1;
622	FILE	*fp2;
623	int	c1;
624	int	c2;
625
626	/* fprintf (stderr, "cmpfile (%s, %s)\n", file1, file2); */
627	if ( (fp1 = fopen (file1, "r")) == NULL )
628		return -1;
629	if ( (fp2 = fopen (file2, "r")) == NULL )
630	{
631		fclose (fp1);
632		return -1;
633	}
634
635	do {
636		c1 = getc (fp1);
637		c2 = getc (fp2);
638	}  while ( c1 != EOF && c2 != EOF && c1 == c2 );
639
640	fclose (fp1);
641	fclose (fp2);
642
643	if ( c1 == c2 )
644		return 0;
645	return 1;
646}
647
648/*****************************************************************
649**	file_age (fname)
650*****************************************************************/
651int	file_age (const char *fname)
652{
653	time_t	curr = time (NULL);
654	time_t	mtime = file_mtime (fname);
655
656	return curr - mtime;
657}
658
659/*****************************************************************
660**	file_mtime (fname)
661*****************************************************************/
662time_t	file_mtime (const char *fname)
663{
664	struct	stat	st;
665
666	if ( stat (fname, &st) < 0 )
667		return 0;
668	return st.st_mtime;
669}
670
671/*****************************************************************
672**	is_exec_ok (prog)
673**	Check if we are running as root or if the file owner of
674**	"prog" do not match the current user or the file permissions
675**	allows file modification for others then the owner.
676**	The same condition will be checked for the group ownership.
677**	return 1 if the execution of the command "prog" will not
678**	open a big security whole, 0 otherwise
679*****************************************************************/
680int	is_exec_ok (const char *prog)
681{
682	uid_t	curr_uid;
683	struct	stat	st;
684
685	if ( stat (prog, &st) < 0 )
686		return 0;
687
688	curr_uid = getuid ();
689	if ( curr_uid == 0 )			/* don't run the cmd if we are root */
690		return 0;
691
692	/* if the file owner and the current user matches and */
693	/* the file mode is not writable except for the owner, we are save */
694	if ( curr_uid == st.st_uid && (st.st_mode & (S_IWGRP | S_IWOTH)) == 0 )
695		return 1;
696
697	/* if the file group and the current group matches and */
698	/* the file mode is not writable except for the group, we are also save */
699	if ( getgid() != st.st_gid && (st.st_mode & (S_IWUSR | S_IWOTH)) == 0 )
700		return 1;
701
702	return 0;
703}
704
705/*****************************************************************
706**	fatal (fmt, ...)
707*****************************************************************/
708void fatal (char *fmt, ...)
709{
710        va_list ap;
711
712        va_start(ap, fmt);
713        if ( progname )
714		fprintf (stderr, "%s: ", progname);
715        vfprintf (stderr, fmt, ap);
716        va_end(ap);
717        exit (127);
718}
719
720/*****************************************************************
721**	error (fmt, ...)
722*****************************************************************/
723void error (char *fmt, ...)
724{
725        va_list ap;
726
727        va_start(ap, fmt);
728        vfprintf (stderr, fmt, ap);
729        va_end(ap);
730}
731
732/*****************************************************************
733**	logmesg (fmt, ...)
734*****************************************************************/
735void logmesg (char *fmt, ...)
736{
737        va_list ap;
738
739#if defined (LOG_WITH_PROGNAME) && LOG_WITH_PROGNAME
740        fprintf (stdout, "%s: ", progname);
741#endif
742        va_start(ap, fmt);
743        vfprintf (stdout, fmt, ap);
744        va_end(ap);
745}
746
747/*****************************************************************
748**	verbmesg (verblvl, conf, fmt, ...)
749*****************************************************************/
750void	verbmesg (int verblvl, const zconf_t *conf, char *fmt, ...)
751{
752	char	str[511+1];
753        va_list ap;
754
755	str[0] = '\0';
756	va_start(ap, fmt);
757	vsnprintf (str, sizeof (str), fmt, ap);
758	va_end(ap);
759
760	//fprintf (stderr, "verbmesg (%d stdout=%d filelog=%d str = :%s:\n", verblvl, conf->verbosity, conf->verboselog, str);
761	if ( verblvl <= conf->verbosity )	/* check if we have to print this to stdout */
762		logmesg (str);
763
764	str_chop (str, '\n');
765	if ( verblvl <= conf->verboselog )	/* check logging to syslog and/or file */
766		lg_mesg (LG_DEBUG, str);
767}
768
769
770/*****************************************************************
771**	logflush ()
772*****************************************************************/
773void logflush ()
774{
775        fflush (stdout);
776}
777
778/*****************************************************************
779**	timestr2time (timestr)
780**	timestr should look like "20071211223901" for 12 dec 2007 22:39:01
781*****************************************************************/
782time_t	timestr2time (const char *timestr)
783{
784	struct	tm	t;
785	time_t	sec;
786
787	// fprintf (stderr, "timestr = \"%s\"\n", timestr);
788	if ( sscanf (timestr, "%4d%2d%2d%2d%2d%2d",
789			&t.tm_year, &t.tm_mon, &t.tm_mday,
790			&t.tm_hour, &t.tm_min, &t.tm_sec) != 6 )
791		return 0L;
792	t.tm_year -= 1900;
793	t.tm_mon -= 1;
794	t.tm_isdst = 0;
795
796#if defined(HAVE_TIMEGM) && HAVE_TIMEGM
797	sec = timegm (&t);
798#else
799	{
800	char	tzstr[31+1];
801	char	*tz;
802
803	tz = getenv("TZ");
804	snprintf (tzstr, sizeof (tzstr), "TZ=%s", "UTC");
805	putenv (tzstr);
806	tzset();
807	sec = mktime(&t);
808	if (tz)
809		snprintf (tzstr, sizeof (tzstr), "TZ=%s", tz);
810	else
811		snprintf (tzstr, sizeof (tzstr), "TZ=%s", "");
812	putenv (tzstr);
813	tzset();
814	}
815#endif
816
817	return sec < 0L ? 0L : sec;
818}
819
820/*****************************************************************
821**	time2str (sec, precison)
822**	sec is seconds since 1.1.1970
823**	precison is currently either 's' (for seconds) or 'm' (minutes)
824*****************************************************************/
825char	*time2str (time_t sec, int precision)
826{
827	struct	tm	*t;
828	static	char	timestr[31+1];	/* 27+1 should be enough */
829#if defined(HAVE_STRFTIME) && HAVE_STRFTIME
830	char	tformat[127+1];
831
832	timestr[0] = '\0';
833	if ( sec <= 0L )
834		return timestr;
835	t = localtime (&sec);
836	if ( precision == 's' )
837		strcpy (tformat, "%b %d %Y %T");
838	else
839		strcpy (tformat, "%b %d %Y %R");
840# if PRINT_TIMEZONE
841	strcat (tformat, " %z");
842# endif
843	strftime (timestr, sizeof (timestr), tformat, t);
844
845#else	/* no strftime available */
846	static	char	*mstr[] = {
847			"Jan", "Feb", "Mar", "Apr", "May", "Jun",
848			"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
849	};
850
851	timestr[0] = '\0';
852	if ( sec <= 0L )
853		return timestr;
854	t = localtime (&sec);
855# if PRINT_TIMEZONE
856	{
857	int	h,	s;
858
859	s = abs (t->tm_gmtoff);
860	h = t->tm_gmtoff / 3600;
861	s = t->tm_gmtoff % 3600;
862	if ( precision == 's' )
863		snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d:%02d %c%02d%02d",
864			mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
865			t->tm_hour, t->tm_min, t->tm_sec,
866			t->tm_gmtoff < 0 ? '-': '+',
867			h, s);
868	else
869		snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d %c%02d%02d",
870			mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
871			t->tm_hour, t->tm_min,
872			t->tm_gmtoff < 0 ? '-': '+',
873			h, s);
874	}
875# else
876	if ( precision == 's' )
877		snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d:%02d",
878			mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
879			t->tm_hour, t->tm_min, t->tm_sec);
880	else
881		snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d",
882			mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
883			t->tm_hour, t->tm_min);
884# endif
885#endif
886
887	return timestr;
888}
889
890/*****************************************************************
891**	time2isostr (sec, precison)
892**	sec is seconds since 1.1.1970
893**	precison is currently either 's' (for seconds) or 'm' (minutes)
894*****************************************************************/
895char	*time2isostr (time_t sec, int precision)
896{
897	struct	tm	*t;
898	static	char	timestr[31+1];	/* 27+1 should be enough */
899
900	timestr[0] = '\0';
901	if ( sec <= 0L )
902		return timestr;
903
904	t = gmtime (&sec);
905	if ( precision == 's' )
906		snprintf (timestr, sizeof (timestr), "%4d%02d%02d%02d%02d%02d",
907			t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
908			t->tm_hour, t->tm_min, t->tm_sec);
909	else
910		snprintf (timestr, sizeof (timestr), "%4d%02d%02d%02d%02d",
911			t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
912			t->tm_hour, t->tm_min);
913
914	return timestr;
915}
916
917/*****************************************************************
918**	age2str (sec)
919**	!!Attention: This function is not reentrant
920*****************************************************************/
921char	*age2str (time_t sec)
922{
923	static	char	str[20+1];	/* "2y51w6d23h50m55s" == 16+1 chars */
924	int	len;
925	int	strsize = sizeof (str);
926
927	len = 0;
928# if PRINT_AGE_WITH_YEAR
929	if ( sec / (YEARSEC) > 0 )
930	{
931		len += snprintf (str+len, strsize - len, "%1luy", sec / YEARSEC );
932		sec %= (YEARSEC);
933	}
934	else
935		len += snprintf (str+len, strsize - len, "  ");
936# endif
937	if ( sec / WEEKSEC > 0 )
938	{
939		len += snprintf (str+len, strsize - len, "%2luw", (ulong) sec / WEEKSEC );
940		sec %= WEEKSEC;
941	}
942	else
943		len += snprintf (str+len, strsize - len, "   ");
944	if ( sec / DAYSEC > 0 )
945	{
946		len += snprintf (str+len, strsize - len, "%2lud", sec / (ulong)DAYSEC);
947		sec %= DAYSEC;
948	}
949	else
950		len += snprintf (str+len, strsize - len, "   ");
951	if ( sec / HOURSEC > 0 )
952	{
953		len += snprintf (str+len, strsize - len, "%2luh", sec / (ulong)HOURSEC);
954		sec %= HOURSEC;
955	}
956	else
957		len += snprintf (str+len, strsize - len, "   ");
958	if ( sec / MINSEC > 0 )
959	{
960		len += snprintf (str+len, strsize - len, "%2lum", sec / (ulong)MINSEC);
961		sec %= MINSEC;
962	}
963	else
964		len += snprintf (str+len, strsize - len, "   ");
965	if ( sec > 0 )
966		snprintf (str+len, strsize - len, "%2lus", (ulong) sec);
967	else
968		len += snprintf (str+len, strsize - len, "   ");
969
970	return str;
971}
972
973/*****************************************************************
974**	start_timer ()
975*****************************************************************/
976time_t	start_timer ()
977{
978	return (time(NULL));
979}
980
981/*****************************************************************
982**	stop_timer ()
983*****************************************************************/
984time_t	stop_timer (time_t start)
985{
986	time_t	stop = time (NULL);
987
988	return stop - start;
989}
990
991
992/****************************************************************
993**
994**	int	gensalt (saltstr, sizeofsaltstr, bits)
995**
996**	generate a random hexstring of 'bits' salt and store it
997**	in saltstr. return 1 on success, otherwise 0.
998**
999*****************************************************************/
1000int	gensalt (char *salt, size_t saltsize, int saltbits, unsigned int seed)
1001{
1002	static	char	hexstr[] = "0123456789ABCDEF";
1003	int	saltlen = 0;	/* current length of salt in hex nibbles */
1004	int	i;
1005	int	hex;
1006
1007	if ( seed == 0 )
1008		srandom (seed = (unsigned int)time (NULL));
1009
1010	saltlen = saltbits / 4;
1011	if ( saltlen+1 > saltsize )
1012		return 0;
1013
1014	for ( i = 0; i < saltlen; i++ )
1015	{
1016		hex = random () % 16;
1017		assert ( hex >= 0 && hex < 16 );
1018		salt[i] = hexstr[hex];
1019	}
1020	salt[i] = '\0';
1021
1022	return 1;
1023}
1024
1025
1026#ifdef COPYZONE_TEST
1027const char *progname;
1028main (int argc, char *argv[])
1029{
1030	progname = *argv;
1031
1032	if ( copyzonefile (argv[1], NULL) < 0 )
1033		error ("can't copy zone file %s\n", argv[1]);
1034}
1035#endif
1036
1037#ifdef URL_TEST
1038const char *progname;
1039main (int argc, char *argv[])
1040{
1041	char	*proto;
1042	char	*host;
1043	char	*port;
1044	char	*para;
1045	char	url[1024];
1046
1047	progname = *argv;
1048
1049	proto = host = port = para = NULL;
1050
1051	if ( --argc <= 0 )
1052	{
1053		fprintf (stderr, "usage: url_test <url>\n");
1054		fprintf (stderr, "e.g.: url_test http://www.hznet.de:80/zkt\n");
1055		exit (1);
1056	}
1057
1058	strcpy (url, argv[1]);
1059	parseurl (url, &proto, &host, &port, &para);
1060
1061	if ( proto )
1062		printf ("proto: \"%s\"\n", proto);
1063	if ( host )
1064		printf ("host: \"%s\"\n", host);
1065	if ( port )
1066		printf ("port: \"%s\"\n", port);
1067	if ( para )
1068		printf ("para: \"%s\"\n", para);
1069
1070}
1071#endif
1072
1073