iptables-1.3.2-20050720
[iptables.git] / extensions / libipt_time.c
index 909ca81..dcf2dc6 100644 (file)
@@ -3,6 +3,7 @@
 #include <netdb.h>
 #include <string.h>
 #include <stdlib.h>
+#include <stddef.h> /* for 'offsetof' */
 #include <getopt.h>
 
 #include <iptables.h>
@@ -17,10 +18,27 @@ help(void)
 {
        printf(
 "TIME v%s options:\n"
-" --timestart value --timestop value --days listofdays\n"
-"          timestart value : HH:MM\n"
-"          timestop  value : HH:MM\n"
-"          listofdays value: a list of days to apply -> ie. Mon,Tue,Wed,Thu,Fri. Case sensitive\n",
+" [ --timestart value ] [ --timestop value] [ --days listofdays ] [ --datestart value ] [ --datestop value ]\n"
+"          timestart value : HH:MM (default 00:00)\n"
+"          timestop  value : HH:MM (default 23:59)\n"
+"                            Note: daylight savings time changes are not tracked\n"
+"          listofdays value: a list of days to apply\n"
+"                            from Mon,Tue,Wed,Thu,Fri,Sat,Sun\n"
+"                            Coma speparated, no space, case sensitive.\n"
+"                            Defaults to all days.\n"
+"          datestart value : YYYY[:MM[:DD[:hh[:mm[:ss]]]]]\n"
+"                            If any of month, day, hour, minute or second is\n"
+"                            not specified, then defaults to their smallest\n"
+"                            1900 <= YYYY < 2037\n"
+"                               1 <= MM <= 12\n"
+"                               1 <= DD <= 31\n"
+"                               0 <= hh <= 23\n"
+"                               0 <= mm <= 59\n"
+"                               0 <= ss <= 59\n"
+"          datestop  value : YYYY[:MM[:DD[:hh[:mm[:ss]]]]]\n"
+"                            If the whole option is ommited, default to never stop\n"
+"                            If any of month, day, hour, minute or second is\n"
+"                            not specified, then default to their smallest\n",
 IPTABLES_VERSION);
 }
 
@@ -28,6 +46,8 @@ static struct option opts[] = {
        { "timestart", 1, 0, '1' },
        { "timestop", 1, 0, '2' },
        { "days", 1, 0, '3'},
+       { "datestart", 1, 0, '4' },
+       { "datestop", 1, 0, '5' },
        {0}
 };
 
@@ -35,9 +55,16 @@ static struct option opts[] = {
 static void
 init(struct ipt_entry_match *m, unsigned int *nfcache)
 {
-       /* caching not yet implemented */
-        *nfcache |= NFC_UNKNOWN;
+       struct ipt_time_info *info = (struct ipt_time_info *)m->data;
        globaldays = 0;
+        /* By default, we match on everyday */
+       info->days_match = 127;
+       /* By default, we match on every hour:min of the day */
+       info->time_start = 0;
+       info->time_stop  = 1439;  /* (23*60+59 = 1439 */
+       /* By default, we don't have any date-begin or date-end boundaries */
+       info->date_start = 0;
+       info->date_stop  = LONG_MAX;
 }
 
 /**
@@ -77,36 +104,45 @@ split_time(char **part1, char **part2, const char *str_2_parse)
        return 1;
 }
 
+static int
+parse_number(char *str, int num_min, int num_max, int *number)
+{
+       /* if the number starts with 0, replace it with a space else
+       string_to_number() will interpret it as octal !! */
+       if (strlen(str) == 0)
+               return 0;
+
+       if ((str[0] == '0') && (str[1] != '\0'))
+               str[0] = ' ';
+
+       return string_to_number(str, num_min, num_max, number);
+}
+
 static void
