]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/blacklist/bin/conf.c
zfs: merge openzfs/zfs@e61076683
[FreeBSD/FreeBSD.git] / contrib / blacklist / bin / conf.c
1 /*      $NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 christos Exp $       */
2
3 /*-
4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: conf.c,v 1.24 2016/04/04 15:52:56 christos Exp $");
37
38 #include <stdio.h>
39 #ifdef HAVE_LIBUTIL_H
40 #include <libutil.h>
41 #endif
42 #ifdef HAVE_UTIL_H
43 #include <util.h>
44 #endif
45 #include <string.h>
46 #include <ctype.h>
47 #include <inttypes.h>
48 #include <netdb.h>
49 #include <pwd.h>
50 #include <syslog.h>
51 #include <errno.h>
52 #include <stdlib.h>
53 #include <limits.h>
54 #include <ifaddrs.h>
55 #include <arpa/inet.h>
56 #include <netinet/in.h>
57 #include <net/if.h>
58 #include <sys/socket.h>
59
60 #include "bl.h"
61 #include "internal.h"
62 #include "support.h"
63 #include "conf.h"
64
65
66 struct sockaddr_if {
67         uint8_t         sif_len;
68         sa_family_t     sif_family;
69         in_port_t       sif_port;
70         char            sif_name[16];
71 };
72
73 #define SIF_NAME(a) \
74     ((const struct sockaddr_if *)(const void *)(a))->sif_name
75
76 static int conf_is_interface(const char *);
77
78 #define FSTAR   -1
79 #define FEQUAL  -2
80
81 static void
82 advance(char **p)
83 {
84         char *ep = *p;
85         while (*ep && !isspace((unsigned char)*ep))
86                 ep++;
87         while (*ep && isspace((unsigned char)*ep))
88                 *ep++ = '\0';
89         *p = ep;
90 }
91
92 static int
93 conf_getnum(const char *f, size_t l, bool local, void *rp, const char *name,
94     const char *p)
95 {
96         int e;
97         intmax_t im;
98         int *r = rp;
99
100         if (strcmp(p, "*") == 0) {
101                 *r = FSTAR;
102                 return 0;
103         }
104         if (strcmp(p, "=") == 0) {
105                 if (local)
106                         goto out;
107                 *r = FEQUAL;
108                 return 0;
109         }
110
111         im = strtoi(p, NULL, 0, 0, INT_MAX, &e);
112         if (e == 0) {
113                 *r = (int)im;
114                 return 0;
115         }
116
117         if (f == NULL)
118                 return -1;
119         (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l,
120            name,  p);
121         return -1;
122 out:
123         (*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config",
124             __func__, f, l, name);
125         return -1;
126
127 }
128
129 static int
130 conf_getnfail(const char *f, size_t l, bool local, struct conf *c,
131     const char *p)
132 {
133         return conf_getnum(f, l, local, &c->c_nfail, "nfail", p);
134 }
135
136 static int
137 conf_getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p)
138 {
139         int e;
140         char *ep;
141         intmax_t tot, im;
142
143         tot = 0;
144         if (strcmp(p, "*") == 0) {
145                 c->c_duration = FSTAR;
146                 return 0;
147         }
148         if (strcmp(p, "=") == 0) {
149                 if (local)
150                         goto out;
151                 c->c_duration = FEQUAL;
152                 return 0;
153         }
154 again:
155         im = strtoi(p, &ep, 0, 0, INT_MAX, &e);
156
157         if (e == ENOTSUP) {
158                 switch (*ep) {
159                 case 'd':
160                         im *= 24;
161                         /*FALLTHROUGH*/
162                 case 'h':
163                         im *= 60;
164                         /*FALLTHROUGH*/
165                 case 'm':
166                         im *= 60;
167                         /*FALLTHROUGH*/
168                 case 's':
169                         e = 0;
170                         tot += im;
171                         if (ep[1] != '\0') {
172                                 p = ep + 2;
173                                 goto again;
174                         }
175                         break;
176                 }
177         } else
178                 tot = im;
179
180         if (e == 0) {
181                 c->c_duration = (int)tot;
182                 return 0;
183         }
184
185         if (f == NULL)
186                 return -1;
187         (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p);
188         return -1;
189 out:
190         (*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local"
191             " config", __func__, f, l);
192         return -1;
193
194 }
195
196 static int
197 conf_getport(const char *f, size_t l, bool local, void *r, const char *p)
198 {
199         struct servent *sv;
200
201         // XXX: Pass in the proto instead
202         if ((sv = getservbyname(p, "tcp")) != NULL) {
203                 *(int *)r = ntohs(sv->s_port);
204                 return 0;
205         }
206         if ((sv = getservbyname(p, "udp")) != NULL) {
207                 *(int *)r = ntohs(sv->s_port);
208                 return 0;
209         }
210
211         return conf_getnum(f, l, local, r, "service", p);
212 }
213
214 static int
215 conf_getmask(const char *f, size_t l, bool local, const char **p, int *mask)
216 {
217         char *d;
218         const char *s = *p;
219
220         if ((d = strchr(s, ':')) != NULL) {
221                 *d++ = '\0';
222                 *p = d;
223         }
224         if ((d = strchr(s, '/')) == NULL) {
225                 *mask = FSTAR;
226                 return 0;
227         }
228
229         *d++ = '\0';
230         return conf_getnum(f, l, local, mask, "mask", d);
231 }
232
233 static int
234 conf_gethostport(const char *f, size_t l, bool local, struct conf *c,
235     const char *p)
236 {
237         char *d;        // XXX: Ok to write to string.
238         in_port_t *port = NULL;
239         const char *pstr;
240
241         if (strcmp(p, "*") == 0) {
242                 c->c_port = FSTAR;
243                 c->c_lmask = FSTAR;
244                 return 0;
245         }
246
247         if ((d = strchr(p, ']')) != NULL) {
248                 *d++ = '\0';
249                 pstr = d;
250                 p++;
251         } else
252                 pstr = p;
253
254         if (conf_getmask(f, l, local, &pstr, &c->c_lmask) == -1)
255                 goto out;
256
257         if (d) {
258                 struct sockaddr_in6 *sin6 = (void *)&c->c_ss;
259                 if (debug)
260                         (*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p);
261                 if (strcmp(p, "*") != 0) {
262                         if (inet_pton(AF_INET6, p, &sin6->sin6_addr) == -1)
263                                 goto out;
264                         sin6->sin6_family = AF_INET6;
265 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
266                         sin6->sin6_len = sizeof(*sin6);
267 #endif
268                         port = &sin6->sin6_port;
269                 }
270         } else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) {
271                 if (pstr == p)
272                         pstr = "*";
273                 struct sockaddr_in *sin = (void *)&c->c_ss;
274                 struct sockaddr_if *sif = (void *)&c->c_ss;
275                 if (debug)
276                         (*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p);
277                 if (strcmp(p, "*") != 0) {
278                         if (conf_is_interface(p)) {
279                                 if (!local)
280                                         goto out2;
281                                 if (debug)
282                                         (*lfun)(LOG_DEBUG, "%s: interface %s",
283                                             __func__, p);
284                                 if (c->c_lmask != FSTAR)
285                                         goto out1;
286                                 sif->sif_family = AF_MAX;
287                                 strlcpy(sif->sif_name, p,
288                                     sizeof(sif->sif_name));
289 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
290                                 sif->sif_len = sizeof(*sif);
291 #endif
292                                 port = &sif->sif_port;
293                         } else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1)
294                         {
295                                 sin->sin_family = AF_INET;
296 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
297                                 sin->sin_len = sizeof(*sin);
298 #endif
299                                 port = &sin->sin_port;
300                         } else
301                                 goto out;
302                 }
303         }
304
305         if (conf_getport(f, l, local, &c->c_port, pstr) == -1)
306                 return -1;
307
308         if (port && c->c_port != FSTAR && c->c_port != FEQUAL)
309                 *port = htons((in_port_t)c->c_port);
310         return 0;
311 out:
312         (*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, pstr);
313         return -1;
314 out1:
315         (*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with "
316             "interface [%s]", __func__, f, l, c->c_lmask, p);
317         return -1;
318 out2:
319         (*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense "
320             "with remote config [%s]", __func__, f, l, p);
321         return -1;
322 }
323
324 static int
325 conf_getproto(const char *f, size_t l, bool local __unused, struct conf *c,
326     const char *p)
327 {
328         if (strcmp(p, "stream") == 0) {
329                 c->c_proto = IPPROTO_TCP;
330                 return 0;
331         }
332         if (strcmp(p, "dgram") == 0) {
333                 c->c_proto = IPPROTO_UDP;
334                 return 0;
335         }
336         return conf_getnum(f, l, local, &c->c_proto, "protocol", p);
337 }
338
339 static int
340 conf_getfamily(const char *f, size_t l, bool local __unused, struct conf *c,
341     const char *p)
342 {
343         if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) {
344                 c->c_family = p[3] == '6' ? AF_INET6 : AF_INET;
345                 return 0;
346         }
347         return conf_getnum(f, l, local, &c->c_family, "family", p);
348 }
349
350 static int
351 conf_getuid(const char *f, size_t l, bool local __unused, struct conf *c,
352     const char *p)
353 {
354         struct passwd *pw;
355
356         if ((pw = getpwnam(p)) != NULL) {
357                 c->c_uid = (int)pw->pw_uid;
358                 return 0;
359         }
360
361         return conf_getnum(f, l, local, &c->c_uid, "user", p);
362 }
363
364
365 static int
366 conf_getname(const char *f, size_t l, bool local, struct conf *c,
367     const char *p)
368 {
369         if (conf_getmask(f, l, local, &p, &c->c_rmask) == -1)
370                 return -1;
371
372         if (strcmp(p, "*") == 0) {
373                 strlcpy(c->c_name, rulename, CONFNAMESZ);
374                 return 0;
375         }
376
377         if (strcmp(p, "=") == 0) {
378                 if (local)
379                         goto out;
380                 c->c_name[0] = '\0';
381                 return 0;
382         }
383
384         snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p);
385         return 0;
386 out:
387         (*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local"
388             " config", __func__, f, l);
389         return -1;
390 }
391
392 static int
393 getvalue(const char *f, size_t l, bool local, void *r, char **p,
394     int (*fun)(const char *, size_t, bool, struct conf *, const char *))
395 {
396         char *ep = *p;
397
398         advance(p);
399         return (*fun)(f, l, local, r, ep);
400 }
401
402
403 static int
404 conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local)
405 {
406         int e;
407
408         while (*p && isspace((unsigned char)*p))
409                 p++;
410
411         memset(c, 0, sizeof(*c));
412         e = getvalue(f, l, local, c, &p, conf_gethostport);
413         if (e) return -1;
414         e = getvalue(f, l, local, c, &p, conf_getproto);
415         if (e) return -1;
416         e = getvalue(f, l, local, c, &p, conf_getfamily);
417         if (e) return -1;
418         e = getvalue(f, l, local, c, &p, conf_getuid);
419         if (e) return -1;
420         e = getvalue(f, l, local, c, &p, conf_getname);
421         if (e) return -1;
422         e = getvalue(f, l, local, c, &p, conf_getnfail);
423         if (e) return -1;
424         e = getvalue(f, l, local, c, &p, conf_getsecs);
425         if (e) return -1;
426
427         return 0;
428 }
429
430 static int
431 conf_sort(const void *v1, const void *v2)
432 {
433         const struct conf *c1 = v1;
434         const struct conf *c2 = v2;
435
436 #define CMP(a, b, f) \
437         if ((a)->f > (b)->f) return -1; \
438         else if ((a)->f < (b)->f) return 1
439
440         CMP(c1, c2, c_ss.ss_family);
441         CMP(c1, c2, c_lmask);
442         CMP(c1, c2, c_port);
443         CMP(c1, c2, c_proto);
444         CMP(c1, c2, c_family);
445         CMP(c1, c2, c_rmask);
446         CMP(c1, c2, c_uid);
447 #undef CMP
448         return 0;
449 }
450
451 static int
452 conf_is_interface(const char *name)
453 {
454         const struct ifaddrs *ifa;
455
456         for (ifa = ifas; ifa; ifa = ifa->ifa_next)
457                 if (strcmp(ifa->ifa_name, name) == 0)
458                         return 1;
459         return 0;
460 }
461
462 #define MASK(m)  ((uint32_t)~((1 << (32 - (m))) - 1))
463
464 static int
465 conf_amask_eq(const void *v1, const void *v2, size_t len, int mask)
466 {
467         const uint32_t *a1 = v1;
468         const uint32_t *a2 = v2;
469         uint32_t m;
470         int omask = mask;
471
472         len >>= 2;
473         switch (mask) {
474         case FSTAR:
475                 if (memcmp(v1, v2, len) == 0)
476                         return 1;
477                 goto out;
478         case FEQUAL:
479                 (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
480                     mask);
481                 abort();
482         default:
483                 break;
484         }
485
486         for (size_t i = 0; i < len; i++) {
487                 if (mask > 32) {
488                         m = htonl((uint32_t)~0);
489                         mask -= 32;
490                 } else if (mask) {
491                         m = htonl(MASK(mask));
492                         mask = 0;
493                 } else
494                         return 1;
495                 if ((a1[i] & m) != (a2[i] & m))
496                         goto out;
497         }
498         return 1;
499 out:
500         if (debug > 1) {
501                 char b1[256], b2[256];
502                 len <<= 2;
503                 blhexdump(b1, sizeof(b1), "a1", v1, len);
504                 blhexdump(b2, sizeof(b2), "a2", v2, len);
505                 (*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__,
506                     b1, b2, omask);
507         }
508         return 0;
509 }
510
511 /*
512  * Apply the mask to the given address
513  */
514 static void
515 conf_apply_mask(void *v, size_t len, int mask)
516 {
517         uint32_t *a = v;
518         uint32_t m;
519
520         switch (mask) {
521         case FSTAR:
522                 return;
523         case FEQUAL:
524                 (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
525                     mask);
526                 abort();
527         default:
528                 break;
529         }
530         len >>= 2;
531
532         for (size_t i = 0; i < len; i++) {
533                 if (mask > 32) {
534                         m = htonl((uint32_t)~0);
535                         mask -= 32;
536                 } else if (mask) {
537                         m = htonl(MASK(mask));
538                         mask = 0;
539                 } else
540                         m = 0;
541                 a[i] &= m;
542         }
543 }
544
545 /*
546  * apply the mask and the port to the address given
547  */
548 static void
549 conf_addr_set(struct conf *c, const struct sockaddr_storage *ss)
550 {
551         struct sockaddr_in *sin;
552         struct sockaddr_in6 *sin6;
553         in_port_t *port;
554         void *addr;
555         size_t alen;
556
557         c->c_lmask = c->c_rmask;
558         c->c_ss = *ss;
559
560         if (c->c_ss.ss_family != c->c_family) {
561                 (*lfun)(LOG_CRIT, "%s: Internal error: mismatched family "
562                     "%u != %u", __func__, c->c_ss.ss_family, c->c_family);
563                 abort();
564         }
565
566         switch (c->c_ss.ss_family) {
567         case AF_INET:
568                 sin = (void *)&c->c_ss;
569                 port = &sin->sin_port;
570                 addr = &sin->sin_addr;
571                 alen = sizeof(sin->sin_addr);
572                 break;
573         case AF_INET6:
574                 sin6 = (void *)&c->c_ss;
575                 port = &sin6->sin6_port;
576                 addr = &sin6->sin6_addr;
577                 alen = sizeof(sin6->sin6_addr);
578                 break;
579         default:
580                 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
581                     __func__, c->c_ss.ss_family);
582                 abort();
583         }
584
585         *port = htons((in_port_t)c->c_port);
586         conf_apply_mask(addr, alen, c->c_lmask);
587         if (c->c_lmask == FSTAR)
588                 c->c_lmask = (int)(alen * 8);
589         if (debug) {
590                 char buf[128];
591                 sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss);
592                 (*lfun)(LOG_DEBUG, "Applied address %s", buf);
593         }
594 }
595
596 /*
597  * Compared two addresses for equality applying the mask
598  */
599 static int
600 conf_inet_eq(const void *v1, const void *v2, int mask)
601 {
602         const struct sockaddr *sa1 = v1;
603         const struct sockaddr *sa2 = v2;
604         size_t size;
605
606         if (sa1->sa_family != sa2->sa_family)
607                 return 0;
608
609         switch (sa1->sa_family) {
610         case AF_INET: {
611                 const struct sockaddr_in *s1 = v1;
612                 const struct sockaddr_in *s2 = v2;
613                 size = sizeof(s1->sin_addr);
614                 v1 = &s1->sin_addr;
615                 v2 = &s2->sin_addr;
616                 break;
617         }
618
619         case AF_INET6: {
620                 const struct sockaddr_in6 *s1 = v1;
621                 const struct sockaddr_in6 *s2 = v2;
622                 size = sizeof(s1->sin6_addr);
623                 v1 = &s1->sin6_addr;
624                 v2 = &s2->sin6_addr;
625                 break;
626         }
627
628         default:
629                 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
630                     __func__, sa1->sa_family);
631                 abort();
632         }
633
634         return conf_amask_eq(v1, v2, size, mask);
635 }
636
637 static int
638 conf_addr_in_interface(const struct sockaddr_storage *s1,
639     const struct sockaddr_storage *s2, int mask)
640 {
641         const char *name = SIF_NAME(s2);
642         const struct ifaddrs *ifa;
643
644         for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
645                 if ((ifa->ifa_flags & IFF_UP) == 0)
646                         continue;
647
648                 if (strcmp(ifa->ifa_name, name) != 0)
649                         continue;
650
651                 if (s1->ss_family != ifa->ifa_addr->sa_family)
652                         continue;
653
654                 bool eq;
655                 switch (s1->ss_family) {
656                 case AF_INET:
657                 case AF_INET6:
658                         eq = conf_inet_eq(ifa->ifa_addr, s1, mask);
659                         break;
660                 default:
661                         (*lfun)(LOG_ERR, "Bad family %u", s1->ss_family);
662                         continue;
663                 }
664                 if (eq)
665                         return 1;
666         }
667         return 0;
668 }
669
670 static int
671 conf_addr_eq(const struct sockaddr_storage *s1,
672     const struct sockaddr_storage *s2, int mask)
673 {
674         switch (s2->ss_family) {
675         case 0:
676                 return 1;
677         case AF_MAX:
678                 return conf_addr_in_interface(s1, s2, mask);
679         case AF_INET:
680         case AF_INET6:
681                 return conf_inet_eq(s1, s2, mask);
682         default:
683                 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
684                     __func__, s1->ss_family);
685                 abort();
686         }
687 }
688
689 static int
690 conf_eq(const struct conf *c1, const struct conf *c2)
691 {
692
693         if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
694                 return 0;
695
696 #define CMP(a, b, f) \
697         if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \
698                 if (debug > 1) \
699                         (*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \
700                             __STRING(f), (a)->f, (b)->f); \
701                 return 0; \
702         }
703         CMP(c1, c2, c_port);
704         CMP(c1, c2, c_proto);
705         CMP(c1, c2, c_family);
706         CMP(c1, c2, c_uid);
707 #undef CMP
708         return 1;
709 }
710
711 static const char *
712 conf_num(char *b, size_t l, int n)
713 {
714         switch (n) {
715         case FSTAR:
716                 return "*";
717         case FEQUAL:
718                 return "=";
719         default:
720                 snprintf(b, l, "%d", n);
721                 return b;
722         }
723 }
724
725 static const char *
726 fmtname(const char *n) {
727         size_t l = strlen(rulename);
728         if (l == 0)
729                 return "*";
730         if (strncmp(n, rulename, l) == 0) {
731                 if (n[l] != '\0')
732                         return n + l;
733                 else
734                         return "*";
735         } else if (!*n)
736                 return "=";
737         else
738                 return n;
739 }
740
741 static void
742 fmtport(char *b, size_t l, int port)
743 {
744         char buf[128];
745
746         if (port == FSTAR)
747                 return;
748
749         if (b[0] == '\0' || strcmp(b, "*") == 0)
750                 snprintf(b, l, "%d", port);
751         else {
752                 snprintf(buf, sizeof(buf), ":%d", port);
753                 strlcat(b, buf, l);
754         }
755 }
756
757 static const char *
758 fmtmask(char *b, size_t l, int fam, int mask)
759 {
760         char buf[128];
761
762         switch (mask) {
763         case FSTAR:
764                 return "";
765         case FEQUAL:
766                 if (strcmp(b, "=") == 0)
767                         return "";
768                 else {
769                         strlcat(b, "/=", l);
770                         return b;
771                 }
772         default:
773                 break;
774         }
775
776         switch (fam) {
777         case AF_INET:
778                 if (mask == 32)
779                         return "";
780                 break;
781         case AF_INET6:
782                 if (mask == 128)
783                         return "";
784                 break;
785         default:
786                 break;
787         }
788
789         snprintf(buf, sizeof(buf), "/%d", mask);
790         strlcat(b, buf, l);
791         return b;
792 }
793
794 static const char *
795 conf_namemask(char *b, size_t l, const struct conf *c)
796 {
797         strlcpy(b, fmtname(c->c_name), l);
798         fmtmask(b, l, c->c_family, c->c_rmask);
799         return b;
800 }
801
802 const char *
803 conf_print(char *buf, size_t len, const char *pref, const char *delim,
804     const struct conf *c)
805 {
806         char ha[128], hb[32], b[5][64];
807         int sp;
808
809 #define N(n, v) conf_num(b[n], sizeof(b[n]), (v))
810
811         switch (c->c_ss.ss_family) {
812         case 0:
813                 snprintf(ha, sizeof(ha), "*");
814                 break;
815         case AF_MAX:
816                 snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss));
817                 break;
818         default:
819                 sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss);
820                 break;
821         }
822
823         fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask);
824         fmtport(ha, sizeof(ha), c->c_port);
825
826         sp = *delim == '\t' ? 20 : -1;
827         hb[0] = '\0';
828         if (*delim)
829                 snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s"
830                     "%s%s" "%s%s%s",
831                     pref, sp, sp, ha, delim, N(0, c->c_proto), delim,
832                     N(1, c->c_family), delim, N(2, c->c_uid), delim,
833                     conf_namemask(hb, sizeof(hb), c), delim,
834                     N(3, c->c_nfail), delim, N(4, c->c_duration));
835         else
836                 snprintf(buf, len, "%starget:%s, proto:%s, family:%s, "
837                     "uid:%s, name:%s, nfail:%s, duration:%s", pref,
838                     ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid),
839                     conf_namemask(hb, sizeof(hb), c),
840                     N(3, c->c_nfail), N(4, c->c_duration));
841         return buf;
842 }
843
844 /*
845  * Apply the local config match to the result
846  */
847 static void
848 conf_apply(struct conf *c, const struct conf *sc)
849 {
850         char buf[BUFSIZ];
851
852         if (debug) {
853                 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
854                     conf_print(buf, sizeof(buf), "merge:\t", "", sc));
855                 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
856                     conf_print(buf, sizeof(buf), "to:\t", "", c));
857         }
858         memcpy(c->c_name, sc->c_name, CONFNAMESZ);
859         c->c_uid = sc->c_uid;
860         c->c_rmask = sc->c_rmask;
861         c->c_nfail = sc->c_nfail;
862         c->c_duration = sc->c_duration;
863
864         if (debug)
865                 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
866                     conf_print(buf, sizeof(buf), "result:\t", "", c));
867 }
868
869 /*
870  * Merge a remote configuration to the result
871  */
872 static void
873 conf_merge(struct conf *c, const struct conf *sc)
874 {
875         char buf[BUFSIZ];
876
877         if (debug) {
878                 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
879                     conf_print(buf, sizeof(buf), "merge:\t", "", sc));
880                 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
881                     conf_print(buf, sizeof(buf), "to:\t", "", c));
882         }
883
884         if (sc->c_name[0])
885                 memcpy(c->c_name, sc->c_name, CONFNAMESZ);
886         if (sc->c_uid != FEQUAL)
887                 c->c_uid = sc->c_uid;
888         if (sc->c_rmask != FEQUAL)
889                 c->c_lmask = c->c_rmask = sc->c_rmask;
890         if (sc->c_nfail != FEQUAL)
891                 c->c_nfail = sc->c_nfail;
892         if (sc->c_duration != FEQUAL)
893                 c->c_duration = sc->c_duration;
894         if (debug)
895                 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
896                     conf_print(buf, sizeof(buf), "result:\t", "", c));
897 }
898
899 static void
900 confset_init(struct confset *cs)
901 {
902         cs->cs_c = NULL;
903         cs->cs_n = 0;
904         cs->cs_m = 0;
905 }
906
907 static int
908 confset_grow(struct confset *cs)
909 {
910         void *tc;
911
912         cs->cs_m += 10;
913         tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c));
914         if (tc == NULL) {
915                 (*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__);
916                 return -1;
917         }
918         cs->cs_c = tc;
919         return 0;
920 }
921
922 static struct conf *
923 confset_get(struct confset *cs)
924 {
925         return &cs->cs_c[cs->cs_n];
926 }
927
928 static bool
929 confset_full(const struct confset *cs)
930 {
931         return cs->cs_n == cs->cs_m;
932 }
933
934 static void
935 confset_sort(struct confset *cs)
936 {
937         qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort);
938 }
939
940 static void
941 confset_add(struct confset *cs)
942 {
943         cs->cs_n++;
944 }
945
946 static void
947 confset_free(struct confset *cs)
948 {
949         free(cs->cs_c);
950         confset_init(cs);
951 }
952
953 static void
954 confset_replace(struct confset *dc, struct confset *sc)
955 {
956         struct confset tc;
957         tc = *dc;
958         *dc = *sc;
959         confset_init(sc);
960         confset_free(&tc);
961 }
962
963 static void
964 confset_list(const struct confset *cs, const char *msg, const char *where)
965 {
966         char buf[BUFSIZ];
967
968         (*lfun)(LOG_DEBUG, "[%s]", msg);
969         (*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration",
970             where);
971         for (size_t i = 0; i < cs->cs_n; i++)
972                 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t",
973                     &cs->cs_c[i]));
974 }
975
976 /*
977  * Match a configuration against the given list and apply the function
978  * to it, returning the matched entry number.
979  */
980 static size_t
981 confset_match(const struct confset *cs, struct conf *c,
982     void (*fun)(struct conf *, const struct conf *))
983 {
984         char buf[BUFSIZ];
985         size_t i;
986
987         for (i = 0; i < cs->cs_n; i++) {
988                 if (debug)
989                         (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
990                             "check:\t", "", &cs->cs_c[i]));
991                 if (conf_eq(c, &cs->cs_c[i])) {
992                         if (debug)
993                                 (*lfun)(LOG_DEBUG, "%s",
994                                     conf_print(buf, sizeof(buf),
995                                     "found:\t", "", &cs->cs_c[i]));
996                         (*fun)(c, &cs->cs_c[i]);
997                         break;
998                 }
999         }
1000         return i;
1001 }
1002
1003 const struct conf *
1004 conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss,
1005     struct conf *cr)
1006 {
1007         int proto;
1008         socklen_t slen;
1009         struct sockaddr_storage lss;
1010         size_t i;
1011         char buf[BUFSIZ];
1012
1013         memset(cr, 0, sizeof(*cr));
1014         slen = sizeof(lss);
1015         memset(&lss, 0, slen);
1016         if (getsockname(fd, (void *)&lss, &slen) == -1) {
1017                 (*lfun)(LOG_ERR, "getsockname failed (%m)");
1018                 return NULL;
1019         }
1020
1021         slen = sizeof(proto);
1022         if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) {
1023                 (*lfun)(LOG_ERR, "getsockopt failed (%m)");
1024                 return NULL;
1025         }
1026
1027         if (debug) {
1028                 sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&lss);
1029                 (*lfun)(LOG_DEBUG, "listening socket: %s", buf);
1030         }
1031
1032         switch (proto) {
1033         case SOCK_STREAM:
1034                 cr->c_proto = IPPROTO_TCP;
1035                 break;
1036         case SOCK_DGRAM:
1037                 cr->c_proto = IPPROTO_UDP;
1038                 break;
1039         default:
1040                 (*lfun)(LOG_ERR, "unsupported protocol %d", proto);
1041                 return NULL;
1042         }
1043
1044         switch (lss.ss_family) {
1045         case AF_INET:
1046                 cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port);
1047                 break;
1048         case AF_INET6:
1049                 cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port);
1050                 break;
1051         default:
1052                 (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family);
1053                 return NULL;
1054         }
1055
1056         cr->c_ss = lss;
1057         cr->c_lmask = FSTAR;
1058         cr->c_uid = (int)uid;
1059         cr->c_family = lss.ss_family;
1060         cr->c_name[0] = '\0';
1061         cr->c_rmask = FSTAR;
1062         cr->c_nfail = FSTAR;
1063         cr->c_duration = FSTAR;
1064
1065         if (debug)
1066                 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
1067                     "look:\t", "", cr));
1068
1069         /* match the local config */
1070         i = confset_match(&lconf, cr, conf_apply);
1071         if (i == lconf.cs_n) {
1072                 if (debug)
1073                         (*lfun)(LOG_DEBUG, "not found");
1074                 return NULL;
1075         }
1076
1077         conf_addr_set(cr, rss);
1078         /* match the remote config */
1079         confset_match(&rconf, cr, conf_merge);
1080         /* to apply the mask */
1081         conf_addr_set(cr, &cr->c_ss);
1082
1083         return cr;
1084 }
1085
1086
1087 void
1088 conf_parse(const char *f)
1089 {
1090         FILE *fp;
1091         char *line;
1092         size_t lineno, len;
1093         struct confset lc, rc, *cs;
1094
1095         if ((fp = fopen(f, "r")) == NULL) {
1096                 (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__, f);
1097                 return;
1098         }
1099
1100         lineno = 1;
1101
1102         confset_init(&rc);
1103         confset_init(&lc);
1104         cs = &lc;
1105         for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL;
1106             free(line))
1107         {
1108                 if (!*line)
1109                         continue;
1110                 if (strcmp(line, "[local]") == 0) {
1111                         cs = &lc;
1112                         continue;
1113                 }
1114                 if (strcmp(line, "[remote]") == 0) {
1115                         cs = &rc;
1116                         continue;
1117                 }
1118
1119                 if (confset_full(cs)) {
1120                         if (confset_grow(cs) == -1) {
1121                                 confset_free(&lc);
1122                                 confset_free(&rc);
1123                                 fclose(fp);
1124                                 free(line);
1125                                 return;
1126                         }
1127                 }
1128                 if (conf_parseline(f, lineno, line, confset_get(cs),
1129                     cs == &lc) == -1)
1130                         continue;
1131                 confset_add(cs);
1132         }
1133
1134         fclose(fp);
1135         confset_sort(&lc);
1136         confset_sort(&rc);
1137
1138         confset_replace(&rconf, &rc);
1139         confset_replace(&lconf, &lc);
1140
1141         if (debug) {
1142                 confset_list(&lconf, "local", "target");
1143                 confset_list(&rconf, "remote", "source");
1144         }
1145 }