# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# include <ctype.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <time.h>
# include <utime.h>
# include <assert.h>
# include <errno.h>
# include <fcntl.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
# include "config_zkt.h"
# include "zconf.h"
# include "log.h"
# include "debug.h"
#define extern
# include "misc.h"
#undef extern
# define TAINTEDCHARS "`$@;&<>|"
extern const char *progname;
static int inc_soa_serial (FILE *fp, int use_unixtime);
const char *getnameappendix (const char *progname, const char *basename)
{
const char *p;
int baselen;
assert (progname != NULL);
assert (basename != NULL);
if ( (p = strrchr (progname, '/')) != NULL )
p++;
else
p = progname;
baselen = strlen (basename);
if ( strncmp (p, basename, baselen-1) == 0 && *(p+baselen) == '-' )
{
p += baselen + 1;
if ( *p )
return p;
}
return NULL;
}
const char *getdefconfname (const char *view)
{
char *p;
char *file;
char *buf;
int size;
if ( (file = getenv ("ZKT_CONFFILE")) == NULL )
file = CONFIG_FILE;
if ( view == NULL || *view == '\0' || (p = strrchr (file, '.')) == NULL )
return strdup (file);
size = strlen (file) + strlen (view) + 1 + 1;
if ( (buf = malloc (size)) == NULL )
return file;
dbg_val1 ("0123456789o123456789o123456789\tsize=%d\n", size);
dbg_val4 ("%.*s-%s%s\n", p - file, file, view, p);
snprintf (buf, size, "%.*s-%s%s", p - file, file, view, p);
return buf;
}
char *str_tolowerdup (const char *s)
{
char *new;
char *p;
if ( s == NULL || (new = p = malloc (strlen (s) + 1)) == NULL )
return NULL;
while ( *s )
*p++ = tolower (*s++);
*p = '\0';
return new;
}
char *str_delspace (char *s)
{
char *start;
char *p;
if ( !s )
return s;
start = s;
for ( p = s; *p; p++ )
if ( !isspace (*p) )
*s++ = *p;
*s = '\0';
return start;
}
int in_strarr (const char *str, char *const arr[], int cnt)
{
if ( arr == NULL || cnt <= 0 )
return 1;
if ( str == NULL || *str == '\0' )
return 0;
while ( --cnt >= 0 )
if ( strcmp (str, arr[cnt]) == 0 )
return 1;
return 0;
}
char *str_untaint (char *str)
{
char *p;
assert (str != NULL);
for ( p = str; *p; p++ )
if ( strchr (TAINTEDCHARS, *p) )
*p = ' ';
return str;
}
char *str_chop (char *str, char c)
{
int len;
assert (str != NULL);
len = strlen (str) - 1;
while ( len >= 0 && str[len] == c )
str[len--] = '\0';
return str;
}
void parseurl (char *url, char **proto, char **host, char **port, char **para)
{
char *start;
char *p;
assert ( url != NULL );
if ( (p = strchr (url, ':')) == NULL )
p = url;
else
if ( p[1] == '/' && p[2] == '/' )
{
*p = '\0';
p += 3;
if ( proto )
*proto = url;
}
else
p = url;
if ( *p == '[' )
{
for ( start = ++p; *p && *p != ']'; p++ )
;
if ( *p )
*p++ = '\0';
}
else
for ( start = p; *p && *p != ':' && *p != '/'; p++ )
;
if ( host )
*host = start;
if ( *p == ':' )
{
*p++ = '\0';
for ( start = p; *p && isdigit (*p); p++ )
;
if ( *p )
*p++ = '\0';
if ( port )
*port = start;
}
if ( *p == '/' )
*p++ = '\0';
if ( *p && para )
*para = p;
}
const char *splitpath (char *path, size_t size, const char *filename)
{
char *p;
if ( !path )
return filename;
*path = '\0';
if ( !filename )
return filename;
if ( (p = strrchr (filename, '/')) )
{
if ( strlen (filename) > size )
return filename;
strcpy (path, filename);
path[p-filename] = '\0';
filename = ++p;
}
return filename;
}
char *pathname (char *path, size_t size, const char *dir, const char *file, const char *ext)
{
int len;
if ( path == NULL || file == NULL )
return path;
len = strlen (file) + 1;
if ( dir )
len += strlen (dir);
if ( ext )
len += strlen (ext);
if ( len > size )
return path;
*path = '\0';
if ( dir && *dir )
{
len = sprintf (path, "%s", dir);
if ( path[len-1] != '/' )
{
path[len++] = '/';
path[len] = '\0';
}
}
strcat (path, file);
if ( ext )
strcat (path, ext);
return path;
}
int is_directory (const char *name)
{
struct stat st;
if ( !name || !*name )
return 0;
return ( stat (name, &st) == 0 && S_ISDIR (st.st_mode) );
}
int fileexist (const char *name)
{
struct stat st;
return ( stat (name, &st) == 0 && S_ISREG (st.st_mode) );
}
size_t filesize (const char *name)
{
struct stat st;
if ( stat (name, &st) == -1 )
return -1L;
return ( st.st_size );
}
int is_keyfilename (const char *name)
{
int len;
if ( name == NULL || *name != 'K' )
return 0;
len = strlen (name);
if ( len > 4 && strcmp (&name[len - 4], ".key") == 0 )
return 1;
return 0;
}
int is_dotfile (const char *name)
{
if ( name && (
(name[0] == '.' && name[1] == '\0') ||
(name[0] == '.' && name[1] == '.' && name[2] == '\0')) )
return 1;
return 0;
}
int touch (const char *fname, time_t sec)
{
struct utimbuf utb;
utb.actime = utb.modtime = sec;
return utime (fname, &utb);
}
int linkfile (const char *fromfile, const char *tofile)
{
int ret;
if ( (ret = link (fromfile, tofile)) == -1 && errno == EEXIST )
if ( unlink (tofile) == 0 )
ret = link (fromfile, tofile);
return ret;
}
int copyfile (const char *fromfile, const char *tofile, const char *dnskeyfile)
{
FILE *infp;
FILE *outfp;
int c;
if ( (infp = fopen (fromfile, "r")) == NULL )
return -1;
if ( (outfp = fopen (tofile, "w")) == NULL )
{
fclose (infp);
return -2;
}
while ( (c = getc (infp)) != EOF )
putc (c, outfp);
fclose (infp);
if ( dnskeyfile && *dnskeyfile && (infp = fopen (dnskeyfile, "r")) != NULL )
{
while ( (c = getc (infp)) != EOF )
putc (c, outfp);
fclose (infp);
}
fclose (outfp);
return 0;
}
int copyzonefile (const char *fromfile, const char *tofile, const char *dnskeyfile)
{
FILE *infp;
FILE *outfp;
int len;
int dnskeys;
int multi_line_dnskey;
int bufoverflow;
char buf[1024];
char *p;
if ( fromfile == NULL )
infp = stdin;
else
if ( (infp = fopen (fromfile, "r")) == NULL )
return -1;
if ( tofile == NULL )
outfp = stdout;
else
if ( (outfp = fopen (tofile, "w")) == NULL )
{
if ( fromfile )
fclose (infp);
return -2;
}
multi_line_dnskey = 0;
dnskeys = 0;
bufoverflow = 0;
while ( fgets (buf, sizeof buf, infp) != NULL )
{
p = buf;
if ( !bufoverflow && !multi_line_dnskey && (*p == '@' || isspace (*p)) )
{
do
p++;
while ( isspace (*p) ) ;
while ( isdigit (*p) )
p++;
while ( isspace (*p) )
p++;
if ( strncasecmp (p, "IN", 2) == 0 )
{
p += 2;
while ( isspace (*p) )
p++;
}
if ( strncasecmp (p, "DNSKEY", 6) == 0 )
{
dnskeys++;
p += 6;
while ( *p )
{
if ( *p == '(' )
multi_line_dnskey = 1;
if ( *p == ')' )
multi_line_dnskey = 0;
p++;
}
if ( dnskeys == 1 )
fprintf (outfp, "$INCLUDE %s\n", dnskeyfile);
}
else
fputs (buf, outfp);
}
else
{
if ( bufoverflow )
fprintf (stderr, "!! buffer overflow in copyzonefile() !!\n");
if ( !multi_line_dnskey )
fputs (buf, outfp);
else
{
while ( *p && *p != ')' )
p++;
if ( *p == ')' )
multi_line_dnskey = 0;
}
}
len = strlen (buf);
bufoverflow = buf[len-1] != '\n';
}
if ( fromfile )
fclose (infp);
if ( tofile )
fclose (outfp);
return 0;
}
int cmpfile (const char *file1, const char *file2)
{
FILE *fp1;
FILE *fp2;
int c1;
int c2;
if ( (fp1 = fopen (file1, "r")) == NULL )
return -1;
if ( (fp2 = fopen (file2, "r")) == NULL )
{
fclose (fp1);
return -1;
}
do {
c1 = getc (fp1);
c2 = getc (fp2);
} while ( c1 != EOF && c2 != EOF && c1 == c2 );
fclose (fp1);
fclose (fp2);
if ( c1 == c2 )
return 0;
return 1;
}
int file_age (const char *fname)
{
time_t curr = time (NULL);
time_t mtime = file_mtime (fname);
return curr - mtime;
}
time_t file_mtime (const char *fname)
{
struct stat st;
if ( stat (fname, &st) < 0 )
return 0;
return st.st_mtime;
}
int is_exec_ok (const char *prog)
{
uid_t curr_uid;
struct stat st;
if ( stat (prog, &st) < 0 )
return 0;
curr_uid = getuid ();
if ( curr_uid == 0 )
return 0;
if ( curr_uid == st.st_uid && (st.st_mode & (S_IWGRP | S_IWOTH)) == 0 )
return 1;
if ( getgid() != st.st_gid && (st.st_mode & (S_IWUSR | S_IWOTH)) == 0 )
return 1;
return 0;
}
void fatal (char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if ( progname )
fprintf (stderr, "%s: ", progname);
vfprintf (stderr, fmt, ap);
va_end(ap);
exit (127);
}
void error (char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf (stderr, fmt, ap);
va_end(ap);
}
void logmesg (char *fmt, ...)
{
va_list ap;
#if defined (LOG_WITH_PROGNAME) && LOG_WITH_PROGNAME
fprintf (stdout, "%s: ", progname);
#endif
va_start(ap, fmt);
vfprintf (stdout, fmt, ap);
va_end(ap);
}
void verbmesg (int verblvl, const zconf_t *conf, char *fmt, ...)
{
char str[511+1];
va_list ap;
str[0] = '\0';
va_start(ap, fmt);
vsnprintf (str, sizeof (str), fmt, ap);
va_end(ap);
if ( verblvl <= conf->verbosity )
logmesg (str);
str_chop (str, '\n');
if ( verblvl <= conf->verboselog )
lg_mesg (LG_DEBUG, str);
}
void logflush ()
{
fflush (stdout);
}
time_t timestr2time (const char *timestr)
{
struct tm t;
time_t sec;
if ( sscanf (timestr, "%4d%2d%2d%2d%2d%2d",
&t.tm_year, &t.tm_mon, &t.tm_mday,
&t.tm_hour, &t.tm_min, &t.tm_sec) != 6 )
return 0L;
t.tm_year -= 1900;
t.tm_mon -= 1;
t.tm_isdst = 0;
#if defined(HAS_TIMEGM) && HAS_TIMEGM
sec = timegm (&t);
#else
{
time_t ret;
char *tz;
tz = getenv("TZ");
setenv("TZ", "UTC", 1);
tzset();
sec = mktime(&t);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
}
#endif
return sec < 0L ? 0L : sec;
}
char *time2str (time_t sec, int precision)
{
struct tm *t;
static char timestr[31+1];
#if defined(HAVE_STRFTIME) && HAVE_STRFTIME
char tformat[127+1];
timestr[0] = '\0';
if ( sec <= 0L )
return timestr;
t = localtime (&sec);
if ( precision == 's' )
strcpy (tformat, "%b %d %Y %T");
else
strcpy (tformat, "%b %d %Y %R");
# if PRINT_TIMEZONE
strcat (tformat, " %z");
# endif
strftime (timestr, sizeof (timestr), tformat, t);
#else
static char *mstr[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
timestr[0] = '\0';
if ( sec <= 0L )
return timestr;
t = localtime (&sec);
# if PRINT_TIMEZONE
{
int h, s;
s = abs (t->tm_gmtoff);
h = t->tm_gmtoff / 3600;
s = t->tm_gmtoff % 3600;
if ( precision == 's' )
snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d:%02d %c%02d%02d",
mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
t->tm_hour, t->tm_min, t->tm_sec,
t->tm_gmtoff < 0 ? '-': '+',
h, s);
else
snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d %c%02d%02d",
mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
t->tm_hour, t->tm_min,
t->tm_gmtoff < 0 ? '-': '+',
h, s);
}
# else
if ( precision == 's' )
snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d:%02d",
mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
t->tm_hour, t->tm_min, t->tm_sec);
else
snprintf (timestr, sizeof (timestr), "%s %2d %4d %02d:%02d",
mstr[t->tm_mon], t->tm_mday, t->tm_year + 1900,
t->tm_hour, t->tm_min);
# endif
#endif
return timestr;
}
char *time2isostr (time_t sec, int precision)
{
struct tm *t;
static char timestr[31+1];
timestr[0] = '\0';
if ( sec <= 0L )
return timestr;
t = gmtime (&sec);
if ( precision == 's' )
snprintf (timestr, sizeof (timestr), "%4d%02d%02d%02d%02d%02d",
t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
else
snprintf (timestr, sizeof (timestr), "%4d%02d%02d%02d%02d",
t->tm_year + 1900, t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min);
return timestr;
}
char *age2str (time_t sec)
{
static char str[20+1];
int len;
int strsize = sizeof (str);
len = 0;
# if PRINT_AGE_WITH_YEAR
if ( sec / (YEARSEC) > 0 )
{
len += snprintf (str+len, strsize - len, "%1luy", sec / YEARSEC );
sec %= (YEARSEC);
}
else
len += snprintf (str+len, strsize - len, " ");
# endif
if ( sec / WEEKSEC > 0 )
{
len += snprintf (str+len, strsize - len, "%2luw", (ulong) sec / WEEKSEC );
sec %= WEEKSEC;
}
else
len += snprintf (str+len, strsize - len, " ");
if ( sec / DAYSEC > 0 )
{
len += snprintf (str+len, strsize - len, "%2lud", sec / (ulong)DAYSEC);
sec %= DAYSEC;
}
else
len += snprintf (str+len, strsize - len, " ");
if ( sec / HOURSEC > 0 )
{
len += snprintf (str+len, strsize - len, "%2luh", sec / (ulong)HOURSEC);
sec %= HOURSEC;
}
else
len += snprintf (str+len, strsize - len, " ");
if ( sec / MINSEC > 0 )
{
len += snprintf (str+len, strsize - len, "%2lum", sec / (ulong)MINSEC);
sec %= MINSEC;
}
else
len += snprintf (str+len, strsize - len, " ");
if ( sec > 0 )
snprintf (str+len, strsize - len, "%2lus", (ulong) sec);
else
len += snprintf (str+len, strsize - len, " ");
return str;
}
time_t start_timer ()
{
return (time(NULL));
}
time_t stop_timer (time_t start)
{
time_t stop = time (NULL);
return stop - start;
}
int inc_serial (const char *fname, int use_unixtime)
{
FILE *fp;
char buf[4095+1];
char master[254+1];
int error;
#if defined(BIND_VERSION) && BIND_VERSION >= 940
if ( use_unixtime )
return 0;
#endif
if ( (fp = fopen (fname, "r+")) == NULL )
return -1;
while ( fgets (buf, sizeof buf, fp) &&
sscanf (buf, "@ IN SOA %255s %*s (\n", master) != 1 )
;
if ( feof (fp) )
{
fclose (fp);
return -2;
}
error = inc_soa_serial (fp, use_unixtime);
if ( fclose (fp) != 0 )
return -5;
return error;
}
static ulong today_serialtime ()
{
struct tm *t;
ulong serialtime;
time_t now;
now = time (NULL);
t = gmtime (&now);
serialtime = (t->tm_year + 1900) * 10000;
serialtime += (t->tm_mon+1) * 100;
serialtime += t->tm_mday;
serialtime *= 100;
return serialtime;
}
static int inc_soa_serial (FILE *fp, int use_unixtime)
{
int c;
long pos, eos;
ulong serial;
int digits;
ulong today;
while ( (c = getc (fp)) != EOF && isspace (c) )
;
ungetc (c, fp);
pos = ftell (fp);
serial = 0L;
if ( fscanf (fp, "%lu ", &serial) != 1 )
return -3;
eos = ftell (fp);
digits = eos - pos;
if ( digits < 10 )
return -4;
if ( use_unixtime )
today = time (NULL);
else
{
today = today_serialtime ();
if ( serial > 1970010100L && serial < today )
serial = today;
serial++;
}
fseek (fp, pos, SEEK_SET);
fprintf (fp, "%-*lu", digits, serial);
return 1;
}
const char *inc_errstr (int err)
{
switch ( err )
{
case -1: return "couldn't open zone file for modifying";
case -2: return "unexpected end of file";
case -3: return "no serial number found in zone file";
case -4: return "not enough space left for serialno";
case -5: return "error on closing zone file";
}
return "";
}
#ifdef SOA_TEST
const char *progname;
main (int argc, char *argv[])
{
ulong now;
int err;
char cmd[255];
progname = *argv;
now = today_serialtime ();
printf ("now = %lu\n", now);
if ( (err = inc_serial (argv[1]), 0) < 0 )
error ("can't change serial errno=%d\n", err);
snprintf (cmd, sizeof(cmd), "head -15 %s", argv[1]);
system (cmd);
}
#endif
#ifdef COPYZONE_TEST
const char *progname;
main (int argc, char *argv[])
{
progname = *argv;
if ( copyzonefile (argv[1], NULL) < 0 )
error ("can't copy zone file %s\n", argv[1]);
}
#endif
#ifdef URL_TEST
const char *progname;
main (int argc, char *argv[])
{
char *proto;
char *host;
char *port;
char *para;
char url[1024];
progname = *argv;
proto = host = port = para = NULL;
if ( --argc <= 0 )
{
fprintf (stderr, "usage: url_test <url>\n");
fprintf (stderr, "e.g.: url_test http://www.hznet.de:80/zkt\n");
exit (1);
}
strcpy (url, argv[1]);
parseurl (url, &proto, &host, &port, ¶);
if ( proto )
printf ("proto: \"%s\"\n", proto);
if ( host )
printf ("host: \"%s\"\n", host);
if ( port )
printf ("port: \"%s\"\n", port);
if ( para )
printf ("para: \"%s\"\n", para);
}
#endif