-parse_time_string(unsigned int *hour, unsigned int *minute, const char *time)
+parse_time_string(int *hour, int *minute, const char *time)
 {
        char *hours;
        char *minutes;
-
        hours = (char *)malloc(3);
        minutes = (char *)malloc(3);
-       bzero((void *)hours, 3);
-       bzero((void *)minutes, 3);
+       memset(hours, 0, 3);
+       memset(minutes, 0, 3);
 
-       if (split_time(&hours, &minutes, time) == 1)
+       if (split_time((char **)&hours, (char **)&minutes, time) == 1)
        {
-                /* if the number starts with 0, replace it with a space else
-                   this string_to_number will interpret it as octal !! */
-                if ((hours[0] == '0') && (hours[1] != '\0'))
-                       hours[0] = ' ';
-               if ((minutes[0] == '0') && (minutes[1] != '\0'))
-                       minutes[0] = ' ';
-
-               if((string_to_number(hours, 0, 23, hour) == -1) ||
-                       (string_to_number(minutes, 0, 59, minute) == -1)) {
-                       *hour = *minute = (-1);
+               *hour = 0;
+               *minute = 0;
+               if ((parse_number((char *)hours, 0, 23, hour) != -1) &&
+                   (parse_number((char *)minutes, 0, 59, minute) != -1))
+               {
+                       free(hours);
+                       free(minutes);
+                       return;
                }
        }
-       if ((*hour != (-1)) && (*minute != (-1))) {
-               free(hours);
-               free(minutes);
-               return;
-       }
+
+       free(hours);
+       free(minutes);
 
        /* If we are here, there was a problem ..*/
        exit_error(PARAMETER_PROBLEM,
@@ -160,10 +196,139 @@ parse_days_string(int *days, const char *daystring)
        }
 }
 
+static int
+parse_date_field(const char *str_to_parse, int str_to_parse_s, int start_pos,
+                 char *dest, int *next_pos)
+{
+       unsigned char found_value = 0;
+       unsigned char found_column = 0;
+       int i;
+
+       for (i=0; i<2; i++)
+       {
+               if ((i+start_pos) >= str_to_parse_s) /* don't exit boundaries of the string..  */
+                       break;
+               if (str_to_parse[i+start_pos] == ':')
+                       found_column = 1;
+               else
+               {
+                       found_value = 1;
+                       dest[i] = str_to_parse[i+start_pos];
+               }
+       }
+       if (found_value == 0)
+               return 0;
+       *next_pos = i + start_pos;
+       if (found_column == 0)
+               ++(*next_pos);
+       return 1;
+}
+
+static int
+split_date(char *year, char *month,  char *day,
+           char *hour, char *minute, char *second,
+           const char *str_to_parse)
+{
+        int i;
+        unsigned char found_column = 0;
+       int str_to_parse_s = strlen(str_to_parse);
+
+        /* Check the length of the string */
+        if ((str_to_parse_s > 19) ||  /* YYYY:MM:DD:HH:MM:SS */
+            (str_to_parse_s < 4))     /* YYYY*/
+                return 0;
+
+       /* Clear the buffers */
+        memset(year, 0, 4);
+       memset(month, 0, 2);
+       memset(day, 0, 2);
+       memset(hour, 0, 2);
+       memset(minute, 0, 2);
+       memset(second, 0, 2);
+
+       /* parse the year YYYY */
+       found_column = 0;
+       for (i=0; i<5; i++)
+       {
+               if (i >= str_to_parse_s)
+                       break;
+               if (str_to_parse[i] == ':')
+               {
+                       found_column = 1;
+                       break;
+               }
+               else
+                       year[i] = str_to_parse[i];
+       }
+       if (found_column == 1)
+               ++i;
+
+       /* parse the month if it exists */
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, month, &i))
+               return 1;
+
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, day, &i))
+               return 1;
+
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, hour, &i))
+               return 1;
+
+       if (! parse_date_field(str_to_parse, str_to_parse_s, i, minute, &i))
+               return 1;
+
+       parse_date_field(str_to_parse, str_to_parse_s, i, second, &i);
+
+        /* if we are here, format should be ok. */
+        return 1;
+}
+
+static time_t
+parse_date_string(const char *str_to_parse)
+{
+       char year[5];
+       char month[3];
+       char day[3];
+       char hour[3];
+       char minute[3];
+       char second[3];
+       struct tm t;
+       time_t temp_time;
+
+       memset(year, 0, 5);
+       memset(month, 0, 3);
+       memset(day, 0, 3);
+       memset(hour, 0, 3);
+       memset(minute, 0, 3);
+       memset(second, 0, 3);
+
+        if (split_date(year, month, day, hour, minute, second, str_to_parse) == 1)
+        {
+               memset((void *)&t, 0, sizeof(struct tm));
+               t.tm_isdst = -1;
+               t.tm_mday = 1;
+               if (!((parse_number(year, 1900, 2037, &(t.tm_year)) == -1) ||
+                     (parse_number(month, 1, 12, &(t.tm_mon)) == -1) ||
+                     (parse_number(day, 1, 31, &(t.tm_mday)) == -1) ||
+                     (parse_number(hour, 0, 9999, &(t.tm_hour)) == -1) ||
+                     (parse_number(minute, 0, 59, &(t.tm_min)) == -1) ||
+                     (parse_number(second, 0, 59, &(t.tm_sec)) == -1)))
+               {
+                       t.tm_year -= 1900;
+                       --(t.tm_mon);
+                       temp_time = mktime(&t);
+                       if (temp_time != -1)
+                               return temp_time;
+               }
+       }
+       exit_error(PARAMETER_PROBLEM,
+                  "invalid date `%s' specified, should be YYYY[:MM[:DD[:hh[:mm[:ss]]]]] format", str_to_parse);
+}
+
 #define IPT_TIME_START 0x01
 #define IPT_TIME_STOP  0x02
 #define IPT_TIME_DAYS  0x04
