909ca81df7da3b75b2096ec7c18b8be1e1e8ce74
[iptables.git] / extensions / libipt_time.c
1 /* Shared library add-on to iptables to add TIME matching support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7
8 #include <iptables.h>
9 #include <linux/netfilter_ipv4/ipt_time.h>
10 #include <time.h>
11
12 static int globaldays;
13
14 /* Function which prints out usage message. */
15 static void
16 help(void)
17 {
18         printf(
19 "TIME v%s options:\n"
20 " --timestart value --timestop value --days listofdays\n"
21 "          timestart value : HH:MM\n"
22 "          timestop  value : HH:MM\n"
23 "          listofdays value: a list of days to apply -> ie. Mon,Tue,Wed,Thu,Fri. Case sensitive\n",
24 IPTABLES_VERSION);
25 }
26
27 static struct option opts[] = {
28         { "timestart", 1, 0, '1' },
29         { "timestop", 1, 0, '2' },
30         { "days", 1, 0, '3'},
31         {0}
32 };
33
34 /* Initialize the match. */
35 static void
36 init(struct ipt_entry_match *m, unsigned int *nfcache)
37 {
38         /* caching not yet implemented */
39         *nfcache |= NFC_UNKNOWN;
40         globaldays = 0;
41 }
42
43 /**
44  * param: part1, a pointer on a string 2 chars maximum long string, that will contain the hours.
45  * param: part2, a pointer on a string 2 chars maximum long string, that will contain the minutes.
46  * param: str_2_parse, the string to parse.
47  * return: 1 if ok, 0 if error.
48  */
49 static int
50 split_time(char **part1, char **part2, const char *str_2_parse)
51 {
52         unsigned short int i,j=0;
53         char *rpart1 = *part1;
54         char *rpart2 = *part2;
55         unsigned char found_column = 0;
56
57         /* Check the length of the string */
58         if (strlen(str_2_parse) > 5)
59                 return 0;
60         /* parse the first part until the ':' */
61         for (i=0; i<2; i++)
62         {
63                 if (str_2_parse[i] == ':')
64                         found_column = 1;
65                 else
66                         rpart1[i] = str_2_parse[i];
67         }
68         if (!found_column)
69                 i++;
70         j=i;
71         /* parse the second part */
72         for (; i<strlen(str_2_parse); i++)
73         {
74                 rpart2[i-j] = str_2_parse[i];
75         }
76         /* if we are here, format should be ok. */
77         return 1;
78 }
79
80 static void
81 parse_time_string(unsigned int *hour, unsigned int *minute, const char *time)
82 {
83         char *hours;
84         char *minutes;
85
86         hours = (char *)malloc(3);
87         minutes = (char *)malloc(3);
88         bzero((void *)hours, 3);
89         bzero((void *)minutes, 3);
90
91         if (split_time(&hours, &minutes, time) == 1)
92         {
93                 /* if the number starts with 0, replace it with a space else
94                    this string_to_number will interpret it as octal !! */
95                 if ((hours[0] == '0') && (hours[1] != '\0'))
96                         hours[0] = ' ';
97                 if ((minutes[0] == '0') && (minutes[1] != '\0'))
98                         minutes[0] = ' ';
99
100                 if((string_to_number(hours, 0, 23, hour) == -1) ||
101                         (string_to_number(minutes, 0, 59, minute) == -1)) {
102                         *hour = *minute = (-1);
103                 }
104         }
105         if ((*hour != (-1)) && (*minute != (-1))) {
106                 free(hours);
107                 free(minutes);
108                 return;
109         }
110
111         /* If we are here, there was a problem ..*/
112         exit_error(PARAMETER_PROBLEM,
113                    "invalid time `%s' specified, should be HH:MM format", time);
114 }
115
116 /* return 1->ok, return 0->error */
117 static int
118 parse_day(int *days, int from, int to, const char *string)
119 {
120         char *dayread;
121         char *days_str[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
122         unsigned short int days_of_week[7] = {64, 32, 16, 8, 4, 2, 1};
123         unsigned int i;
124
125         dayread = (char *)malloc(4);
126         bzero(dayread, 4);
127         if ((to-from) != 3) {
128                 free(dayread);
129                 return 0;
130         }
131         for (i=from; i<to; i++)
132                 dayread[i-from] = string[i];
133         for (i=0; i<7; i++)
134                 if (strcmp(dayread, days_str[i]) == 0)
135                 {
136                         *days |= days_of_week[i];
137                         free(dayread);
138                         return 1;
139                 }
140         /* if we are here, we didn't read a valid day */
141         free(dayread);
142         return 0;
143 }
144
145 static void
146 parse_days_string(int *days, const char *daystring)
147 {
148         int len;
149         int i=0;
150         char *err = "invalid days `%s' specified, should be Sun,Mon,Tue... format";
151
152         len = strlen(daystring);
153         if (len < 3)
154                 exit_error(PARAMETER_PROBLEM, err, daystring);  
155         while(i<len)
156         {
157                 if (parse_day(days, i, i+3, daystring) == 0)
158                         exit_error(PARAMETER_PROBLEM, err, daystring);
159                 i += 4;
160         }
161 }
162
163 #define IPT_TIME_START 0x01
164 #define IPT_TIME_STOP  0x02
165 #define IPT_TIME_DAYS  0x04
166
167
168 /* Function which parses command options; returns true if it
169    ate an option */
170 static int
171 parse(int c, char **argv, int invert, unsigned int *flags,
172       const struct ipt_entry *entry,
173       unsigned int *nfcache,
174       struct ipt_entry_match **match)
175 {
176         struct ipt_time_info *timeinfo = (struct ipt_time_info *)(*match)->data;
177         int hours, minutes;
178
179         switch (c)
180         {
181                 /* timestart */
182         case '1':
183                 if (invert)
184                         exit_error(PARAMETER_PROBLEM,
185                                    "unexpected '!' with --timestart");
186                 if (*flags & IPT_TIME_START)
187                         exit_error(PARAMETER_PROBLEM,
188                                    "Can't specify --timestart twice");
189                 parse_time_string(&hours, &minutes, optarg);
190                 timeinfo->time_start = (hours * 60) + minutes;
191                 *flags |= IPT_TIME_START;
192                 break;
193                 /* timestop */
194         case '2':
195                 if (invert)
196                         exit_error(PARAMETER_PROBLEM,
197                                    "unexpected '!' with --timestop");
198                 if (*flags & IPT_TIME_STOP)
199                         exit_error(PARAMETER_PROBLEM,
200                                    "Can't specify --timestop twice");
201                 parse_time_string(&hours, &minutes, optarg);
202                 timeinfo->time_stop = (hours * 60) + minutes;
203                 *flags |= IPT_TIME_STOP;
204                 break;
205
206                 /* days */
207         case '3':
208                 if (invert)
209                         exit_error(PARAMETER_PROBLEM,
210                                    "unexpected '!' with --days");
211                 if (*flags & IPT_TIME_DAYS)
212                         exit_error(PARAMETER_PROBLEM,
213                                    "Can't specify --days twice");
214                 parse_days_string(&globaldays, optarg);
215                 timeinfo->days_match = globaldays;
216                 *flags |= IPT_TIME_DAYS;
217                 break;
218         default:
219                 return 0;
220         }
221         return 1;
222 }
223
224 /* Final check; must have specified --timestart --timestop --days. */
225 static void
226 final_check(unsigned int flags)
227 {
228         if (flags != (IPT_TIME_START | IPT_TIME_STOP | IPT_TIME_DAYS))
229                 exit_error(PARAMETER_PROBLEM,
230                            "TIME match: You must specify `--timestart --timestop and --days'");
231 }
232
233
234 static void
235 print_days(int daynum)
236 {
237         char *days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
238         unsigned short int days_of_week[7] = {64, 32, 16, 8, 4, 2, 1};
239         unsigned short int i, nbdays=0;
240
241         for (i=0; i<7; i++) {
242                 if ((days_of_week[i] & daynum) == days_of_week[i])
243                 {
244                         if (nbdays>0)
245                                 printf(",%s", days[i]);
246                         else
247                                 printf("%s", days[i]);
248                         ++nbdays;
249                 }
250         }
251 }
252
253 static void
254 divide_time(int fulltime, int *hours, int *minutes)
255 {
256         *hours = fulltime / 60;
257         *minutes = fulltime % 60;
258 }
259
260 /* Prints out the matchinfo. */
261 static void
262 print(const struct ipt_ip *ip,
263       const struct ipt_entry_match *match,
264       int numeric)
265 {
266         struct ipt_time_info *time = ((struct ipt_time_info *)match->data);
267         int hour_start, hour_stop, minute_start, minute_stop;
268
269         divide_time(time->time_start, &hour_start, &minute_start);
270         divide_time(time->time_stop, &hour_stop, &minute_stop);
271         printf(" TIME from %d:%d to %d:%d on ",
272                hour_start, minute_start,
273                hour_stop, minute_stop);
274         print_days(time->days_match);
275         printf(" ");
276 }
277
278 /* Saves the data in parsable form to stdout. */
279 static void
280 save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
281 {
282         struct ipt_time_info *time = ((struct ipt_time_info *)match->data);
283         int hour_start, hour_stop, minute_start, minute_stop;
284
285         divide_time(time->time_start, &hour_start, &minute_start);
286         divide_time(time->time_stop, &hour_stop, &minute_stop);
287         printf(" --timestart %.2d:%.2d --timestop %.2d:%.2d --days ",
288                hour_start, minute_start,
289                hour_stop, minute_stop);
290         print_days(time->days_match);
291         printf(" ");
292 }
293
294 static
295 struct iptables_match timestruct
296 = { NULL,
297     "time",
298     IPTABLES_VERSION,
299     IPT_ALIGN(sizeof(struct ipt_time_info)),
300     IPT_ALIGN(sizeof(struct ipt_time_info)),
301     &help,
302     &init,
303     &parse,
304     &final_check,
305     &print,
306     &save,
307     opts
308 };
309
310 void _init(void)
311 {
312         register_match(&timestruct);
313 }