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