-
+#define IPT_DATE_START 0x08
+#define IPT_DATE_STOP  0x10
 
 /* Function which parses command options; returns true if it
    ate an option */
@@ -175,6 +340,7 @@ parse(int c, char **argv, int invert, unsigned int *flags,
 {
        struct ipt_time_info *timeinfo = (struct ipt_time_info *)(*match)->data;
        int hours, minutes;
+       time_t temp_date;
 
        switch (c)
        {
@@ -215,19 +381,43 @@ parse(int c, char **argv, int invert, unsigned int *flags,
                timeinfo->days_match = globaldays;
                *flags |= IPT_TIME_DAYS;
                break;
+
+               /* datestart */
+       case '4':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "unexpected '!' with --datestart");
+               if (*flags & IPT_DATE_START)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --datestart twice");
+               temp_date = parse_date_string(optarg);
+               timeinfo->date_start = temp_date;
+               *flags |= IPT_DATE_START;
+               break;
+
+               /* datestop*/
+       case '5':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "unexpected '!' with --datestop");
+               if (*flags & IPT_DATE_STOP)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --datestop twice");
+               temp_date = parse_date_string(optarg);
+               timeinfo->date_stop = temp_date;
+               *flags |= IPT_DATE_STOP;
+               break;
        default:
                return 0;
        }
        return 1;
 }
 
-/* Final check; must have specified --timestart --timestop --days. */
+/* Final check */
 static void
 final_check(unsigned int flags)
 {
-       if (flags != (IPT_TIME_START | IPT_TIME_STOP | IPT_TIME_DAYS))
-               exit_error(PARAMETER_PROBLEM,
-                          "TIME match: You must specify `--timestart --timestop and --days'");
+       /* Nothing to do */
 }
 
 
@@ -248,6 +438,7 @@ print_days(int daynum)
                        ++nbdays;
                }
        }
+       printf(" ");
 }
 
 static void
