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