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