libxt_time.c revision ad326ef9f734ac30548de292c59fc0e2fd81ac2a
1/* 2 * libxt_time - iptables part for xt_time 3 * Copyright � Jan Engelhardt <jengelh@computergmbh.de>, 2007 4 * 5 * libxt_time.c is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 or 3 of the License. 8 * 9 * Based on libipt_time.c. 10 */ 11#include <sys/types.h> 12#include <getopt.h> 13#include <stdbool.h> 14#include <stdio.h> 15#include <string.h> 16#include <stdlib.h> 17#include <stddef.h> 18#include <time.h> 19#include <linux/netfilter/xt_time.h> 20#include <xtables.h> 21#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) 22 23enum { /* getopt "seen" bits */ 24 F_DATE_START = 1 << 0, 25 F_DATE_STOP = 1 << 1, 26 F_TIME_START = 1 << 2, 27 F_TIME_STOP = 1 << 3, 28 F_MONTHDAYS = 1 << 4, 29 F_WEEKDAYS = 1 << 5, 30 F_TIMEZONE = 1 << 6, 31}; 32 33static const char *const week_days[] = { 34 NULL, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", 35}; 36 37static const struct option time_opts[] = { 38 {"datestart", true, NULL, 'D'}, 39 {"datestop", true, NULL, 'E'}, 40 {"timestart", true, NULL, 'X'}, 41 {"timestop", true, NULL, 'Y'}, 42 {"weekdays", true, NULL, 'w'}, 43 {"monthdays", true, NULL, 'm'}, 44 {"localtz", false, NULL, 'l'}, 45 {"utc", false, NULL, 'u'}, 46 {NULL}, 47}; 48 49static void time_help(void) 50{ 51 printf( 52"TIME v%s 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", 63IPTABLES_VERSION); 64} 65 66static void time_init(struct xt_entry_match *m) 67{ 68 struct xt_time_info *info = (void *)m->data; 69 70 /* By default, we match on every day, every daytime */ 71 info->monthdays_match = XT_TIME_ALL_MONTHDAYS; 72 info->weekdays_match = XT_TIME_ALL_WEEKDAYS; 73 info->daytime_start = XT_TIME_MIN_DAYTIME; 74 info->daytime_stop = XT_TIME_MAX_DAYTIME; 75 76 /* ...and have no date-begin or date-end boundary */ 77 info->date_start = 0; 78 info->date_stop = LONG_MAX; 79 80 /* local time is default */ 81 info->flags |= XT_TIME_LOCAL_TZ; 82} 83 84static time_t time_parse_date(const char *s, bool end) 85{ 86 unsigned int month = 1, day = 1, hour = 0, minute = 0, second = 0; 87 unsigned int year = end ? 2038 : 1970; 88 const char *os = s; 89 struct tm tm; 90 time_t ret; 91 char *e; 92 93 year = strtoul(s, &e, 10); 94 if ((*e != '-' && *e != '\0') || year < 1970 || year > 2038) 95 goto out; 96 if (*e == '\0') 97 goto eval; 98 99 s = e + 1; 100 month = strtoul(s, &e, 10); 101 if ((*e != '-' && *e != '\0') || month > 12) 102 goto out; 103 if (*e == '\0') 104 goto eval; 105 106 s = e + 1; 107 day = strtoul(s, &e, 10); 108 if ((*e != 'T' && *e != '\0') || day > 31) 109 goto out; 110 if (*e == '\0') 111 goto eval; 112 113 s = e + 1; 114 hour = strtoul(s, &e, 10); 115 if ((*e != ':' && *e != '\0') || hour > 23) 116 goto out; 117 if (*e == '\0') 118 goto eval; 119 120 s = e + 1; 121 minute = strtoul(s, &e, 10); 122 if ((*e != ':' && *e != '\0') || minute > 59) 123 goto out; 124 if (*e == '\0') 125 goto eval; 126 127 s = e + 1; 128 second = strtoul(s, &e, 10); 129 if (*e != '\0' || second > 59) 130 goto out; 131 132 eval: 133 tm.tm_year = year - 1900; 134 tm.tm_mon = month - 1; 135 tm.tm_mday = day; 136 tm.tm_hour = hour; 137 tm.tm_min = minute; 138 tm.tm_sec = second; 139 ret = mktime(&tm); 140 if (ret >= 0) 141 return ret; 142 perror("mktime"); 143 exit_error(OTHER_PROBLEM, "mktime returned an error"); 144 145 out: 146 exit_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 exit_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 exit_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 exit_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 exit_error(PARAMETER_PROBLEM, 241 "%s is not a valid day specifier", day); 242 } 243 244 return ret; 245} 246 247static int time_parse(int c, char **argv, int invert, unsigned int *flags, 248 const void *entry, struct xt_entry_match **match) 249{ 250 struct xt_time_info *info = (void *)(*match)->data; 251 252 switch (c) { 253 case 'D': /* --datestart */ 254 if (*flags & F_DATE_START) 255 exit_error(PARAMETER_PROBLEM, 256 "Cannot specify --datestart twice"); 257 if (invert) 258 exit_error(PARAMETER_PROBLEM, 259 "Unexpected \"!\" with --datestart"); 260 info->date_start = time_parse_date(optarg, false); 261 *flags |= F_DATE_START; 262 return 1; 263 case 'E': /* --datestop */ 264 if (*flags & F_DATE_STOP) 265 exit_error(PARAMETER_PROBLEM, 266 "Cannot specify --datestop more than once"); 267 if (invert) 268 exit_error(PARAMETER_PROBLEM, 269 "unexpected \"!\" with --datestop"); 270 info->date_stop = time_parse_date(optarg, true); 271 *flags |= F_DATE_STOP; 272 return 1; 273 case 'X': /* --timestart */ 274 if (*flags & F_TIME_START) 275 exit_error(PARAMETER_PROBLEM, 276 "Cannot specify --timestart more than once"); 277 if (invert) 278 exit_error(PARAMETER_PROBLEM, 279 "Unexpected \"!\" with --timestart"); 280 info->daytime_start = time_parse_minutes(optarg); 281 *flags |= F_TIME_START; 282 return 1; 283 case 'Y': /* --timestop */ 284 if (*flags & F_TIME_STOP) 285 exit_error(PARAMETER_PROBLEM, 286 "Cannot specify --timestop more than once"); 287 if (invert) 288 exit_error(PARAMETER_PROBLEM, 289 "Unexpected \"!\" with --timestop"); 290 info->daytime_stop = time_parse_minutes(optarg); 291 *flags |= F_TIME_STOP; 292 return 1; 293 case 'l': /* --localtz */ 294 if (*flags & F_TIMEZONE) 295 exit_error(PARAMETER_PROBLEM, 296 "Can only specify exactly one of --localtz or --utc"); 297 info->flags |= XT_TIME_LOCAL_TZ; 298 *flags |= F_TIMEZONE; 299 return 1; 300 case 'm': /* --monthdays */ 301 if (*flags & F_MONTHDAYS) 302 exit_error(PARAMETER_PROBLEM, 303 "Cannot specify --monthdays more than once"); 304 info->monthdays_match = time_parse_monthdays(optarg); 305 if (invert) 306 info->monthdays_match ^= XT_TIME_ALL_MONTHDAYS; 307 *flags |= F_MONTHDAYS; 308 return 1; 309 case 'w': /* --weekdays */ 310 if (*flags & F_WEEKDAYS) 311 exit_error(PARAMETER_PROBLEM, 312 "Cannot specify --weekdays more than once"); 313 info->weekdays_match = time_parse_weekdays(optarg); 314 if (invert) 315 info->weekdays_match ^= XT_TIME_ALL_WEEKDAYS; 316 *flags |= F_WEEKDAYS; 317 return 1; 318 case 'u': /* --utc */ 319 if (*flags & F_TIMEZONE) 320 exit_error(PARAMETER_PROBLEM, 321 "Can only specify exactly one of --localtz or --utc"); 322 info->flags &= ~XT_TIME_LOCAL_TZ; 323 *flags |= F_TIMEZONE; 324 return 1; 325 } 326 return 0; 327} 328 329static void time_check(unsigned int flags) 330{ 331} 332 333static void time_print_date(time_t date, const char *command) 334{ 335 struct tm *t; 336 337 /* If it is the default value, do not print it. */ 338 if (date == 0 || date == LONG_MAX) 339 return; 340 341 t = localtime(&date); 342 if (command != NULL) 343 /* 344 * Need a contiguous string (no whitespaces), hence using 345 * the ISO 8601 "T" variant. 346 */ 347 printf("%s %04u-%02u-%02uT%02u:%02u:%02u ", 348 command, t->tm_year + 1900, t->tm_mon + 1, 349 t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); 350 else 351 printf("%04u-%02u-%02u %02u:%02u:%02u ", 352 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 353 t->tm_hour, t->tm_min, t->tm_sec); 354} 355 356static void time_print_monthdays(uint32_t mask, bool human_readable) 357{ 358 unsigned int i, nbdays = 0; 359 360 for (i = 1; i <= 31; ++i) 361 if (mask & (1 << i)) { 362 if (nbdays++ > 0) 363 printf(","); 364 printf("%u", i); 365 if (human_readable) 366 switch (i % 10) { 367 case 1: 368 printf("st"); 369 break; 370 case 2: 371 printf("nd"); 372 break; 373 case 3: 374 printf("rd"); 375 break; 376 default: 377 printf("th"); 378 break; 379 } 380 } 381 printf(" "); 382} 383 384static void time_print_weekdays(unsigned int mask) 385{ 386 unsigned int i, nbdays = 0; 387 388 for (i = 1; i <= 7; ++i) 389 if (mask & (1 << i)) { 390 if (nbdays > 0) 391 printf(",%s", week_days[i]); 392 else 393 printf("%s", week_days[i]); 394 ++nbdays; 395 } 396 printf(" "); 397} 398 399static inline void divide_time(unsigned int fulltime, unsigned int *hours, 400 unsigned int *minutes, unsigned int *seconds) 401{ 402 *seconds = fulltime % 60; 403 fulltime /= 60; 404 *minutes = fulltime % 60; 405 *hours = fulltime / 60; 406} 407 408static void time_print(const void *ip, const struct xt_entry_match *match, 409 int numeric) 410{ 411 struct xt_time_info *info = (void *)match->data; 412 unsigned int h, m, s; 413 414 printf("TIME "); 415 416 if (info->daytime_start != XT_TIME_MIN_DAYTIME || 417 info->daytime_stop != XT_TIME_MAX_DAYTIME) { 418 divide_time(info->daytime_start, &h, &m, &s); 419 printf("from %02u:%02u:%02u ", h, m, s); 420 divide_time(info->daytime_stop, &h, &m, &s); 421 printf("to %02u:%02u:%02u ", h, m, s); 422 } 423 if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { 424 printf("on "); 425 time_print_weekdays(info->weekdays_match); 426 } 427 if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { 428 printf("on "); 429 time_print_monthdays(info->monthdays_match, true); 430 } 431 if (info->date_start != 0) { 432 printf("starting from "); 433 time_print_date(info->date_start, NULL); 434 } 435 if (info->date_stop != LONG_MAX) { 436 printf("until date "); 437 time_print_date(info->date_stop, NULL); 438 } 439 if (!(info->flags & XT_TIME_LOCAL_TZ)) 440 printf("UTC "); 441} 442 443static void time_save(const void *ip, const struct xt_entry_match *match) 444{ 445 const struct xt_time_info *info = (const void *)match->data; 446 unsigned int h, m, s; 447 448 if (info->daytime_start != XT_TIME_MIN_DAYTIME || 449 info->daytime_stop != XT_TIME_MAX_DAYTIME) { 450 divide_time(info->daytime_start, &h, &m, &s); 451 printf("--timestart %02u:%02u:%02u ", h, m, s); 452 divide_time(info->daytime_stop, &h, &m, &s); 453 printf("--timestop %02u:%02u:%02u ", h, m, s); 454 } 455 if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { 456 printf("--monthdays "); 457 time_print_monthdays(info->monthdays_match, false); 458 } 459 if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { 460 printf("--weekdays "); 461 time_print_weekdays(info->weekdays_match); 462 printf(" "); 463 } 464 time_print_date(info->date_start, "--datestart"); 465 time_print_date(info->date_stop, "--datestop"); 466 if (!(info->flags & XT_TIME_LOCAL_TZ)) 467 printf("--utc "); 468} 469 470static struct xtables_match time_reg = { 471 .name = "time", 472 .family = AF_INET, 473 .version = IPTABLES_VERSION, 474 .size = XT_ALIGN(sizeof(struct xt_time_info)), 475 .userspacesize = XT_ALIGN(sizeof(struct xt_time_info)), 476 .help = time_help, 477 .init = time_init, 478 .parse = time_parse, 479 .final_check = time_check, 480 .print = time_print, 481 .save = time_save, 482 .extra_opts = time_opts, 483}; 484 485static struct xtables_match time6_reg = { 486 .name = "time", 487 .family = AF_INET6, 488 .version = IPTABLES_VERSION, 489 .size = XT_ALIGN(sizeof(struct xt_time_info)), 490 .userspacesize = XT_ALIGN(sizeof(struct xt_time_info)), 491 .help = time_help, 492 .init = time_init, 493 .parse = time_parse, 494 .final_check = time_check, 495 .print = time_print, 496 .save = time_save, 497 .extra_opts = time_opts, 498}; 499 500void _init(void) 501{ 502 xtables_register_match(&time_reg); 503 xtables_register_match(&time6_reg); 504} 505