treewide: replace unsafe string functions
[oweals/firewall3.git] / options.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013-2014 Jo-Philipp Wich <jo@mein.io>
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 "options.h"
20 #include "ubus.h"
21
22
23 static bool
24 put_value(void *ptr, void *val, int elem_size, bool is_list)
25 {
26         void *copy;
27
28         if (is_list)
29         {
30                 copy = malloc(elem_size);
31
32                 if (!copy)
33                         return false;
34
35                 memcpy(copy, val, elem_size);
36                 list_add_tail((struct list_head *)copy, (struct list_head *)ptr);
37                 return true;
38         }
39
40         memcpy(ptr, val, elem_size);
41         return false;
42 }
43
44 static bool
45 parse_enum(void *ptr, const char *val, const char **values, int min, int max)
46 {
47         int i, l = strlen(val);
48
49         if (l > 0)
50         {
51                 for (i = 0; i <= (max - min); i++)
52                 {
53                         if (!strncasecmp(val, values[i], l))
54                         {
55                                 *((int *)ptr) = min + i;
56                                 return true;
57                         }
58                 }
59         }
60
61         return false;
62 }
63
64
65 const char *fw3_flag_names[__FW3_FLAG_MAX] = {
66         "filter",
67         "nat",
68         "mangle",
69         "raw",
70
71         "IPv4",
72         "IPv6",
73
74         "ACCEPT",
75         "REJECT",
76         "DROP",
77         "NOTRACK",
78         "HELPER",
79         "MARK",
80         "DSCP",
81         "DNAT",
82         "SNAT",
83         "MASQUERADE",
84
85         "ACCEPT",
86         "REJECT",
87         "DROP",
88 };
89
90 const char *fw3_reject_code_names[__FW3_REJECT_CODE_MAX] = {
91         "tcp-reset",
92         "port-unreach",
93         "adm-prohibited",
94 };
95
96 const char *fw3_limit_units[__FW3_LIMIT_UNIT_MAX] = {
97         "second",
98         "minute",
99         "hour",
100         "day",
101 };
102
103 const char *fw3_ipset_method_names[__FW3_IPSET_METHOD_MAX] = {
104         "(bug)",
105         "bitmap",
106         "hash",
107         "list",
108 };
109
110 const char *fw3_ipset_type_names[__FW3_IPSET_TYPE_MAX] = {
111         "(bug)",
112         "ip",
113         "port",
114         "mac",
115         "net",
116         "set",
117 };
118
119 static const char *weekdays[] = {
120         "monday",
121         "tuesday",
122         "wednesday",
123         "thursday",
124         "friday",
125         "saturday",
126         "sunday",
127 };
128
129 static const char *include_types[] = {
130         "script",
131         "restore",
132 };
133
134 static const char *reflection_sources[] = {
135         "internal",
136         "external",
137 };
138
139 static const struct { const char *name; uint8_t dscp; } dscp_classes[] = {
140         { "CS0",  0x00 },
141         { "CS1",  0x08 },
142         { "CS2",  0x10 },
143         { "CS3",  0x18 },
144         { "CS4",  0x20 },
145         { "CS5",  0x28 },
146         { "CS6",  0x30 },
147         { "CS7",  0x38 },
148         { "BE",   0x00 },
149         { "AF11", 0x0a },
150         { "AF12", 0x0c },
151         { "AF13", 0x0e },
152         { "AF21", 0x12 },
153         { "AF22", 0x14 },
154         { "AF23", 0x16 },
155         { "AF31", 0x1a },
156         { "AF32", 0x1c },
157         { "AF33", 0x1e },
158         { "AF41", 0x22 },
159         { "AF42", 0x24 },
160         { "AF43", 0x26 },
161         { "EF",   0x2e }
162 };
163
164
165 bool
166 fw3_parse_bool(void *ptr, const char *val, bool is_list)
167 {
168         if (!strcmp(val, "true") || !strcmp(val, "yes") || !strcmp(val, "1"))
169                 *((bool *)ptr) = true;
170         else
171                 *((bool *)ptr) = false;
172
173         return true;
174 }
175
176 bool
177 fw3_parse_int(void *ptr, const char *val, bool is_list)
178 {
179         char *e;
180         int n = strtol(val, &e, 0);
181
182         if (e == val || *e)
183                 return false;
184
185         *((int *)ptr) = n;
186
187         return true;
188 }
189
190 bool
191 fw3_parse_string(void *ptr, const char *val, bool is_list)
192 {
193         *((char **)ptr) = (char *)val;
194         return true;
195 }
196
197 bool
198 fw3_parse_target(void *ptr, const char *val, bool is_list)
199 {
200         return parse_enum(ptr, val, &fw3_flag_names[FW3_FLAG_ACCEPT],
201                           FW3_FLAG_ACCEPT, FW3_FLAG_MASQUERADE);
202 }
203
204 bool
205 fw3_parse_reject_code(void *ptr, const char *val, bool is_list)
206 {
207         return parse_enum(ptr, val, &fw3_reject_code_names[FW3_REJECT_CODE_TCP_RESET],
208                           FW3_REJECT_CODE_TCP_RESET, FW3_REJECT_CODE_ADM_PROHIBITED);
209 }
210
211 bool
212 fw3_parse_limit(void *ptr, const char *val, bool is_list)
213 {
214         struct fw3_limit *limit = ptr;
215         enum fw3_limit_unit u = FW3_LIMIT_UNIT_SECOND;
216         char *e;
217         int n;
218
219         if (*val == '!')
220         {
221                 limit->invert = true;
222                 while (isspace(*++val));
223         }
224
225         n = strtol(val, &e, 10);
226
227         if (errno == ERANGE || errno == EINVAL)
228                 return false;
229
230         if (*e && *e++ != '/')
231                 return false;
232
233         if (!strlen(e))
234                 return false;
235
236         if (!parse_enum(&u, e, fw3_limit_units, 0, FW3_LIMIT_UNIT_DAY))
237                 return false;
238
239         limit->rate = n;
240         limit->unit = u;
241
242         return true;
243 }
244
245 bool
246 fw3_parse_device(void *ptr, const char *val, bool is_list)
247 {
248         char *p;
249         struct fw3_device dev = { };
250
251         if (*val == '*')
252         {
253                 dev.set = true;
254                 dev.any = true;
255                 put_value(ptr, &dev, sizeof(dev), is_list);
256                 return true;
257         }
258
259         if (*val == '!')
260         {
261                 dev.invert = true;
262                 while (isspace(*++val));
263         }
264
265         if ((p = strchr(val, '@')) != NULL)
266         {
267                 *p++ = 0;
268                 snprintf(dev.network, sizeof(dev.network), "%s", p);
269         }
270
271         if (*val)
272                 snprintf(dev.name, sizeof(dev.name), "%s", val);
273         else
274                 return false;
275
276         dev.set = true;
277         put_value(ptr, &dev, sizeof(dev), is_list);
278         return true;
279 }
280
281 bool
282 fw3_parse_address(void *ptr, const char *val, bool is_list)
283 {
284         struct fw3_address addr = { };
285         struct in_addr v4;
286         struct in6_addr v6;
287         char *p = NULL, *m = NULL, *s, *e;
288         int bits = -1;
289
290         if (*val == '!')
291         {
292                 addr.invert = true;
293                 while (isspace(*++val));
294         }
295
296         s = strdup(val);
297
298         if (!s)
299                 return false;
300
301         if ((m = strchr(s, '/')) != NULL)
302                 *m++ = 0;
303         else if ((p = strchr(s, '-')) != NULL)
304                 *p++ = 0;
305
306         if (inet_pton(AF_INET6, s, &v6))
307         {
308                 addr.family = FW3_FAMILY_V6;
309                 addr.address.v6 = v6;
310
311                 if (m)
312                 {
313                         if (!inet_pton(AF_INET6, m, &v6))
314                         {
315                                 bits = strtol(m, &e, 10);
316
317                                 if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v6))
318                                         goto fail;
319                         }
320
321                         addr.mask.v6 = v6;
322                 }
323                 else if (p)
324                 {
325                         if (!inet_pton(AF_INET6, p, &addr.mask.v6))
326                                 goto fail;
327
328                         addr.range = true;
329                 }
330                 else
331                 {
332                         memset(addr.mask.v6.s6_addr, 0xFF, 16);
333                 }
334         }
335         else if (inet_pton(AF_INET, s, &v4))
336         {
337                 addr.family = FW3_FAMILY_V4;
338                 addr.address.v4 = v4;
339
340                 if (m)
341                 {
342                         if (!inet_pton(AF_INET, m, &v4))
343                         {
344                                 bits = strtol(m, &e, 10);
345
346                                 if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v4))
347                                         goto fail;
348                         }
349
350                         addr.mask.v4 = v4;
351                 }
352                 else if (p)
353                 {
354                         if (!inet_pton(AF_INET, p, &addr.mask.v4))
355                                 goto fail;
356
357                         addr.range = true;
358                 }
359                 else
360                 {
361                         addr.mask.v4.s_addr = 0xFFFFFFFF;
362                 }
363         }
364         else
365         {
366                 goto fail;
367         }
368
369         free(s);
370         addr.set = true;
371         put_value(ptr, &addr, sizeof(addr), is_list);
372         return true;
373
374 fail:
375         free(s);
376         return false;
377 }
378
379 bool
380 fw3_parse_network(void *ptr, const char *val, bool is_list)
381 {
382         struct fw3_device dev = { };
383         struct fw3_address *addr, *tmp;
384         LIST_HEAD(addr_list);
385         int n_addrs;
386
387         if (!fw3_parse_address(ptr, val, is_list))
388         {
389                 if (!fw3_parse_device(&dev, val, false))
390                         return false;
391
392                 n_addrs = fw3_ubus_address(&addr_list, dev.name);
393
394                 list_for_each_entry(addr, &addr_list, list)
395                 {
396                         addr->invert = dev.invert;
397                         addr->resolved = true;
398                 }
399
400                 /* add an empty address member with .set = false, .resolved = true
401                  * to signal resolving failure to callers */
402                 if (n_addrs == 0)
403                 {
404                         tmp = fw3_alloc(sizeof(*tmp));
405                         tmp->resolved = true;
406
407                         list_add_tail(&tmp->list, &addr_list);
408                 }
409
410                 if (is_list)
411                 {
412                         list_splice_tail(&addr_list, ptr);
413                 }
414                 else if (!list_empty(&addr_list))
415                 {
416                         memcpy(ptr, list_first_entry(&addr_list, typeof(*addr), list),
417                                sizeof(*addr));
418
419                         list_for_each_entry_safe(addr, tmp, &addr_list, list)
420                                 free(addr);
421                 }
422         }
423
424         return true;
425 }
426
427 bool
428 fw3_parse_mac(void *ptr, const char *val, bool is_list)
429 {
430         struct fw3_mac addr = { };
431         struct ether_addr *mac;
432
433         if (*val == '!')
434         {
435                 addr.invert = true;
436                 while (isspace(*++val));
437         }
438
439         if ((mac = ether_aton(val)) != NULL)
440         {
441                 addr.mac = *mac;
442                 addr.set = true;
443
444                 put_value(ptr, &addr, sizeof(addr), is_list);
445                 return true;
446         }
447
448         return false;
449 }
450
451 bool
452 fw3_parse_port(void *ptr, const char *val, bool is_list)
453 {
454         struct fw3_port range = { };
455         uint16_t n;
456         uint16_t m;
457         char *p;
458
459         if (*val == '!')
460         {
461                 range.invert = true;
462                 while (isspace(*++val));
463         }
464
465         n = strtoul(val, &p, 10);
466
467         if (errno == ERANGE || errno == EINVAL)
468                 return false;
469
470         if (*p && *p != '-' && *p != ':')
471                 return false;
472
473         if (*p)
474         {
475                 m = strtoul(++p, NULL, 10);
476
477                 if (errno == ERANGE || errno == EINVAL || m < n)
478                         return false;
479
480                 range.port_min = n;
481                 range.port_max = m;
482         }
483         else
484         {
485                 range.port_min = n;
486                 range.port_max = n;
487         }
488
489         range.set = true;
490         put_value(ptr, &range, sizeof(range), is_list);
491         return true;
492 }
493
494 bool
495 fw3_parse_family(void *ptr, const char *val, bool is_list)
496 {
497         if (!strcmp(val, "any") || !strcmp(val, "*"))
498                 *((enum fw3_family *)ptr) = FW3_FAMILY_ANY;
499         else if (!strcmp(val, "inet") || strrchr(val, '4'))
500                 *((enum fw3_family *)ptr) = FW3_FAMILY_V4;
501         else if (!strcmp(val, "inet6") || strrchr(val, '6'))
502                 *((enum fw3_family *)ptr) = FW3_FAMILY_V6;
503         else
504                 return false;
505
506         return true;
507 }
508
509 bool
510 fw3_parse_icmptype(void *ptr, const char *val, bool is_list)
511 {
512         struct fw3_icmptype icmp = { };
513         bool v4 = false;
514         bool v6 = false;
515         char *p;
516         int i;
517
518         for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v4); i++)
519         {
520                 if (!strcmp(val, fw3_icmptype_list_v4[i].name))
521                 {
522                         icmp.type     = fw3_icmptype_list_v4[i].type;
523                         icmp.code_min = fw3_icmptype_list_v4[i].code_min;
524                         icmp.code_max = fw3_icmptype_list_v4[i].code_max;
525
526                         v4 = true;
527                         break;
528                 }
529         }
530
531         for (i = 0; i < ARRAY_SIZE(fw3_icmptype_list_v6); i++)
532         {
533                 if (!strcmp(val, fw3_icmptype_list_v6[i].name))
534                 {
535                         icmp.type6     = fw3_icmptype_list_v6[i].type;
536                         icmp.code6_min = fw3_icmptype_list_v6[i].code_min;
537                         icmp.code6_max = fw3_icmptype_list_v6[i].code_max;
538
539                         v6 = true;
540                         break;
541                 }
542         }
543
544         if (!v4 && !v6)
545         {
546                 i = strtoul(val, &p, 10);
547
548                 if ((p == val) || (*p != '/' && *p != 0) || (i > 0xFF))
549                         return false;
550
551                 icmp.type = i;
552
553                 if (*p == '/')
554                 {
555                         val = ++p;
556                         i = strtoul(val, &p, 10);
557
558                         if ((p == val) || (*p != 0) || (i > 0xFF))
559                                 return false;
560
561                         icmp.code_min = i;
562                         icmp.code_max = i;
563                 }
564                 else
565                 {
566                         icmp.code_min = 0;
567                         icmp.code_max = 0xFF;
568                 }
569
570                 icmp.type6     = icmp.type;
571                 icmp.code6_min = icmp.code_min;
572                 icmp.code6_max = icmp.code_max;
573
574                 v4 = true;
575                 v6 = true;
576         }
577
578         icmp.family = (v4 && v6) ? FW3_FAMILY_ANY
579                                  : (v6 ? FW3_FAMILY_V6 : FW3_FAMILY_V4);
580
581         put_value(ptr, &icmp, sizeof(icmp), is_list);
582         return true;
583 }
584
585 bool
586 fw3_parse_protocol(void *ptr, const char *val, bool is_list)
587 {
588         struct fw3_protocol proto = { };
589         struct protoent *ent;
590         char *e;
591
592         if (*val == '!')
593         {
594                 proto.invert = true;
595                 while (isspace(*++val));
596         }
597
598         if (!strcmp(val, "all") || !strcmp(val, "any") || !strcmp(val, "*"))
599         {
600                 proto.any = true;
601                 put_value(ptr, &proto, sizeof(proto), is_list);
602                 return true;
603         }
604         else if (!strcmp(val, "icmpv6"))
605         {
606                 val = "ipv6-icmp";
607         }
608         else if (!strcmp(val, "tcpudp"))
609         {
610                 proto.protocol = 6;
611                 if (put_value(ptr, &proto, sizeof(proto), is_list))
612                 {
613                         proto.protocol = 17;
614                         put_value(ptr, &proto, sizeof(proto), is_list);
615                 }
616
617                 return true;
618         }
619
620         ent = getprotobyname(val);
621
622         if (ent)
623         {
624                 proto.protocol = ent->p_proto;
625                 put_value(ptr, &proto, sizeof(proto), is_list);
626                 return true;
627         }
628
629         proto.protocol = strtoul(val, &e, 10);
630
631         if ((e == val) || (*e != 0))
632                 return false;
633
634         put_value(ptr, &proto, sizeof(proto), is_list);
635         return true;
636 }
637
638 bool
639 fw3_parse_ipset_method(void *ptr, const char *val, bool is_list)
640 {
641         return parse_enum(ptr, val, &fw3_ipset_method_names[FW3_IPSET_METHOD_BITMAP],
642                           FW3_IPSET_METHOD_BITMAP, FW3_IPSET_METHOD_LIST);
643 }
644
645 bool
646 fw3_parse_ipset_datatype(void *ptr, const char *val, bool is_list)
647 {
648         struct fw3_ipset_datatype type = { };
649
650         type.dir = "src";
651
652         if (!strncmp(val, "dest_", 5))
653         {
654                 val += 5;
655                 type.dir = "dst";
656         }
657         else if (!strncmp(val, "dst_", 4))
658         {
659                 val += 4;
660                 type.dir = "dst";
661         }
662         else if (!strncmp(val, "src_", 4))
663         {
664                 val += 4;
665                 type.dir = "src";
666         }
667
668         if (parse_enum(&type.type, val, &fw3_ipset_type_names[FW3_IPSET_TYPE_IP],
669                        FW3_IPSET_TYPE_IP, FW3_IPSET_TYPE_SET))
670         {
671                 put_value(ptr, &type, sizeof(type), is_list);
672                 return true;
673         }
674
675         return false;
676 }
677
678 bool
679 fw3_parse_date(void *ptr, const char *val, bool is_list)
680 {
681         unsigned int year = 1970, mon = 1, day = 1, hour = 0, min = 0, sec = 0;
682         struct tm tm = { 0 };
683         time_t ts;
684         char *p;
685
686         year = strtoul(val, &p, 10);
687         if ((*p != '-' && *p) || year < 1970 || year > 2038)
688                 goto fail;
689         else if (!*p)
690                 goto ret;
691
692         mon = strtoul(++p, &p, 10);
693         if ((*p != '-' && *p) || mon > 12)
694                 goto fail;
695         else if (!*p)
696                 goto ret;
697
698         day = strtoul(++p, &p, 10);
699         if ((*p != 'T' && *p) || day > 31)
700                 goto fail;
701         else if (!*p)
702                 goto ret;
703
704         hour = strtoul(++p, &p, 10);
705         if ((*p != ':' && *p) || hour > 23)
706                 goto fail;
707         else if (!*p)
708                 goto ret;
709
710         min = strtoul(++p, &p, 10);
711         if ((*p != ':' && *p) || min > 59)
712                 goto fail;
713         else if (!*p)
714                 goto ret;
715
716         sec = strtoul(++p, &p, 10);
717         if (*p || sec > 59)
718                 goto fail;
719
720 ret:
721         tm.tm_year = year - 1900;
722         tm.tm_mon  = mon - 1;
723         tm.tm_mday = day;
724         tm.tm_hour = hour;
725         tm.tm_min  = min;
726         tm.tm_sec  = sec;
727
728         ts = mktime(&tm) - timezone;
729
730         if (ts >= 0)
731         {
732                 gmtime_r(&ts, (struct tm *)ptr);
733                 return true;
734         }
735
736 fail:
737         return false;
738 }
739
740 bool
741 fw3_parse_time(void *ptr, const char *val, bool is_list)
742 {
743         unsigned int hour = 0, min = 0, sec = 0;
744         char *p;
745
746         hour = strtoul(val, &p, 10);
747         if (*p != ':' || hour > 23)
748                 goto fail;
749
750         min = strtoul(++p, &p, 10);
751         if ((*p != ':' && *p) || min > 59)
752                 goto fail;
753         else if (!*p)
754                 goto ret;
755
756         sec = strtoul(++p, &p, 10);
757         if (*p || sec > 59)
758                 goto fail;
759
760 ret:
761         *((int *)ptr) = 60 * 60 * hour + 60 * min + sec;
762         return true;
763
764 fail:
765         return false;
766 }
767
768 bool
769 fw3_parse_weekdays(void *ptr, const char *val, bool is_list)
770 {
771         unsigned int w = 0;
772         char *p, *s;
773
774         if (*val == '!')
775         {
776                 fw3_setbit(*(uint8_t *)ptr, 0);
777                 while (isspace(*++val));
778         }
779
780         if (!(s = strdup(val)))
781                 return false;
782
783         for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
784         {
785                 if (!parse_enum(&w, p, weekdays, 1, 7))
786                 {
787                         w = strtoul(p, &p, 10);
788
789                         if (*p || w < 1 || w > 7)
790                         {
791                                 free(s);
792                                 return false;
793                         }
794                 }
795
796                 fw3_setbit(*(uint8_t *)ptr, w);
797         }
798
799         free(s);
800         return true;
801 }
802
803 bool
804 fw3_parse_monthdays(void *ptr, const char *val, bool is_list)
805 {
806         unsigned int d;
807         char *p, *s;
808
809         if (*val == '!')
810         {
811                 fw3_setbit(*(uint32_t *)ptr, 0);
812                 while (isspace(*++val));
813         }
814
815         if (!(s = strdup(val)))
816                 return false;
817
818         for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t"))
819         {
820                 d = strtoul(p, &p, 10);
821
822                 if (*p || d < 1 || d > 31)
823                 {
824                         free(s);
825                         return false;
826                 }
827
828                 fw3_setbit(*(uint32_t *)ptr, d);
829         }
830
831         free(s);
832         return true;
833 }
834
835 bool
836 fw3_parse_include_type(void *ptr, const char *val, bool is_list)
837 {
838         return parse_enum(ptr, val, include_types,
839                           FW3_INC_TYPE_SCRIPT, FW3_INC_TYPE_RESTORE);
840 }
841
842 bool
843 fw3_parse_reflection_source(void *ptr, const char *val, bool is_list)
844 {
845         return parse_enum(ptr, val, reflection_sources,
846                           FW3_REFLECTION_INTERNAL, FW3_REFLECTION_EXTERNAL);
847 }
848
849 bool
850 fw3_parse_mark(void *ptr, const char *val, bool is_list)
851 {
852         uint32_t n;
853         char *s, *e;
854         struct fw3_mark *m = ptr;
855
856         if (*val == '!')
857         {
858                 m->invert = true;
859                 while (isspace(*++val));
860         }
861
862         if ((s = strchr(val, '/')) != NULL)
863                 *s++ = 0;
864
865         n = strtoul(val, &e, 0);
866
867         if (e == val || *e)
868                 return false;
869
870         m->mark = n;
871         m->mask = 0xFFFFFFFF;
872
873         if (s)
874         {
875                 n = strtoul(s, &e, 0);
876
877                 if (e == s || *e)
878                         return false;
879
880                 m->mask = n;
881         }
882
883         m->set = true;
884         return true;
885 }
886
887 bool
888 fw3_parse_dscp(void *ptr, const char *val, bool is_list)
889 {
890         uint32_t n;
891         char *e;
892         struct fw3_dscp *d = ptr;
893
894         if (*val == '!')
895         {
896                 d->invert = true;
897                 while (isspace(*++val));
898         }
899
900         for (n = 0; n < sizeof(dscp_classes) / sizeof(dscp_classes[0]); n++)
901         {
902                 if (strcmp(dscp_classes[n].name, val))
903                         continue;
904
905                 d->set = true;
906                 d->dscp = dscp_classes[n].dscp;
907                 return true;
908         }
909
910         n = strtoul(val, &e, 0);
911
912         if (e == val || *e || n > 0x3F)
913                 return false;
914
915         d->set = true;
916         d->dscp = n;
917         return true;
918 }
919
920 bool
921 fw3_parse_setmatch(void *ptr, const char *val, bool is_list)
922 {
923         struct fw3_setmatch *m = ptr;
924         char *p, *s;
925         int i;
926
927         if (*val == '!')
928         {
929                 m->invert = true;
930                 while (isspace(*++val));
931         }
932
933         if (!(s = strdup(val)))
934                 return false;
935
936         if (!(p = strtok(s, " \t")))
937         {
938                 free(s);
939                 return false;
940         }
941
942         snprintf(m->name, sizeof(m->name), "%s", p);
943
944         for (i = 0, p = strtok(NULL, " \t,");
945              i < 3 && p != NULL;
946              i++, p = strtok(NULL, " \t,"))
947         {
948                 if (!strncmp(p, "dest", 4) || !strncmp(p, "dst", 3))
949                         m->dir[i] = "dst";
950                 else if (!strncmp(p, "src", 3))
951                         m->dir[i] = "src";
952         }
953
954         free(s);
955
956         m->set = true;
957         return true;
958 }
959
960 bool
961 fw3_parse_direction(void *ptr, const char *val, bool is_list)
962 {
963         bool *is_out = ptr;
964         bool valid = true;
965
966         if (!strcmp(val, "in") || !strcmp(val, "ingress"))
967                 *is_out = false;
968         else if (!strcmp(val, "out") || !strcmp(val, "egress"))
969                 *is_out = true;
970         else
971                 valid = false;
972
973         return valid;
974 }
975
976 bool
977 fw3_parse_cthelper(void *ptr, const char *val, bool is_list)
978 {
979         struct fw3_cthelpermatch m = { };
980
981         if (*val == '!')
982         {
983                 m.invert = true;
984                 while (isspace(*++val));
985         }
986
987         if (*val)
988         {
989                 m.set = true;
990                 snprintf(m.name, sizeof(m.name), "%s", val);
991                 put_value(ptr, &m, sizeof(m), is_list);
992                 return true;
993         }
994
995         return false;
996 }
997
998 bool
999 fw3_parse_setentry(void *ptr, const char *val, bool is_list)
1000 {
1001         struct fw3_setentry e = { };
1002
1003         e.value = val;
1004         put_value(ptr, &e, sizeof(e), is_list);
1005
1006         return true;
1007 }
1008
1009
1010 bool
1011 fw3_parse_options(void *s, const struct fw3_option *opts,
1012                   struct uci_section *section)
1013 {
1014         char *p, *v;
1015         bool known, inv;
1016         struct uci_element *e, *l;
1017         struct uci_option *o;
1018         const struct fw3_option *opt;
1019         struct list_head *dest;
1020         bool valid = true;
1021
1022         uci_foreach_element(&section->options, e)
1023         {
1024                 o = uci_to_option(e);
1025                 known = false;
1026
1027                 for (opt = opts; opt->name; opt++)
1028                 {
1029                         if (!opt->parse)
1030                                 continue;
1031
1032                         if (strcmp(opt->name, e->name))
1033                                 continue;
1034
1035                         if (o->type == UCI_TYPE_LIST)
1036                         {
1037                                 if (!opt->elem_size)
1038                                 {
1039                                         warn_elem(e, "must not be a list");
1040                                         valid = false;
1041                                 }
1042                                 else
1043                                 {
1044                                         dest = (struct list_head *)((char *)s + opt->offset);
1045
1046                                         uci_foreach_element(&o->v.list, l)
1047                                         {
1048                                                 if (!l->name)
1049                                                         continue;
1050
1051                                                 if (!opt->parse(dest, l->name, true))
1052                                                 {
1053                                                         warn_elem(e, "has invalid value '%s'", l->name);
1054                                                         valid = false;
1055                                                         continue;
1056                                                 }
1057                                         }
1058                                 }
1059                         }
1060                         else
1061                         {
1062                                 v = o->v.string;
1063
1064                                 if (!v)
1065                                         continue;
1066
1067                                 if (!opt->elem_size)
1068                                 {
1069                                         if (!opt->parse((char *)s + opt->offset, o->v.string, false))
1070                                         {
1071                                                 warn_elem(e, "has invalid value '%s'", o->v.string);
1072                                                 valid = false;
1073                                         }
1074                                 }
1075                                 else
1076                                 {
1077                                         inv = false;
1078                                         dest = (struct list_head *)((char *)s + opt->offset);
1079
1080                                         for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
1081                                         {
1082                                                 /* If we encounter a sole "!" token, assume that it
1083                                                  * is meant to be part of the next token, so silently
1084                                                  * skip it and remember the state... */
1085                                                 if (!strcmp(p, "!"))
1086                                                 {
1087                                                         inv = true;
1088                                                         continue;
1089                                                 }
1090
1091                                                 /* The previous token was a sole "!", rewind pointer
1092                                                  * back by one byte to precede the value with an
1093                                                  * exclamation mark which effectively turns
1094                                                  * ("!", "foo") into ("!foo") */
1095                                                 if (inv)
1096                                                 {
1097                                                         *--p = '!';
1098                                                         inv = false;
1099                                                 }
1100
1101                                                 if (!opt->parse(dest, p, true))
1102                                                 {
1103                                                         warn_elem(e, "has invalid value '%s'", p);
1104                                                         valid = false;
1105                                                         continue;
1106                                                 }
1107                                         }
1108
1109                                         /* The last token was a sole "!" without any subsequent
1110                                          * text, so pass it to the option parser as-is. */
1111                                         if (inv && !opt->parse(dest, "!", true))
1112                                         {
1113                                                 warn_elem(e, "has invalid value '%s'", p);
1114                                                 valid = false;
1115                                         }
1116                                 }
1117                         }
1118
1119                         known = true;
1120                         break;
1121                 }
1122
1123                 if (!known)
1124                         warn_elem(e, "is unknown");
1125         }
1126
1127         return valid;
1128 }
1129
1130
1131 bool
1132 fw3_parse_blob_options(void *s, const struct fw3_option *opts,
1133                        struct blob_attr *a, const char *name)
1134 {
1135         char *p, *v, buf[16];
1136         bool known;
1137         unsigned rem, erem;
1138         struct blob_attr *o, *e;
1139         const struct fw3_option *opt;
1140         struct list_head *dest;
1141         bool valid = true;
1142
1143         blobmsg_for_each_attr(o, a, rem)
1144         {
1145                 known = false;
1146
1147                 for (opt = opts; opt->name; opt++)
1148                 {
1149                         if (!opt->parse)
1150                                 continue;
1151
1152                         if (strcmp(opt->name, blobmsg_name(o)))
1153                                 continue;
1154
1155                         if (blobmsg_type(o) == BLOBMSG_TYPE_ARRAY)
1156                         {
1157                                 if (!opt->elem_size)
1158                                 {
1159                                         fprintf(stderr, "%s: '%s' must not be a list\n",
1160                                                 name, opt->name);
1161
1162                                         valid = false;
1163                                 }
1164                                 else
1165                                 {
1166                                         dest = (struct list_head *)((char *)s + opt->offset);
1167
1168                                         blobmsg_for_each_attr(e, o, erem)
1169                                         {
1170                                                 if (blobmsg_type(e) == BLOBMSG_TYPE_INT32) {
1171                                                         snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(e));
1172                                                         v = buf;
1173                                                 } else {
1174                                                         v = blobmsg_get_string(e);
1175                                                 }
1176
1177                                                 if (!opt->parse(dest, v, true))
1178                                                 {
1179                                                         fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1180                                                                 name, opt->name, v);
1181                                                         valid = false;
1182                                                         continue;
1183                                                 }
1184                                         }
1185                                 }
1186                         }
1187                         else
1188                         {
1189                                 if (blobmsg_type(o) == BLOBMSG_TYPE_INT32) {
1190                                         snprintf(buf, sizeof(buf), "%d", blobmsg_get_u32(o));
1191                                         v = buf;
1192                                 } else {
1193                                         v = blobmsg_get_string(o);
1194                                 }
1195
1196                                 if (!v)
1197                                         continue;
1198
1199                                 if (!opt->elem_size)
1200                                 {
1201                                         if (!opt->parse((char *)s + opt->offset, v, false))
1202                                         {
1203                                                 fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1204                                                         name, opt->name, v);
1205                                                 valid = false;
1206                                         }
1207                                 }
1208                                 else
1209                                 {
1210                                         dest = (struct list_head *)((char *)s + opt->offset);
1211
1212                                         for (p = strtok(v, " \t"); p != NULL; p = strtok(NULL, " \t"))
1213                                         {
1214                                                 if (!opt->parse(dest, p, true))
1215                                                 {
1216                                                         fprintf(stderr, "%s: '%s' has invalid value '%s'\n",
1217                                                                 name, opt->name, p);
1218                                                         valid = false;
1219                                                         continue;
1220                                                 }
1221                                         }
1222                                 }
1223                         }
1224
1225                         known = true;
1226                         break;
1227                 }
1228
1229                 if (!known && strcmp(blobmsg_name(o), "type"))
1230                         fprintf(stderr, "%s: '%s' is unknown\n", name, blobmsg_name(o));
1231         }
1232
1233         return valid;
1234 }
1235
1236
1237 const char *
1238 fw3_address_to_string(struct fw3_address *address, bool allow_invert, bool as_cidr)
1239 {
1240         char *p, ip[INET6_ADDRSTRLEN];
1241         static char buf[INET6_ADDRSTRLEN * 2 + 2];
1242         size_t rem = sizeof(buf);
1243         int len;
1244
1245         p = buf;
1246
1247         if (address->invert && allow_invert) {
1248                 *p++ = '!';
1249                 *p = 0;
1250                 rem--;
1251         }
1252
1253         inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1254                   &address->address.v4, ip, sizeof(ip));
1255
1256         len = snprintf(p, rem, "%s", ip);
1257
1258         if (len < 0 || len >= rem)
1259                 return buf;
1260
1261         rem -= len;
1262         p += len;
1263
1264         if (address->range)
1265         {
1266                 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1267                           &address->mask.v4, ip, sizeof(ip));
1268
1269                 snprintf(p, rem, "-%s", ip);
1270         }
1271         else if (!as_cidr)
1272         {
1273                 inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
1274                           &address->mask.v4, ip, sizeof(ip));
1275
1276                 snprintf(p, rem, "/%s", ip);
1277         }
1278         else
1279         {
1280                 snprintf(p, rem, "/%u",
1281                          fw3_netmask2bitlen(address->family, &address->mask.v6));
1282         }
1283
1284         return buf;
1285 }