unify object freeing
[project/firewall3.git] / utils.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "utils.h"
20 #include "options.h"
21
22 #include "zones.h"
23 #include "ipsets.h"
24
25
26 static int lock_fd = -1;
27 static pid_t pipe_pid = -1;
28 static FILE *pipe_fd = NULL;
29
30 static void
31 warn_elem_section_name(struct uci_section *s, bool find_name)
32 {
33         int i = 0;
34         struct uci_option *o;
35         struct uci_element *tmp;
36
37         if (s->anonymous)
38         {
39                 uci_foreach_element(&s->package->sections, tmp)
40                 {
41                         if (strcmp(uci_to_section(tmp)->type, s->type))
42                                 continue;
43
44                         if (&s->e == tmp)
45                                 break;
46
47                         i++;
48                 }
49
50                 fprintf(stderr, "@%s[%d]", s->type, i);
51
52                 if (find_name)
53                 {
54                         uci_foreach_element(&s->options, tmp)
55                         {
56                                 o = uci_to_option(tmp);
57
58                                 if (!strcmp(tmp->name, "name") && (o->type == UCI_TYPE_STRING))
59                                 {
60                                         fprintf(stderr, " (%s)", o->v.string);
61                                         break;
62                                 }
63                         }
64                 }
65         }
66         else
67         {
68                 fprintf(stderr, "'%s'", s->e.name);
69         }
70
71         if (find_name)
72                 fprintf(stderr, " ");
73 }
74
75 void
76 warn_elem(struct uci_element *e, const char *format, ...)
77 {
78         if (e->type == UCI_TYPE_SECTION)
79         {
80                 fprintf(stderr, "Warning: Section ");
81                 warn_elem_section_name(uci_to_section(e), true);
82         }
83         else if (e->type == UCI_TYPE_OPTION)
84         {
85                 fprintf(stderr, "Warning: Option ");
86                 warn_elem_section_name(uci_to_option(e)->section, false);
87                 fprintf(stderr, ".%s ", e->name);
88         }
89
90     va_list argptr;
91     va_start(argptr, format);
92     vfprintf(stderr, format, argptr);
93     va_end(argptr);
94
95         fprintf(stderr, "\n");
96 }
97
98 void
99 warn(const char* format, ...)
100 {
101         fprintf(stderr, "Warning: ");
102     va_list argptr;
103     va_start(argptr, format);
104     vfprintf(stderr, format, argptr);
105     va_end(argptr);
106         fprintf(stderr, "\n");
107 }
108
109 void
110 error(const char* format, ...)
111 {
112         fprintf(stderr, "Error: ");
113     va_list argptr;
114     va_start(argptr, format);
115     vfprintf(stderr, format, argptr);
116     va_end(argptr);
117         fprintf(stderr, "\n");
118
119         exit(1);
120 }
121
122 void
123 info(const char* format, ...)
124 {
125         va_list argptr;
126     va_start(argptr, format);
127     vfprintf(stderr, format, argptr);
128     va_end(argptr);
129         fprintf(stderr, "\n");
130 }
131
132 const char *
133 fw3_find_command(const char *cmd)
134 {
135         struct stat s;
136         int plen = 0, clen = strlen(cmd) + 1;
137         char *search, *p;
138         static char path[PATH_MAX];
139
140         if (!stat(cmd, &s) && S_ISREG(s.st_mode))
141                 return cmd;
142
143         search = getenv("PATH");
144
145         if (!search)
146                 search = "/bin:/usr/bin:/sbin:/usr/sbin";
147
148         p = search;
149
150         do
151         {
152                 if (*p != ':' && *p != '\0')
153                         continue;
154
155                 plen = p - search;
156
157                 if ((plen + clen) >= sizeof(path))
158                         continue;
159
160                 strncpy(path, search, plen);
161                 sprintf(path + plen, "/%s", cmd);
162
163                 if (!stat(path, &s) && S_ISREG(s.st_mode))
164                         return path;
165
166                 search = p + 1;
167         }
168         while (*p++);
169
170         return NULL;
171 }
172
173 bool
174 fw3_stdout_pipe(void)
175 {
176         pipe_fd = stdout;
177         return true;
178 }
179
180 bool
181 __fw3_command_pipe(bool silent, const char *command, ...)
182 {
183         pid_t pid;
184         va_list argp;
185         int pfds[2];
186         int argn;
187         char *arg, **args, **tmp;
188
189         command = fw3_find_command(command);
190
191         if (!command)
192                 return false;
193
194         if (pipe(pfds))
195                 return false;
196
197         argn = 2;
198         args = malloc(argn * sizeof(arg));
199
200         if (!args)
201                 return false;
202
203         args[0] = (char *)command;
204         args[1] = NULL;
205
206         va_start(argp, command);
207
208         while ((arg = va_arg(argp, char *)) != NULL)
209         {
210                 tmp = realloc(args, ++argn * sizeof(arg));
211
212                 if (!tmp)
213                         break;
214
215                 args = tmp;
216                 args[argn-2] = arg;
217                 args[argn-1] = NULL;
218         }
219
220         va_end(argp);
221
222         switch ((pid = fork()))
223         {
224         case -1:
225                 return false;
226
227         case 0:
228                 dup2(pfds[0], 0);
229
230                 close(pfds[0]);
231                 close(pfds[1]);
232
233                 close(1);
234
235                 if (silent)
236                         close(2);
237
238                 execv(command, args);
239
240         default:
241                 signal(SIGPIPE, SIG_IGN);
242                 pipe_pid = pid;
243                 close(pfds[0]);
244         }
245
246         pipe_fd = fdopen(pfds[1], "w");
247         return true;
248 }
249
250 void
251 fw3_pr(const char *fmt, ...)
252 {
253     va_list args;
254     va_start(args, fmt);
255     vfprintf(pipe_fd, fmt, args);
256     va_end(args);
257 }
258
259 void
260 fw3_command_close(void)
261 {
262         if (pipe_fd && pipe_fd != stdout)
263                 fclose(pipe_fd);
264
265         if (pipe_pid > -1)
266                 waitpid(pipe_pid, NULL, 0);
267
268         signal(SIGPIPE, SIG_DFL);
269
270         pipe_fd = NULL;
271         pipe_pid = -1;
272 }
273
274 bool
275 fw3_has_table(bool ipv6, const char *table)
276 {
277         FILE *f;
278
279         char line[12];
280         bool seen = false;
281
282         const char *path = ipv6
283                 ? "/proc/net/ip6_tables_names" : "/proc/net/ip_tables_names";
284
285         if (!(f = fopen(path, "r")))
286                 return false;
287
288         while (fgets(line, sizeof(line), f))
289         {
290                 if (!strncmp(line, table, strlen(table)))
291                 {
292                         seen = true;
293                         break;
294                 }
295         }
296
297         fclose(f);
298
299         return seen;
300 }
301
302
303 bool
304 fw3_lock(void)
305 {
306         lock_fd = open(FW3_LOCKFILE, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
307
308         if (lock_fd < 0)
309         {
310                 warn("Cannot create lock file %s: %s", FW3_LOCKFILE, strerror(errno));
311                 return false;
312         }
313
314         if (flock(lock_fd, LOCK_EX))
315         {
316                 warn("Cannot acquire exclusive lock: %s", strerror(errno));
317                 return false;
318         }
319
320         return true;
321 }
322
323 void
324 fw3_unlock(void)
325 {
326         if (lock_fd < 0)
327                 return;
328
329         if (flock(lock_fd, LOCK_UN))
330                 warn("Cannot release exclusive lock: %s", strerror(errno));
331
332         close(lock_fd);
333         unlink(FW3_LOCKFILE);
334
335         lock_fd = -1;
336 }
337
338
339 bool
340 fw3_read_statefile(void *state)
341 {
342         FILE *sf;
343
344         int n, type;
345         char line[128];
346         const char *p, *name;
347
348         uint16_t flags[2];
349
350         struct fw3_state *s = state;
351         struct fw3_zone *zone;
352         struct fw3_ipset *ipset;
353
354         sf = fopen(FW3_STATEFILE, "r");
355
356         if (!sf)
357                 return false;
358
359         while (fgets(line, sizeof(line), sf))
360         {
361                 p = strtok(line, " \t\n");
362
363                 if (!p)
364                         continue;
365
366                 type = strtoul(p, NULL, 10);
367                 name = strtok(NULL, " \t\n");
368
369                 if (!name)
370                         continue;
371
372                 for (n = 0, p = strtok(NULL, " \t\n");
373                      n < ARRAY_SIZE(flags) && p != NULL;
374                      n++, p = strtok(NULL, " \t\n"))
375                 {
376                         flags[n] = strtoul(p, NULL, 10);
377                 }
378
379                 switch (type)
380                 {
381                 case FW3_TYPE_DEFAULTS:
382                         s->running_defaults.flags = flags[0];
383                         break;
384
385                 case FW3_TYPE_ZONE:
386                         if (!(zone = fw3_lookup_zone(state, name, false)))
387                         {
388                                 zone = fw3_alloc_zone();
389
390                                 if (!zone)
391                                         continue;
392
393                                 zone->name = strdup(name);
394                                 list_add_tail(&zone->list, &s->zones);
395                         }
396
397                         zone->src_flags = flags[0];
398                         zone->dst_flags = flags[1];
399                         list_add_tail(&zone->running_list, &s->running_zones);
400                         break;
401
402                 case FW3_TYPE_IPSET:
403                         if (!(ipset = fw3_lookup_ipset(state, name, false)))
404                         {
405                                 ipset = fw3_alloc_ipset();
406
407                                 if (!ipset)
408                                         continue;
409
410                                 ipset->name = strdup(name);
411                                 list_add_tail(&ipset->list, &s->ipsets);
412                         }
413
414                         ipset->flags = flags[0];
415                         list_add_tail(&ipset->running_list, &s->running_ipsets);
416                         break;
417                 }
418         }
419
420         fclose(sf);
421
422         return true;
423 }
424
425 void
426 fw3_write_statefile(void *state)
427 {
428         FILE *sf;
429         struct fw3_state *s = state;
430         struct fw3_defaults *d = &s->defaults;
431         struct fw3_zone *z;
432         struct fw3_ipset *i;
433
434         int mask = (1 << FW3_FAMILY_V4) | (1 << FW3_FAMILY_V6);
435
436         if (!(d->flags & mask))
437         {
438                 if (unlink(FW3_STATEFILE))
439                         warn("Unable to remove state %s: %s",
440                              FW3_STATEFILE, strerror(errno));
441
442                 return;
443         }
444
445         sf = fopen(FW3_STATEFILE, "w");
446
447         if (!sf)
448         {
449                 warn("Cannot create state %s: %s", FW3_STATEFILE, strerror(errno));
450                 return;
451         }
452
453         fprintf(sf, "%u - %u\n", FW3_TYPE_DEFAULTS, d->flags);
454
455         list_for_each_entry(z, &s->running_zones, running_list)
456         {
457                 fprintf(sf, "%u %s %u %u\n", FW3_TYPE_ZONE,
458                         z->name, z->src_flags, z->dst_flags);
459         }
460
461         list_for_each_entry(i, &s->running_ipsets, running_list)
462         {
463                 fprintf(sf, "%u %s %u\n", FW3_TYPE_IPSET, i->name, i->flags);
464         }
465
466         fclose(sf);
467 }
468
469
470 struct object_list_heads
471 {
472         struct list_head list;
473         struct list_head running_list;
474 };
475
476 void
477 fw3_set_running(void *object, struct list_head *dest)
478 {
479         struct object_list_heads *o = object;
480
481         if (dest && !o->running_list.next)
482                 list_add_tail(&o->running_list, dest);
483         else if (!dest && o->running_list.next)
484                 list_del(&o->running_list);
485 }
486
487 void
488 fw3_free_object(void *obj, const void *opts)
489 {
490         const struct fw3_option *ol;
491
492         for (ol = opts; ol->name; ol++)
493                 if (ol->elem_size)
494                         fw3_free_list((struct list_head *)((char *)obj + ol->offset));
495
496         free(obj);
497 }