blob: 989806deb567327c2e9c83884597a3a9a9b71e24 [file] [log] [blame]
Jan Engelhardtad326ef2007-09-23 15:17:42 +00001/*
2 * libxt_time - iptables part for xt_time
Jan Engelhardt032722b2007-10-20 15:17:30 +00003 * Copyright © CC Computer Consultants GmbH, 2007
4 * Contact: <jengelh@computergmbh.de>
Jan Engelhardtad326ef2007-09-23 15:17:42 +00005 *
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>
Jan Engelhardtef18e812008-08-04 12:47:48 +020015#include <stdint.h>
Jan Engelhardtad326ef2007-09-23 15:17:42 +000016#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include <stddef.h>
20#include <time.h>
Phil Oester9a90f902008-09-01 15:07:26 +020021#include <limits.h>
22
Jan Engelhardtad326ef2007-09-23 15:17:42 +000023#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'},
Max Kellermann9ee386a2008-01-29 13:48:05 +000050 { .name = NULL }
Jan Engelhardtad326ef2007-09-23 15:17:42 +000051};
52
53static void time_help(void)
54{
55 printf(
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020056"time match options:\n"
Jan Engelhardt9b488b92008-06-08 19:11:51 +020057" --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");
Jan Engelhardtad326ef2007-09-23 15:17:42 +000067}
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;
Patrick McHardyfceebd82007-10-18 12:34:20 +000081 info->date_stop = INT_MAX;
Jan Engelhardtad326ef2007-09-23 15:17:42 +000082
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
Jan Engelhardtad326ef2007-09-23 15:17:42 +0000332static 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 }
Patrick McHardyfceebd82007-10-18 12:34:20 +0000434 if (info->date_stop != INT_MAX) {
Jan Engelhardtad326ef2007-09-23 15:17:42 +0000435 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
Jan Engelhardt181dead2007-10-04 16:27:07 +0000469static struct xtables_match time_match = {
Jan Engelhardtad326ef2007-09-23 15:17:42 +0000470 .name = "time",
Jan Engelhardt23545c22008-02-14 04:23:04 +0100471 .family = AF_UNSPEC,
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200472 .version = XTABLES_VERSION,
Jan Engelhardtad326ef2007-09-23 15:17:42 +0000473 .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,
Jan Engelhardtad326ef2007-09-23 15:17:42 +0000478 .print = time_print,
479 .save = time_save,
480 .extra_opts = time_opts,
481};
482
483void _init(void)
484{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000485 xtables_register_match(&time_match);
Jan Engelhardtad326ef2007-09-23 15:17:42 +0000486}