3 /*****************************************************************
5 ** @(#) misc.c -- helper functions for the dnssec zone key tools
7 ** Copyright (c) Jan 2005, Holger Zuleger HZnet. All rights reserved.
9 ** This software is open source.
11 ** Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions
15 ** Redistributions of source code must retain the above copyright notice,
16 ** this list of conditions and the following disclaimer.
18 ** Redistributions in binary form must reproduce the above copyright notice,
19 ** this list of conditions and the following disclaimer in the documentation
20 ** and/or other materials provided with the distribution.
22 ** Neither the name of Holger Zuleger HZnet nor the names of its contributors may
23 ** be used to endorse or promote products derived from this software without
24 ** specific prior written permission.
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
30 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 ** POSSIBILITY OF SUCH DAMAGE.
38 *****************************************************************/
42 # include <unistd.h> /* for link(), unlink() */
44 # include <sys/types.h>
45 # include <sys/stat.h>
54 # include "config_zkt.h"
62 # define TAINTEDCHARS "`$@;&<>|"
64 extern const char *progname
;
66 /*****************************************************************
67 ** getnameappendix (progname, basename)
68 ** return a pointer to the substring in progname subsequent
69 ** following "<basename>-".
70 *****************************************************************/
71 const char *getnameappendix (const char *progname
, const char *basename
)
76 assert (progname
!= NULL
);
77 assert (basename
!= NULL
);
79 if ( (p
= strrchr (progname
, '/')) != NULL
)
84 baselen
= strlen (basename
);
85 if ( strncmp (p
, basename
, baselen
-1) == 0 && *(p
+baselen
) == '-' )
95 /*****************************************************************
96 ** getdefconfname (view)
97 ** returns a pointer to a dynamic string containing the
98 ** default configuration file name
99 *****************************************************************/
100 const char *getdefconfname (const char *view
)
107 if ( (file
= getenv ("ZKT_CONFFILE")) == NULL
)
109 dbg_val2 ("getdefconfname (%s) file = %s\n", view
? view
: "NULL", file
);
111 if ( view
== NULL
|| *view
== '\0' || (p
= strrchr (file
, '.')) == NULL
)
112 return strdup (file
);
114 size
= strlen (file
) + strlen (view
) + 1 + 1;
115 if ( (buf
= malloc (size
)) == NULL
)
116 return strdup (file
);
118 dbg_val1 ("0123456789o123456789o123456789\tsize=%d\n", size
);
119 dbg_val4 ("%.*s-%s%s\n", p
- file
, file
, view
, p
);
121 snprintf (buf
, size
, "%.*s-%s%s", p
- file
, file
, view
, p
);
125 /*****************************************************************
126 ** domain_canonicdup (s)
127 ** returns NULL or a pointer to a dynamic string containing the
128 ** canonic (all lower case letters and ending with a '.')
130 *****************************************************************/
131 char *domain_canonicdup (const char *s
)
143 if ( len
> 0 && s
[len
-1] != '.' )
146 if ( (new = p
= malloc (len
+ 1)) == NULL
)
150 *p
++ = tolower (*s
++);
157 #if 0 /* replaced by domain_canonicdup */
158 /*****************************************************************
159 ** str_tolowerdup (s)
160 *****************************************************************/
161 char *str_tolowerdup (const char *s
)
166 if ( s
== NULL
|| (new = p
= malloc (strlen (s
) + 1)) == NULL
)
170 *p
++ = tolower (*s
++);
177 /*****************************************************************
179 ** Remove in string 's' all white space char
180 *****************************************************************/
181 char *str_delspace (char *s
)
186 if ( !s
) /* no string present ? */
190 for ( p
= s
; *p
; p
++ )
192 *s
++ = *p
; /* copy each nonspace */
194 *s
= '\0'; /* terminate string */
199 /*****************************************************************
200 ** in_strarr (str, arr, cnt)
201 ** check if string array 'arr' contains the string 'str'
202 ** return 1 if true or 'arr' or 'str' is empty, otherwise 0
203 *****************************************************************/
204 int in_strarr (const char *str
, char *const arr
[], int cnt
)
206 if ( arr
== NULL
|| cnt
<= 0 )
209 if ( str
== NULL
|| *str
== '\0' )
213 if ( strcmp (str
, arr
[cnt
]) == 0 )
219 /*****************************************************************
221 ** Remove in string 's' all TAINTED chars
222 *****************************************************************/
223 char *str_untaint (char *str
)
227 assert (str
!= NULL
);
229 for ( p
= str
; *p
; p
++ )
230 if ( strchr (TAINTEDCHARS
, *p
) )
235 /*****************************************************************
237 ** delete all occurrences of char 'c' at the end of string 's'
238 *****************************************************************/
239 char *str_chop (char *str
, char c
)
243 assert (str
!= NULL
);
245 len
= strlen (str
) - 1;
246 while ( len
>= 0 && str
[len
] == c
)
252 /*****************************************************************
253 ** parseurl (url, &proto, &host, &port, ¶ )
254 ** parses the given url (e.g. "proto://host.with.domain:port/para")
255 ** and set the pointer variables to the corresponding part of the string.
256 *****************************************************************/
257 void parseurl (char *url
, char **proto
, char **host
, char **port
, char **para
)
262 assert ( url
!= NULL
);
265 if ( (p
= strchr (url
, ':')) == NULL
) /* no protocol string given ? */
267 else /* looks like a protocol string */
268 if ( p
[1] == '/' && p
[2] == '/' ) /* protocol string ? */
275 else /* no protocol string found ! */
279 if ( *p
== '[' ) /* ipv6 address as hostname ? */
281 for ( start
= ++p
; *p
&& *p
!= ']'; p
++ )
287 for ( start
= p
; *p
&& *p
!= ':' && *p
!= '/'; p
++ )
296 for ( start
= p
; *p
&& isdigit (*p
); p
++ )
311 /*****************************************************************
312 ** splitpath (path, pathsize, filename)
313 ** if filename is build of "path/file" then copy filename to path
314 ** and split of the filename part.
315 ** return pointer to filename part in path or NULL if path is too
316 ** small to hold "path+filename"
317 *****************************************************************/
318 const char *splitpath (char *path
, size_t psize
, const char *filename
)
329 if ( (p
= strrchr (filename
, '/')) ) /* file arg contains path ? */
331 if ( strlen (filename
) + 1 > psize
)
334 strcpy (path
, filename
); /* copy whole filename to path */
335 path
[p
-filename
] = '\0'; /* split of the file part */
341 /*****************************************************************
342 ** pathname (path, size, dir, file, ext)
343 ** Concatenate 'dir', 'file' and 'ext' (if not null) to build
344 ** a pathname, and store the result in the character array
345 ** with length 'size' pointed to by 'path'.
346 *****************************************************************/
347 char *pathname (char *path
, size_t size
, const char *dir
, const char *file
, const char *ext
)
351 if ( path
== NULL
|| file
== NULL
)
354 len
= strlen (file
) + 1;
365 len
= sprintf (path
, "%s", dir
);
366 if ( path
[len
-1] != '/' )
378 /*****************************************************************
379 ** is_directory (name)
380 ** Check if the given pathname 'name' exists and is a directory.
382 *****************************************************************/
383 int is_directory (const char *name
)
387 if ( !name
|| !*name
)
390 return ( stat (name
, &st
) == 0 && S_ISDIR (st
.st_mode
) );
393 /*****************************************************************
395 ** Check if a file with the given pathname 'name' exists.
397 *****************************************************************/
398 int fileexist (const char *name
)
401 return ( stat (name
, &st
) == 0 && S_ISREG (st
.st_mode
) );
404 /*****************************************************************
406 ** return the size of the file with the given pathname 'name'.
407 ** returns -1 if the file not exist
408 *****************************************************************/
409 size_t filesize (const char *name
)
412 if ( stat (name
, &st
) == -1 )
414 return ( st
.st_size
);
417 /*****************************************************************
418 ** is_keyfilename (name)
419 ** Check if the given name looks like a dnssec (public)
420 ** keyfile name. Returns 0 | 1
421 *****************************************************************/
422 int is_keyfilename (const char *name
)
426 if ( name
== NULL
|| *name
!= 'K' )
430 if ( len
> 4 && strcmp (&name
[len
- 4], ".key") == 0 )
436 /*****************************************************************
437 ** is_dotfilename (name)
438 ** Check if the given pathname 'name' looks like "." or "..".
440 *****************************************************************/
441 int is_dotfilename (const char *name
)
444 (name
[0] == '.' && name
[1] == '\0') ||
445 (name
[0] == '.' && name
[1] == '.' && name
[2] == '\0')) )
451 /*****************************************************************
453 ** Set the modification time of the given pathname 'fname' to
454 ** 'sec'. Returns 0 on success.
455 *****************************************************************/
456 int touch (const char *fname
, time_t sec
)
460 utb
.actime
= utb
.modtime
= sec
;
461 return utime (fname
, &utb
);
464 /*****************************************************************
465 ** linkfile (fromfile, tofile)
466 *****************************************************************/
467 int linkfile (const char *fromfile
, const char *tofile
)
471 /* fprintf (stderr, "linkfile (%s, %s)\n", fromfile, tofile); */
472 if ( (ret
= link (fromfile
, tofile
)) == -1 && errno
== EEXIST
)
473 if ( unlink (tofile
) == 0 )
474 ret
= link (fromfile
, tofile
);
479 /*****************************************************************
480 ** copyfile (fromfile, tofile, dnskeyfile)
481 *****************************************************************/
482 int copyfile (const char *fromfile
, const char *tofile
, const char *dnskeyfile
)
488 /* fprintf (stderr, "copyfile (%s, %s)\n", fromfile, tofile); */
489 if ( (infp
= fopen (fromfile
, "r")) == NULL
)
491 if ( (outfp
= fopen (tofile
, "w")) == NULL
)
496 while ( (c
= getc (infp
)) != EOF
)
500 if ( dnskeyfile
&& *dnskeyfile
&& (infp
= fopen (dnskeyfile
, "r")) != NULL
)
502 while ( (c
= getc (infp
)) != EOF
)
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 *****************************************************************/
516 int copyzonefile (const char *fromfile
, const char *tofile
, const char *dnskeyfile
)
522 int multi_line_dnskey
;
527 if ( fromfile
== NULL
)
530 if ( (infp
= fopen (fromfile
, "r")) == NULL
)
532 if ( tofile
== NULL
)
535 if ( (outfp
= fopen (tofile
, "w")) == NULL
)
542 multi_line_dnskey
= 0;
545 while ( fgets (buf
, sizeof buf
, infp
) != NULL
)
548 if ( !bufoverflow
&& !multi_line_dnskey
&& (*p
== '@' || isspace (*p
)) ) /* check if DNSKEY RR */
552 while ( isspace (*p
) ) ;
555 while ( isdigit (*p
) )
558 while ( isspace (*p
) )
562 if ( strncasecmp (p
, "IN", 2) == 0 )
565 while ( isspace (*p
) )
569 if ( strncasecmp (p
, "DNSKEY", 6) == 0 ) /* bingo! */
576 multi_line_dnskey
= 1;
578 multi_line_dnskey
= 0;
582 fprintf (outfp
, "$INCLUDE %s\n", dnskeyfile
);
590 fprintf (stderr
, "!! buffer overflow in copyzonefile() !!\n");
591 if ( !multi_line_dnskey
)
595 while ( *p
&& *p
!= ')' )
598 multi_line_dnskey
= 0;
603 bufoverflow
= buf
[len
-1] != '\n'; /* line too long ? */
614 /*****************************************************************
615 ** cmpfile (file1, file2)
616 ** returns -1 on error, 1 if the files differ and 0 if they
618 *****************************************************************/
619 int cmpfile (const char *file1
, const char *file2
)
626 /* fprintf (stderr, "cmpfile (%s, %s)\n", file1, file2); */
627 if ( (fp1
= fopen (file1
, "r")) == NULL
)
629 if ( (fp2
= fopen (file2
, "r")) == NULL
)
638 } while ( c1
!= EOF
&& c2
!= EOF
&& c1
== c2
);
648 /*****************************************************************
650 *****************************************************************/
651 int file_age (const char *fname
)
653 time_t curr
= time (NULL
);
654 time_t mtime
= file_mtime (fname
);
659 /*****************************************************************
660 ** file_mtime (fname)
661 *****************************************************************/
662 time_t file_mtime (const char *fname
)
666 if ( stat (fname
, &st
) < 0 )
671 /*****************************************************************
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 *****************************************************************/
680 int is_exec_ok (const char *prog
)
685 if ( stat (prog
, &st
) < 0 )
688 curr_uid
= getuid ();
689 if ( curr_uid
== 0 ) /* don't run the cmd if we are root */
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 )
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 )
705 /*****************************************************************
707 *****************************************************************/
708 void fatal (char *fmt
, ...)
714 fprintf (stderr
, "%s: ", progname
);
715 vfprintf (stderr
, fmt
, ap
);
720 /*****************************************************************
722 *****************************************************************/
723 void error (char *fmt
, ...)
728 vfprintf (stderr
, fmt
, ap
);
732 /*****************************************************************
733 ** logmesg (fmt, ...)
734 *****************************************************************/
735 void logmesg (char *fmt
, ...)
739 #if defined (LOG_WITH_PROGNAME) && LOG_WITH_PROGNAME
740 fprintf (stdout
, "%s: ", progname
);
743 vfprintf (stdout
, fmt
, ap
);
747 /*****************************************************************
748 ** verbmesg (verblvl, conf, fmt, ...)
749 *****************************************************************/
750 void verbmesg (int verblvl
, const zconf_t
*conf
, char *fmt
, ...)
757 vsnprintf (str
, sizeof (str
), fmt
, ap
);
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 */
764 str_chop (str
, '\n');
765 if ( verblvl
<= conf
->verboselog
) /* check logging to syslog and/or file */
766 lg_mesg (LG_DEBUG
, str
);
770 /*****************************************************************
772 *****************************************************************/
778 /*****************************************************************
779 ** timestr2time (timestr)
780 ** timestr should look like "20071211223901" for 12 dec 2007 22:39:01
781 *****************************************************************/
782 time_t timestr2time (const char *timestr
)
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 )
796 #if defined(HAVE_TIMEGM) && HAVE_TIMEGM
804 snprintf (tzstr
, sizeof (tzstr
), "TZ=%s", "UTC");
809 snprintf (tzstr
, sizeof (tzstr
), "TZ=%s", tz
);
811 snprintf (tzstr
, sizeof (tzstr
), "TZ=%s", "");
817 return sec
< 0L ? 0L : sec
;
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 *****************************************************************/
825 char *time2str (time_t sec
, int precision
)
828 static char timestr
[31+1]; /* 27+1 should be enough */
829 #if defined(HAVE_STRFTIME) && HAVE_STRFTIME
835 t
= localtime (&sec
);
836 if ( precision
== 's' )
837 strcpy (tformat
, "%b %d %Y %T");
839 strcpy (tformat
, "%b %d %Y %R");
841 strcat (tformat
, " %z");
843 strftime (timestr
, sizeof (timestr
), tformat
, t
);
845 #else /* no strftime available */
846 static char *mstr
[] = {
847 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
848 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
854 t
= localtime (&sec
);
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 ? '-': '+',
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 ? '-': '+',
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
);
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
);
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 *****************************************************************/
895 char *time2isostr (time_t sec
, int precision
)
898 static char timestr
[31+1]; /* 27+1 should be enough */
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
);
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
);
917 /*****************************************************************
919 ** !!Attention: This function is not reentrant
920 *****************************************************************/
921 char *age2str (time_t sec
)
923 static char str
[20+1]; /* "2y51w6d23h50m55s" == 16+1 chars */
925 int strsize
= sizeof (str
);
928 # if PRINT_AGE_WITH_YEAR
929 if ( sec
/ (YEARSEC
) > 0 )
931 len
+= snprintf (str
+len
, strsize
- len
, "%1luy", sec
/ YEARSEC
);
935 len
+= snprintf (str
+len
, strsize
- len
, " ");
937 if ( sec
/ WEEKSEC
> 0 )
939 len
+= snprintf (str
+len
, strsize
- len
, "%2luw", (ulong
) sec
/ WEEKSEC
);
943 len
+= snprintf (str
+len
, strsize
- len
, " ");
944 if ( sec
/ DAYSEC
> 0 )
946 len
+= snprintf (str
+len
, strsize
- len
, "%2lud", sec
/ (ulong
)DAYSEC
);
950 len
+= snprintf (str
+len
, strsize
- len
, " ");
951 if ( sec
/ HOURSEC
> 0 )
953 len
+= snprintf (str
+len
, strsize
- len
, "%2luh", sec
/ (ulong
)HOURSEC
);
957 len
+= snprintf (str
+len
, strsize
- len
, " ");
958 if ( sec
/ MINSEC
> 0 )
960 len
+= snprintf (str
+len
, strsize
- len
, "%2lum", sec
/ (ulong
)MINSEC
);
964 len
+= snprintf (str
+len
, strsize
- len
, " ");
966 snprintf (str
+len
, strsize
- len
, "%2lus", (ulong
) sec
);
968 len
+= snprintf (str
+len
, strsize
- len
, " ");
973 /*****************************************************************
975 *****************************************************************/
976 time_t start_timer ()
981 /*****************************************************************
983 *****************************************************************/
984 time_t stop_timer (time_t start
)
986 time_t stop
= time (NULL
);
992 /****************************************************************
994 ** int gensalt (saltstr, sizeofstalstr, bits)
996 ** generate a random hexstring of 'bits' salt and store it
997 ** in saltstr. return 1 on success, otherwise 0.
999 *****************************************************************/
1000 int gensalt (char *salt
, size_t saltsize
, int saltbits
)
1002 static char hexstr
[] = "0123456789ABCDEF";
1003 static int seed
= 0;
1004 int saltlen
= 0; /* current length of salt in hex nibbles */
1009 srandom (seed
= (unsigned int)time (NULL
));
1011 saltlen
= saltbits
/ 4;
1012 if ( saltlen
+1 > saltsize
)
1015 for ( i
= 0; i
< saltlen
; i
++ )
1017 hex
= random () % 16;
1018 assert ( hex
>= 0 && hex
< 16 );
1019 salt
[i
] = hexstr
[hex
];
1027 #ifdef COPYZONE_TEST
1028 const char *progname
;
1029 main (int argc
, char *argv
[])
1033 if ( copyzonefile (argv
[1], NULL
) < 0 )
1034 error ("can't copy zone file %s\n", argv
[1]);
1039 const char *progname
;
1040 main (int argc
, char *argv
[])
1050 proto
= host
= port
= para
= NULL
;
1054 fprintf (stderr
, "usage: url_test <url>\n");
1055 fprintf (stderr
, "e.g.: url_test http://www.hznet.de:80/zkt\n");
1059 strcpy (url
, argv
[1]);
1060 parseurl (url
, &proto
, &host
, &port
, ¶
);
1063 printf ("proto: \"%s\"\n", proto
);
1065 printf ("host: \"%s\"\n", host
);
1067 printf ("port: \"%s\"\n", port
);
1069 printf ("para: \"%s\"\n", para
);