@@ -257,6 +448,23 @@ divide_time(int fulltime, int *hours, int *minutes)
        *minutes = fulltime % 60;
 }
 
+static void
+print_date(time_t date, char *command)
+{
+       struct tm *t;
+
+       /* If it's default value, don't print..*/
+       if (((date == 0) || (date == LONG_MAX)) && (command != NULL))
+               return;
+       t = localtime(&date);
+       if (command != NULL)
+               printf("%s %d:%d:%d:%d:%d:%d ", command, (t->tm_year + 1900), (t->tm_mon + 1),
+                       t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+        else
+               printf("%d-%d-%d %d:%d:%d ", (t->tm_year + 1900), (t->tm_mon + 1),
+                       t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+}
+
 /* Prints out the matchinfo. */
 static void
 print(const struct ipt_ip *ip,
@@ -268,11 +476,26 @@ print(const struct ipt_ip *ip,
 
        divide_time(time->time_start, &hour_start, &minute_start);
        divide_time(time->time_stop, &hour_stop, &minute_stop);
-       printf(" TIME from %d:%d to %d:%d on ",
-              hour_start, minute_start,
-              hour_stop, minute_stop);
-       print_days(time->days_match);
-       printf(" ");
+       printf("TIME ");
+       if (time->time_start != 0)
+               printf("from %d:%d ", hour_start, minute_start);
+       if (time->time_stop != 1439) /* 23*60+59 = 1439 */
+               printf("to %d:%d ", hour_stop, minute_stop);
+       printf("on ");
+       if (time->days_match == 127)
+               printf("all days ");
+       else
+               print_days(time->days_match);
+       if (time->date_start != 0)
+       {
+               printf("starting from ");
+               print_date(time->date_start, NULL);
+       }
+       if (time->date_stop != LONG_MAX)
+       {
+               printf("until date ");
+               print_date(time->date_stop, NULL);
+       }
 }
 
 /* Saves the data in parsable form to stdout. */
@@ -284,27 +507,40 @@ save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
 
        divide_time(time->time_start, &hour_start, &minute_start);
        divide_time(time->time_stop, &hour_stop, &minute_stop);
-       printf(" --timestart %.2d:%.2d --timestop %.2d:%.2d --days ",
-              hour_start, minute_start,
-              hour_stop, minute_stop);
-       print_days(time->days_match);
-       printf(" ");
+       if (time->time_start != 0)
+               printf("--timestart %.2d:%.2d ",
+                       hour_start, minute_start);
+       
+       if (time->time_stop != 1439) /* 23*60+59 = 1439 */
+               printf("--timestop %.2d:%.2d ",
+                       hour_stop, minute_stop);
+       
+       if (time->days_match != 127)
+       {
+               printf("--days ");
+               print_days(time->days_match);
+               printf(" ");
+       }
+       print_date(time->date_start, "--datestart");
+       print_date(time->date_stop, "--datestop");
 }
 
+/* have to use offsetof() instead of IPT_ALIGN(), since kerneltime must not
+ * be compared when user deletes rule with '-D' */
 static
-struct iptables_match timestruct
-= { NULL,
-    "time",
-    IPTABLES_VERSION,
-    IPT_ALIGN(sizeof(struct ipt_time_info)),
-    IPT_ALIGN(sizeof(struct ipt_time_info)),
-    &help,
-    &init,
-    &parse,
-    &final_check,
-    &print,
-    &save,
-    opts
+struct iptables_match timestruct = {
+       .next           = NULL,
+       .name           = "time",
+       .version        = IPTABLES_VERSION,
+       .size           = IPT_ALIGN(sizeof(struct ipt_time_info)),
+       .userspacesize  = offsetof(struct ipt_time_info, kerneltime),
+       .help           = &help,
+       .init           = &init,
+       .parse          = &parse,
+       .final_check    = &final_check,
+       .print          = &print,
+       .save           = &save,
+       .extra_opts     = opts
 };
 
 void _init(void)