text: /*
   *  Copyright (c) 2009-2020, Peter Haag
   *  Copyright (c) 2004-2008, SWITCH - Teleinformatikdienste fuer Lehre und Forschung
   *  All rights reserved.
   *  
   *  Redistribution and use in source and binary forms, with or without 
   *  modification, are permitted provided that the following conditions are met:
   *  
   *   * Redistributions of source code must retain the above copyright notice, 
   *     this list of conditions and the following disclaimer.
   *   * Redistributions in binary form must reproduce the above copyright notice, 
   *     this list of conditions and the following disclaimer in the documentation 
   *     and/or other materials provided with the distribution.
   *   * Neither the name of the author nor the names of its contributors may be 
   *     used to endorse or promote products derived from this software without 
   *     specific prior written permission.
   *  
   *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
   *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
   *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
   *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
   *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
   *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
   *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
   *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
   *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
   *  POSSIBILITY OF SUCH DAMAGE.
   *  
   */
  
  #ifdef HAVE_CONFIG_H 
  #include "config.h"
  #endif
  
  #include <stdio.h>
  #include <unistd.h>
  #include <stdlib.h>
  #include <time.h>
  #include <string.h>
  #include <errno.h>
  #include <sys/types.h>
  #include <sys/select.h>
  #include <sys/socket.h>
  #include <arpa/inet.h>
  
  #ifndef SYSLOG_NAMES
  #	define SYSLOG_NAMES 1
  #endif
  
  #include <syslog.h>
  #include <stdarg.h>
  
  #ifdef HAVE_STDINT_H
  #include <stdint.h>
  #endif
  
  #include "util.h"
  
  /* Global vars */
  
  static int verbose = 0;
  
  /* Function prototypes */
  static int check_number(char *s, int len);
  
  static int ParseTime(char *s, time_t *t_start);
  
  uint32_t twin_first, twin_last;
  
  static int use_syslog = 0;
  
  #ifdef sun
  struct _code {
      char    *c_name;
      int c_val;
  } facilitynames[] = {
      { "auth", LOG_AUTH },
      { "cron", LOG_CRON },
      { "daemon", LOG_DAEMON },
      { "kern", LOG_KERN },
      { "lpr", LOG_LPR },
      { "mail", LOG_MAIL },
      { "news", LOG_NEWS },
      { "security", LOG_AUTH },       /* DEPRECATED */
      { "syslog", LOG_SYSLOG },
      { "user", LOG_USER },
      { "uucp", LOG_UUCP },
      { "local0", LOG_LOCAL0 },
      { "local1", LOG_LOCAL1 },
      { "local2", LOG_LOCAL2 },
      { "local3", LOG_LOCAL3 },
      { "local4", LOG_LOCAL4 },
      { "local5", LOG_LOCAL5 },
      { "local6", LOG_LOCAL6 },
      { "local7", LOG_LOCAL7 },
      { NULL, -1 }
  };
  #endif
  
  /* Functions */
  
  /*
  ** usleep(3) implemented with select.
  */
  
  void xsleep(long sec) {
  	struct timeval tv;
  
  	tv.tv_sec = sec;
  	tv.tv_usec = 0;
  
  	select(0, NULL, NULL, NULL, &tv);
  }
  
  void EndLog() {
  	if ( use_syslog )
  		closelog();
  } // End of CloseLog
  
  int InitLog(int use_syslog, char *name, char *facility, int verbose_log) {
  int i;
  char *logname;
  
  	verbose = verbose_log;
  	if ( !use_syslog ) 
  		return 1;
  
  	if ( !facility || strlen(facility) > 32 ) {
  		fprintf(stderr, "Invalid syslog facility name '%s'!\n", facility);
  		return 0;
  	}
  
  	
  	i = 0;
  	while ( facilitynames[i].c_name && strcasecmp(facilitynames[i].c_name, facility ) != 0 ) {
  		i++;
  	}
  
  	if ( facilitynames[i].c_name == NULL ) {
  		fprintf(stderr, "Invalid syslog facility name '%s'!\n", facility);
  		return 0;
  	}
  
  	if ( (logname = strrchr(name , '/')) != 0 ) {
  		logname++;
  	} else {
  		logname = name;
  	}
  	openlog(logname, LOG_CONS|LOG_PID, facilitynames[i].c_val);
  	use_syslog = 1;
  
  	return 1;
  
  } // End of InitLog
  
  /* 
   * some modules are needed for daemon code as well as normal stdio code 
   * therefore a generic LogError is defined, which maps in this case
   * to stderr
   */
  void LogError(char *format, ...) {
  va_list var_args;
  char string[512];
  
  	if ( use_syslog ) {
  		va_start(var_args, format);
  		vsnprintf(string, 511, format, var_args);
  		va_end(var_args);
  		syslog(LOG_ERR, "%s", string);
  		dbg_printf("%s\n", string);
  	} else {
  		va_start(var_args, format);
  		vsnprintf(string, 511, format, var_args);
  		fprintf(stderr, "%s\n", string);
  		va_end(var_args);
  	}
  	
  } // End of LogError
  
  void LogInfo(char *format, ...) {
  va_list var_args;
  char string[512];
  
  	if ( use_syslog ) {
  		va_start(var_args, format);
  		vsnprintf(string, 511, format, var_args);
  		va_end(var_args);
  		syslog(LOG_INFO, "%s", string);
  		dbg_printf("%s\n", string);
  	} else {
  		va_start(var_args, format);
  		vsnprintf(string, 511, format, var_args);
  		fprintf(stderr, "%s\n", string);
  		va_end(var_args);
  	}
  	
  } // End of LogInfo
  
  static int check_number(char *s, int len) {
  int i;
  int l = strlen(s);
  
  	for ( i=0; i<l; i++ ) {
  		if ( s[i] < '0' || s[i] > '9' ) {
  			LogError("Time format error at '%s': unexpected character: '%c'.\n", s, s[i]);
  			return 0;
  		}
  	}
  
  	if ( l != len ) {
  		LogError("Time format error: '%s' unexpected.\n", s);
  		return 0;
  	}
  	return 1;
  
  } // End of check_number
  
  static int ParseTime(char *s, time_t *t_start ) {
  struct tm ts;
  int	i;
  char *p, *q;
  
  
  	/* A time string may look like:
  	 * yyyy/MM/dd.hh:mm:ss
  	 */
  
  	memset((void *)&ts, 0, sizeof(ts));
  	ts.tm_isdst = -1;
  
  	p = s;
  
  	// parse year
  	q = strchr(p, '/');
  	if ( q ) {
  		*q++ = 0;
  	}
  	if ( !check_number(p,4) )
  		return 0;
  	i = atoi(p);
  	if ( i > 2038 || i < 1970 ) {
  		LogError("Year out of range: '%i'\n", i);
  		*t_start = 0;
  		return 0;
  	}
  	ts.tm_year = i - 1900;
  	if ( !q ) {
  		ts.tm_mday = 1;
  		*t_start = mktime(&ts);
  		return 1;
  	}
  
  	// parse month
  	p = q;
  	q = strchr(p, '/');
  	if ( q ) 
  		*q++ = 0;
  	if ( !check_number(p,2) ) 
  		return 0;
  	i = atoi(p);
  	if ( i < 1 || i > 12 ) {
  		LogError("Month out of range: '%i'\n", i);
  		*t_start = 0;
  		return 0;
  	}
  	ts.tm_mon = i - 1;
  	if ( !q ) {
  		ts.tm_mday = 1;
  		*t_start   = mktime(&ts);
  		return 1;
  	}
  
  	// Parse day
  	p = q;
  	q = strchr(p, '.');
  	if ( q ) 
  		*q++ = 0;
  	if ( !check_number(p,2) ) 
  		return 0;
  	i = atoi(p);
  	if ( i < 1 || i > 31 ) {
  		LogError("Day out of range: '%i'\n", i);
  		*t_start = 0;
  		return 0;
  	}
  	ts.tm_mday = i;
  	if ( !q ) {
  		*t_start = mktime(&ts);
  		return 1;
  	}
  
  	// Parse hour
  	p = q;
  	q = strchr(p, ':');
  	if ( q ) 
  		*q++ = 0;
  	if ( !check_number(p,2) ) 
  		return 0;
  	i = atoi(p);
  	if ( i < 0 || i > 23 ) {
  		LogError("Hour out of range: '%i'\n", i);
  		*t_start = 0;
  		return 0;
  	}
  	ts.tm_hour = i;
  	if ( !q ) {
  		*t_start = mktime(&ts);
  		return 1;
  	}
  
  	// Parse minute
  	p = q;
  	q = strchr(p, ':');
  	if ( q ) 
  		*q++ = 0;
  	if ( !check_number(p,2) ) 
  		return 0;
  	i = atoi(p);
  	if ( i < 0 || i > 59 ) {
  		LogError("Minute out of range: '%i'\n", i);
  		*t_start = 0;
  		return 0;
  	}
  	ts.tm_min = i;
  	if ( !q ) {
  		*t_start = mktime(&ts);
  		return 1;
  	}
  
  	// Parse second
  	p = q;
  	if ( !check_number(p,2) ) 
  		return 0;
  	i = atoi(p);
  	if ( i < 0 || i > 59 ) {
  		LogError("Seconds out of range: '%i'\n", i);
  		*t_start = 0;
  		return 0;
  	}
  	ts.tm_sec = i;
  	*t_start = mktime(&ts);
  	return 1;
  
  } // End of ParseTime
  
  
  int ScanTimeFrame(char *tstring, time_t *t_start, time_t *t_end) {
  char *p;
  
  	if ( !tstring ) {
  		fprintf(stderr,"Time Window format error\n");
  		return 0;
  	}
  
  	// check for delta time window
  	if ( tstring[0] == '-' || tstring[0] == '+' ) {
  		if ( !twin_first || !twin_last ) {
  			fprintf(stderr,"Time Window error: No time slot information available\n");
  			return 0;
  		}
  
  		if ( tstring[0] == '-' ) {
  			*t_start = twin_last + atoi(tstring);
  			*t_end	 = twin_last;
  			return 1;
  		}
  		
  		if ( tstring[0] == '+' ) {
  			*t_start = twin_first;
  			*t_end	 = twin_first + atoi(tstring);
  			return 1;
  		}
  	}
  
  	if ( strlen(tstring) < 4 ) {
  		fprintf(stderr,"Time Window format error '%s'\n", tstring);
  		return 0;
  	}
  	if ( (p = strchr(tstring, '-') ) == NULL ) {
  		ParseTime(tstring, t_start);
  		*t_end = 0xFFFFFFFF;
  	} else {
  		*p++ = 0;
  		ParseTime(tstring, t_start);
  		ParseTime(p, t_end);
  	}
  
  	return *t_start == 0 || *t_end == 0 ? 0 : 1;
  
  } // End of ScanTimeFrame
  
  char *TimeString(time_t start, time_t end) {
  static char datestr[255];
  char t1[64], t2[64];
  struct tm	*tbuff;
  
  	if ( start ) {
  		tbuff = localtime(&start);
  		if ( !tbuff ) {
  			perror("Error time convert");
  			exit(250);
  		}
  		strftime(t1, 63, "%Y-%m-%d %H:%M:%S", tbuff);
  
  		tbuff = localtime(&end);
  		if ( !tbuff ) {
  			perror("Error time convert");
  			exit(250);
  		}
  		strftime(t2, 63, "%Y-%m-%d %H:%M:%S", tbuff);
  
  		snprintf(datestr, 254, "%s - %s", t1, t2);
  	} else {
  		snprintf(datestr, 254, "Time Window unknown");
  	}
  	datestr[254] = 0;
  	return datestr;
  }
  
  char *UNIX2ISO(time_t t) {
  struct tm	*when;
  static char timestring[32];
  
  	when = localtime(&t);
  	when->tm_isdst = -1;
  	snprintf(timestring, 31, "%4i%02i%02i%02i%02i%02i", 
  		when->tm_year + 1900, when->tm_mon + 1, when->tm_mday, when->tm_hour, when->tm_min, when->tm_sec);
  	timestring[31] = '\0';
  
  	return timestring;
  
  } // End of UNIX2ISO
  
  time_t ISO2UNIX(char *timestring) {
  char c, *p;
  struct tm	when;
  time_t		t;
  
  	// let localtime fill in all default fields such as summer time, TZ etc.
  	t = time(NULL);
  	localtime_r(&t, &when);
  	when.tm_sec  = 0;
  	when.tm_wday = 0;
  	when.tm_yday = 0;
  	when.tm_isdst = -1;
  
  	size_t len = strlen(timestring);
  	if ( len != 12 && len != 14) {
  		LogError( "Wrong time format '%s'\n", timestring);
  		return 0;
  	}
  	// 2019 05 05 12 00 (10)
  	// year
  	p = timestring;
  	c = p[4];
  	p[4] = '\0';
  	when.tm_year = atoi(p) - 1900;
  	p[4] = c;
  
  	// month
  	p += 4;
  	c = p[2];
  	p[2] = '\0';
  	when.tm_mon = atoi(p) - 1;
  	p[2] = c;
  
  	// day
  	p += 2;
  	c = p[2];
  	p[2] = '\0';
  	when.tm_mday = atoi(p);
  	p[2] = c;
  	
  	// hour
  	p += 2;
  	c = p[2];
  	p[2] = '\0';
  	when.tm_hour = atoi(p);
  	p[2] = c;
  	
  	// minute
  	p += 2;
  	c = p[2];
  	p[2] = '\0';
  	when.tm_min = atoi(p);
  	p[2] = c;
  	
  	if ( len == 14 ) {
  		p += 2;
  		when.tm_sec = atoi(p);
  	}
  
  	t = mktime(&when);
  	if ( t == -1 ) {
  		LogError( "Failed to convert string '%s'\n", timestring);
  		return 0;
  	} else {
  // printf("%s %s", timestring, ctime(&t));
  		return t;
  	}
  		
  } // End of ISO2UNIX
  
  
  void InitStringlist(stringlist_t *list, int block_size) {
  
  	list->list  = NULL;
  	list->num_strings = 0;
  	list->max_index   = 0;
  	list->block_size  = block_size;
  
  } // End of InitStringlist
  
  void InsertString(stringlist_t *list, char *string) {
  
  	if ( !list->list ) {
  		list->max_index   = list->block_size;
  		list->num_strings = 0;
  		list->list = (char **)malloc(list->max_index * sizeof(char *));
  		if ( !list->list ) {
  			LogError("malloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
  			exit(250);
  		}
  	}
  	list->list[list->num_strings++] = string ? strdup(string) : NULL;
  
  	if ( list->num_strings == list->max_index ) {
  		list->max_index += list->block_size;
  		list->list = (char **)realloc(list->list, list->max_index * sizeof(char *));
  		if ( !list->list ) {
  			LogError("realloc() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
  			exit(250);
  		}
  	}
  
  } // End of InsertString
  
  void format_number(uint64_t num, char *s, int scale, int fixed_width) {
  double f = num;
  
  	if ( !scale ) {
  		snprintf(s, 31, "%llu", (long long unsigned)num);
  	} else {
  
  		if ( f >= _1TB ) {
  			if ( fixed_width ) 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%5.1f T", f / _1TB );
  			else 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%.1f T", f / _1TB );
  		} else if ( f >= _1GB ) {
  			if ( fixed_width ) 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%5.1f G", f / _1GB );
  			else 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%.1f G", f / _1GB );
  		} else if ( f >= _1MB ) {
  			if ( fixed_width ) 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%5.1f M", f / _1MB );
  			else 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%.1f M", f / _1MB );
  		} else  {
  			if ( fixed_width ) 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%4.0f", f );
  			else 
  				snprintf(s, NUMBER_STRING_SIZE-1, "%.0f", f );
  		} 
  		s[NUMBER_STRING_SIZE-1] = '\0';
  	}
  
  } // End of format_number
  
  void inet_ntop_mask(uint32_t ipv4, int mask, char *s, size_t sSize) {
  
  	if ( mask ) {
  		ipv4 &= 0xffffffffL << ( 32 - mask );
  		ipv4 = htonl(ipv4);
  		inet_ntop(AF_INET, &ipv4, s, sSize);
  	} else {
  		s[0] = '\0';
  	}
  
  } // End of inet_ntop_mask
  
  void inet6_ntop_mask(uint64_t ipv6[2], int mask, char *s, size_t sSize) {
      uint64_t ip[2];
  
      ip[0] = ipv6[0];
      ip[1] = ipv6[1];
      if ( mask ) {
          if ( mask <= 64 ) {
              ip[0] = ip[0] & (0xffffffffffffffffLL << (64 - mask));
              ip[1] = 0;
          } else {
              ip[1] = ip[1] & (0xffffffffffffffffLL << (128 - mask));
          }
          ip[0] = htonll(ip[0]);
          ip[1] = htonll(ip[1]);
          inet_ntop(AF_INET6, ip, s, sSize);
  
      } else {
          s[0] = '\0';
      }
  } // End of inet_ntop_mask
  