libxt_time.c revision b1319cc083de658c0007da93f25d19874f75d55f
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 /* 140 * Offsetting, if any, is done by xt_time.ko, 141 * so we have to disable it here in userspace. 142 */ 143 setenv("TZ", "UTC", true); 144 tzset(); 145 ret = mktime(&tm); 146 if (ret >= 0) 147 return ret; 148 perror("mktime"); 149 xtables_error(OTHER_PROBLEM, "mktime returned an error"); 150 151 out: 152 xtables_error(PARAMETER_PROBLEM, "Invalid date \"%s\" specified. Should " 153 "be YYYY[-MM[-DD[Thh[:mm[:ss]]]]]", os); 154 return -1; 155} 156 157static unsigned int time_parse_minutes(const char *s) 158{ 159 unsigned int hour, minute, second = 0; 160 char *e; 161 162 hour = strtoul(s, &e, 10); 163 if (*e != ':' || hour > 23) 164 goto out; 165 166 s = e + 1; 167 minute = strtoul(s, &e, 10); 168 if ((*e != ':' && *e != '\0') || minute > 59) 169 goto out; 170 if (*e == '\0') 171 goto eval; 172 173 s = e + 1; 174 second = strtoul(s, &e, 10); 175 if (*e != '\0' || second > 59) 176 goto out; 177 178 eval: 179 return 60 * 60 * hour + 60 * minute + second; 180 181 out: 182 xtables_error(PARAMETER_PROBLEM, "invalid time \"%s\" specified, " 183 "should be hh:mm[:ss] format and within the boundaries", s); 184 return -1; 185} 186 187static const char *my_strseg(char *buf, unsigned int buflen, 188 const char **arg, char delim) 189{ 190 const char *sep; 191 192 if (*arg == NULL || **arg == '\0') 193 return NULL; 194 sep = strchr(*arg, delim); 195 if (sep == NULL) { 196 snprintf(buf, buflen, "%s", *arg); 197 *arg = NULL; 198 return buf; 199 } 200 snprintf(buf, buflen, "%.*s", (unsigned int)(sep - *arg), *arg); 201 *arg = sep + 1; 202 return buf; 203} 204 205static uint32_t time_parse_monthdays(const char *arg) 206{ 207 char day[3], *err = NULL; 208 uint32_t ret = 0; 209 unsigned int i; 210 211 while (my_strseg(day, sizeof(day), &arg, ',') != NULL) { 212 i = strtoul(day, &err, 0); 213 if ((*err != ',' && *err != '\0') || i > 31) 214 xtables_error(PARAMETER_PROBLEM, 215 "%s is not a valid day for --monthdays", day); 216 ret |= 1 << i; 217 } 218 219 return ret; 220} 221 222static unsigned int time_parse_weekdays(const char *arg) 223{ 224 char day[4], *err = NULL; 225 unsigned int i, ret = 0; 226 bool valid; 227 228 while (my_strseg(day, sizeof(day), &arg, ',') != NULL) { 229 i = strtoul(day, &err, 0); 230 if (*err == '\0') { 231 if (i == 0) 232 xtables_error(PARAMETER_PROBLEM, 233 "No, the week does NOT begin with Sunday."); 234 ret |= 1 << i; 235 continue; 236 } 237 238 valid = false; 239 for (i = 1; i < ARRAY_SIZE(week_days); ++i) 240 if (strncmp(day, week_days[i], 2) == 0) { 241 ret |= 1 << i; 242 valid = true; 243 } 244 245 if (!valid) 246 xtables_error(PARAMETER_PROBLEM, 247 "%s is not a valid day specifier", day); 248 } 249 250 return ret; 251} 252 253static void time_parse(struct xt_option_call *cb) 254{ 255 struct xt_time_info *info = cb->data; 256 257 xtables_option_parse(cb); 258 switch (cb->entry->id) { 259 case O_DATE_START: 260 info->date_start = time_parse_date(cb->arg, false); 261 break; 262 case O_DATE_STOP: 263 info->date_stop = time_parse_date(cb->arg, true); 264 break; 265 case O_TIME_START: 266 info->daytime_start = time_parse_minutes(cb->arg); 267 break; 268 case O_TIME_STOP: 269 info->daytime_stop = time_parse_minutes(cb->arg); 270 break; 271 case O_LOCAL_TZ: 272 info->flags |= XT_TIME_LOCAL_TZ; 273 break; 274 case O_MONTHDAYS: 275 info->monthdays_match = time_parse_monthdays(cb->arg); 276 if (cb->invert) 277 info->monthdays_match ^= XT_TIME_ALL_MONTHDAYS; 278 break; 279 case O_WEEKDAYS: 280 info->weekdays_match = time_parse_weekdays(cb->arg); 281 if (cb->invert) 282 info->weekdays_match ^= XT_TIME_ALL_WEEKDAYS; 283 break; 284 case O_UTC: 285 info->flags &= ~XT_TIME_LOCAL_TZ; 286 break; 287 } 288} 289 290static void time_print_date(time_t date, const char *command) 291{ 292 struct tm *t; 293 294 /* If it is the default value, do not print it. */ 295 if (date == 0 || date == LONG_MAX) 296 return; 297 298 t = gmtime(&date); 299 if (command != NULL) 300 /* 301 * Need a contiguous string (no whitespaces), hence using 302 * the ISO 8601 "T" variant. 303 */ 304 printf(" %s %04u-%02u-%02uT%02u:%02u:%02u", 305 command, t->tm_year + 1900, t->tm_mon + 1, 306 t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); 307 else 308 printf(" %04u-%02u-%02u %02u:%02u:%02u", 309 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, 310 t->tm_hour, t->tm_min, t->tm_sec); 311} 312 313static void time_print_monthdays(uint32_t mask, bool human_readable) 314{ 315 unsigned int i, nbdays = 0; 316 317 printf(" "); 318 for (i = 1; i <= 31; ++i) 319 if (mask & (1 << i)) { 320 if (nbdays++ > 0) 321 printf(","); 322 printf("%u", i); 323 if (human_readable) 324 switch (i % 10) { 325 case 1: 326 printf("st"); 327 break; 328 case 2: 329 printf("nd"); 330 break; 331 case 3: 332 printf("rd"); 333 break; 334 default: 335 printf("th"); 336 break; 337 } 338 } 339} 340 341static void time_print_weekdays(unsigned int mask) 342{ 343 unsigned int i, nbdays = 0; 344 345 printf(" "); 346 for (i = 1; i <= 7; ++i) 347 if (mask & (1 << i)) { 348 if (nbdays > 0) 349 printf(",%s", week_days[i]); 350 else 351 printf("%s", week_days[i]); 352 ++nbdays; 353 } 354} 355 356static inline void divide_time(unsigned int fulltime, unsigned int *hours, 357 unsigned int *minutes, unsigned int *seconds) 358{ 359 *seconds = fulltime % 60; 360 fulltime /= 60; 361 *minutes = fulltime % 60; 362 *hours = fulltime / 60; 363} 364 365static void time_print(const void *ip, const struct xt_entry_match *match, 366 int numeric) 367{ 368 const struct xt_time_info *info = (const void *)match->data; 369 unsigned int h, m, s; 370 371 printf(" TIME"); 372 373 if (info->daytime_start != XT_TIME_MIN_DAYTIME || 374 info->daytime_stop != XT_TIME_MAX_DAYTIME) { 375 divide_time(info->daytime_start, &h, &m, &s); 376 printf(" from %02u:%02u:%02u", h, m, s); 377 divide_time(info->daytime_stop, &h, &m, &s); 378 printf(" to %02u:%02u:%02u", h, m, s); 379 } 380 if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { 381 printf(" on"); 382 time_print_weekdays(info->weekdays_match); 383 } 384 if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { 385 printf(" on"); 386 time_print_monthdays(info->monthdays_match, true); 387 } 388 if (info->date_start != 0) { 389 printf(" starting from"); 390 time_print_date(info->date_start, NULL); 391 } 392 if (info->date_stop != INT_MAX) { 393 printf(" until date"); 394 time_print_date(info->date_stop, NULL); 395 } 396 if (!(info->flags & XT_TIME_LOCAL_TZ)) 397 printf(" UTC"); 398} 399 400static void time_save(const void *ip, const struct xt_entry_match *match) 401{ 402 const struct xt_time_info *info = (const void *)match->data; 403 unsigned int h, m, s; 404 405 if (info->daytime_start != XT_TIME_MIN_DAYTIME || 406 info->daytime_stop != XT_TIME_MAX_DAYTIME) { 407 divide_time(info->daytime_start, &h, &m, &s); 408 printf(" --timestart %02u:%02u:%02u", h, m, s); 409 divide_time(info->daytime_stop, &h, &m, &s); 410 printf(" --timestop %02u:%02u:%02u", h, m, s); 411 } 412 if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { 413 printf(" --monthdays"); 414 time_print_monthdays(info->monthdays_match, false); 415 } 416 if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { 417 printf(" --weekdays"); 418 time_print_weekdays(info->weekdays_match); 419 } 420 time_print_date(info->date_start, "--datestart"); 421 time_print_date(info->date_stop, "--datestop"); 422 if (!(info->flags & XT_TIME_LOCAL_TZ)) 423 printf(" --utc"); 424} 425 426static struct xtables_match time_match = { 427 .name = "time", 428 .family = NFPROTO_UNSPEC, 429 .version = XTABLES_VERSION, 430 .size = XT_ALIGN(sizeof(struct xt_time_info)), 431 .userspacesize = XT_ALIGN(sizeof(struct xt_time_info)), 432 .help = time_help, 433 .init = time_init, 434 .print = time_print, 435 .save = time_save, 436 .x6_parse = time_parse, 437 .x6_options = time_opts, 438}; 439 440void _init(void) 441{ 442 xtables_register_match(&time_match); 443} 444