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