libxt_time.c revision d64d54777b4a9405a8229a533e44a2e80f000a9f
1/* 2 * libxt_time - iptables part for xt_time 3 * Copyright © CC Computer Consultants GmbH, 2007 4 * Contact: <jengelh@computergmbh.de> 5 * 6 * libxt_time.c is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 or 3 of the License. 9 * 10 * Based on libipt_time.c. 11 */ 12#include <stdio.h> 13#include <string.h> 14#include <stdlib.h> 15#include <time.h> 16#include <linux/types.h> 17#include <linux/netfilter/xt_time.h> 18#include <xtables.h> 19 20enum { 21 O_DATE_START = 0, 22 O_DATE_STOP, 23 O_TIME_START, 24 O_TIME_STOP, 25 O_MONTHDAYS, 26 O_WEEKDAYS, 27 O_LOCAL_TZ, 28 O_UTC, 29}; 30 31static const char *const week_days[] = { 32 NULL, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", 33}; 34 35static const struct xt_option_entry time_opts[] = { 36 {.name = "datestart", .id = O_DATE_START, .type = XTTYPE_STRING}, 37 {.name = "datestop", .id = O_DATE_STOP, .type = XTTYPE_STRING}, 38 {.name = "timestart", .id = O_TIME_START, .type = XTTYPE_STRING}, 39 {.name = "timestop", .id = O_TIME_STOP, .type = XTTYPE_STRING}, 40 {.name = "weekdays", .id = O_WEEKDAYS, .type = XTTYPE_STRING, 41 .flags = XTOPT_INVERT}, 42 {.name = "monthdays", .id = O_MONTHDAYS, .type = XTTYPE_STRING, 43 .flags = XTOPT_INVERT}, 44 {.name = "localtz", .id = O_LOCAL_TZ, .type = XTTYPE_NONE}, 45 {.name = "utc", .id = O_UTC, .type = XTTYPE_NONE}, 46 XTOPT_TABLEEND, 47}; 48 49static void time_help(void) 50{ 51 printf( 52"time match options:\n" 53" --datestart time Start and stop time, to be given in ISO 8601\n" 54" --datestop time (YYYY[-MM[-DD[Thh[:mm[:ss]]]]])\n" 55" --timestart time Start and stop daytime (hh:mm[:ss])\n" 56" --timestop time (between 00:00:00 and 23:59:59)\n" 57"[!] --monthdays value List of days on which to match, separated by comma\n" 58" (Possible days: 1 to 31; defaults to all)\n" 59"[!] --weekdays value List of weekdays on which to match, sep. by comma\n" 60" (Possible days: Mon,Tue,Wed,Thu,Fri,Sat,Sun or 1 to 7\n" 61" Defaults to all weekdays.)\n" 62" --localtz/--utc Time is interpreted as UTC/local time\n"); 63} 64 65static void time_init(struct xt_entry_match *m) 66{ 67 struct xt_time_info *info = (void *)m->data; 68 69 /* By default, we match on every day, every daytime */ 70 info->monthdays_match = XT_TIME_ALL_MONTHDAYS; 71 info->weekdays_match = XT_TIME_ALL_WEEKDAYS; 72 info->daytime_start = XT_TIME_MIN_DAYTIME; 73 info->daytime_stop = XT_TIME_MAX_DAYTIME; 74 75 /* ...and have no date-begin or date-end boundary */ 76 info->date_start = 0; 77 info->date_stop = INT_MAX; 78 79 /* local time is default */ 80 info->flags |= XT_TIME_LOCAL_TZ; 81} 82 83static time_t time_parse_date(const char *s, bool end) 84{ 85 unsigned int month = 1, day = 1, hour = 0, minute = 0, second = 0; 86 unsigned int year = end ? 2038 : 1970; 87 const char *os = s; 88 struct tm tm; 89 time_t ret; 90 char *e; 91 92 year = strtoul(s, &e, 10); 93 if ((*e != '-' && *e != '\0') || year < 1970 || year > 2038) 94 goto out; 95 if (*e == '\0') 96 goto eval; 97 98 s = e + 1; 99 month = strtoul(s, &e, 10); 100 if ((*e != '-' && *e != '\0') || month > 12) 101 goto out; 102 if (*e == '\0') 103 goto eval; 104 105 s = e + 1; 106 day = strtoul(s, &e, 10); 107 if ((*e != 'T' && *e != '\0') || day > 31) 108 goto out; 109 if (*e == '\0') 110 goto eval; 111 112 s = e + 1; 113 hour = strtoul(s, &e, 10); 114 if ((*e != ':' && *e != '\0') || hour > 23) 115 goto out; 116 if (*e == '\0') 117 goto eval; 118 119 s = e + 1; 120 minute = strtoul(s, &e, 10); 121 if ((*e != ':' && *e != '\0') || minute > 59) 122 goto out; 123 if (*e == '\0') 124 goto eval; 125 126 s = e + 1; 127 second = strtoul(s, &e, 10); 128 if (*e != '\0' || second > 59) 129 goto out; 130 131 eval: 132 tm.tm_year = year - 1900; 133 tm.tm_mon = month - 1; 134 tm.tm_mday = day; 135 tm.tm_hour = hour; 136 tm.tm_min = minute; 137 tm.tm_sec = second; 138 tm.tm_isdst = 0; 139 ret = mktime(&tm); 140 if (ret >= 0) 141 return ret; 142 perror("mktime"); 143 xtables_error(OTHER_PROBLEM, "mktime returned an error"); 144 145 out: 146 xtables_error(PARAMETER_PROBLEM, "Invalid date \"%s\" specified. Should " 147 "be YYYY[-MM[-DD[Thh[:mm[:ss]]]]]", os); 148 return -1; 149} 150 151static unsigned int time_parse_minutes(const char *s) 152{ 153 unsigned int hour, minute, second = 0; 154 char *e; 155 156 hour = strtoul(s, &e, 10); 157 if (*e != ':' || hour > 23) 158 goto out; 159 160 s = e + 1; 161 minute = strtoul(s, &e, 10); 162 if ((*e != ':' && *e != '\0') || minute > 59) 163 goto out; 164 if (*e == '\0') 165 goto eval; 166 167 s = e + 1; 168 second = strtoul(s, &e, 10); 169 if (*e != '\0' || second > 59) 170 goto out; 171 172 eval: 173 return 60 * 60 * hour + 60 * minute + second; 174 175 out: 176 xtables_error(PARAMETER_PROBLEM, "invalid time \"%s\" specified, " 177 "should be hh:mm[:ss] format and within the boundaries", s); 178 return -1; 179} 180 181static const char *my_strseg(char *buf, unsigned int buflen, 182 const char **arg, char delim) 183{ 184 const char *sep; 185 186 if (*arg == NULL || **arg == '\0') 187 return NULL; 188 sep = strchr(*arg, delim); 189 if (sep == NULL) { 190 snprintf(buf, buflen, "%s", *arg); 191 *arg = NULL; 192 return buf; 193 } 194 snprintf(buf, buflen, "%.*s", (unsigned int)(sep - *arg), *arg); 195 *arg = sep + 1; 196 return buf; 197} 198 199static uint32_t time_parse_monthdays(const char *arg) 200{ 201 char day[3], *err = NULL; 202 uint32_t ret = 0; 203 unsigned int i; 204 205 while (my_strseg(day, sizeof(day), &arg, ',') != NULL) { 206 i = strtoul(day, &err, 0); 207 if ((*err != ',' && *err != '\0') || i > 31) 208 xtables_error(PARAMETER_PROBLEM, 209 "%s is not a valid day for --monthdays", day); 210 ret |= 1 << i; 211 } 212 213 return ret; 214} 215 216static unsigned int time_parse_weekdays(const char *arg) 217{ 218 char day[4], *err = NULL; 219 unsigned int i, ret = 0; 220 bool valid; 221 222 while (my_strseg(day, sizeof(day), &arg, ',') != NULL) { 223 i = strtoul(day, &err, 0); 224 if (*err == '\0') { 225 if (i == 0) 226 xtables_error(PARAMETER_PROBLEM, 227 "No, the week does NOT begin with Sunday."); 228 ret |= 1 << i; 229 continue; 230 } 231 232 valid = false; 233 for (i = 1; i < ARRAY_SIZE(week_days); ++i) 234 if (strncmp(day, week_days[i], 2) == 0) { 235 ret |= 1 << i; 236 valid = true; 237 } 238 239 if (!valid) 240 xtables_error(PARAMETER_PROBLEM, 241 "%s is not a valid day specifier", day); 242 } 243 244 return ret; 245} 246 247static void time_parse(struct xt_option_call *cb) 248{ 249 struct xt_time_info *info = cb->data; 250 251 xtables_option_parse(cb); 252 switch (cb->entry->id) { 253 case O_DATE_START: 254 info->date_start = time_parse_date(cb->arg, false); 255 break; 256 case O_DATE_STOP: 257 info->date_stop = time_parse_date(cb->arg, true); 258 break; 259 case O_TIME_START: 260 info->daytime_start = time_parse_minutes(cb->arg); 261 break; 262 case O_TIME_STOP: 263 info->daytime_stop = time_parse_minutes(cb->arg); 264 break; 265 case O_LOCAL_TZ: 266 info->flags |= XT_TIME_LOCAL_TZ; 267 break; 268 case O_MONTHDAYS: 269 info->monthdays_match = time_parse_monthdays(cb->arg); 270 if (cb->invert) 271 info->monthdays_match ^= XT_TIME_ALL_MONTHDAYS; 272 break; 273 case O_WEEKDAYS: 274 info->weekdays_match = time_parse_weekdays(cb->arg); 275 if (cb->invert) 276 info->weekdays_match ^= XT_TIME_ALL_WEEKDAYS; 277 break; 278 case O_UTC: 279 info->flags &= ~XT_TIME_LOCAL_TZ; 280 break; 281 } 282} 283 284static void time_print_date(time_t date, const char *command) 285{ 286 struct tm *t; 287 288 /* If it is the default value, do not print it. */ 289 if (date == 0 || date == LONG_MAX) 290 return; 291 292 t = localtime(&date); 293 if (command != NULL) 294 /* 295 * Need a contiguous string (no whitespaces), hence using 296 * the ISO 8601 "T" variant. 297 */ 298 printf(" %s %04u-%02u-%02uT%02u:%02u:%02u", 299 command, t->tm_year + 1900, t->tm_mon + 1, 300 t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); 301 else 302 printf(" %04u-%02u-%02u %02u:%02u:%02u", 303 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 304 t->tm_hour, t->tm_min, t->tm_sec); 305} 306 307static void time_print_monthdays(uint32_t mask, bool human_readable) 308{ 309 unsigned int i, nbdays = 0; 310 311 printf(" "); 312 for (i = 1; i <= 31; ++i) 313 if (mask & (1 << i)) { 314 if (nbdays++ > 0) 315 printf(","); 316 printf("%u", i); 317 if (human_readable) 318 switch (i % 10) { 319 case 1: 320 printf("st"); 321 break; 322 case 2: 323 printf("nd"); 324 break; 325 case 3: 326 printf("rd"); 327 break; 328 default: 329 printf("th"); 330 break; 331 } 332 } 333} 334 335static void time_print_weekdays(unsigned int mask) 336{ 337 unsigned int i, nbdays = 0; 338 339 printf(" "); 340 for (i = 1; i <= 7; ++i) 341 if (mask & (1 << i)) { 342 if (nbdays > 0) 343 printf(",%s", week_days[i]); 344 else 345 printf("%s", week_days[i]); 346 ++nbdays; 347 } 348} 349 350static inline void divide_time(unsigned int fulltime, unsigned int *hours, 351 unsigned int *minutes, unsigned int *seconds) 352{ 353 *seconds = fulltime % 60; 354 fulltime /= 60; 355 *minutes = fulltime % 60; 356 *hours = fulltime / 60; 357} 358 359static void time_print(const void *ip, const struct xt_entry_match *match, 360 int numeric) 361{ 362 const struct xt_time_info *info = (const void *)match->data; 363 unsigned int h, m, s; 364 365 printf(" TIME"); 366 367 if (info->daytime_start != XT_TIME_MIN_DAYTIME || 368 info->daytime_stop != XT_TIME_MAX_DAYTIME) { 369 divide_time(info->daytime_start, &h, &m, &s); 370 printf(" from %02u:%02u:%02u", h, m, s); 371 divide_time(info->daytime_stop, &h, &m, &s); 372 printf(" to %02u:%02u:%02u", h, m, s); 373 } 374 if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { 375 printf(" on"); 376 time_print_weekdays(info->weekdays_match); 377 } 378 if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { 379 printf(" on"); 380 time_print_monthdays(info->monthdays_match, true); 381 } 382 if (info->date_start != 0) { 383 printf(" starting from"); 384 time_print_date(info->date_start, NULL); 385 } 386 if (info->date_stop != INT_MAX) { 387 printf(" until date"); 388 time_print_date(info->date_stop, NULL); 389 } 390 if (!(info->flags & XT_TIME_LOCAL_TZ)) 391 printf(" UTC"); 392} 393 394static void time_save(const void *ip, const struct xt_entry_match *match) 395{ 396 const struct xt_time_info *info = (const void *)match->data; 397 unsigned int h, m, s; 398 399 if (info->daytime_start != XT_TIME_MIN_DAYTIME || 400 info->daytime_stop != XT_TIME_MAX_DAYTIME) { 401 divide_time(info->daytime_start, &h, &m, &s); 402 printf(" --timestart %02u:%02u:%02u", h, m, s); 403 divide_time(info->daytime_stop, &h, &m, &s); 404 printf(" --timestop %02u:%02u:%02u", h, m, s); 405 } 406 if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { 407 printf(" --monthdays"); 408 time_print_monthdays(info->monthdays_match, false); 409 } 410 if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { 411 printf(" --weekdays"); 412 time_print_weekdays(info->weekdays_match); 413 } 414 time_print_date(info->date_start, "--datestart"); 415 time_print_date(info->date_stop, "--datestop"); 416 if (!(info->flags & XT_TIME_LOCAL_TZ)) 417 printf(" --utc"); 418} 419 420static struct xtables_match time_match = { 421 .name = "time", 422 .family = NFPROTO_UNSPEC, 423 .version = XTABLES_VERSION, 424 .size = XT_ALIGN(sizeof(struct xt_time_info)), 425 .userspacesize = XT_ALIGN(sizeof(struct xt_time_info)), 426 .help = time_help, 427 .init = time_init, 428 .print = time_print, 429 .save = time_save, 430 .x6_parse = time_parse, 431 .x6_options = time_opts, 432}; 433 434void _init(void) 435{ 436 xtables_register_match(&time_match); 437} 438