Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/blocklist/bin/conf.c
105152 views
1
/* $NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 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
#ifdef HAVE_SYS_CDEFS_H
36
#include <sys/cdefs.h>
37
#endif
38
__RCSID("$NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 christos Exp $");
39
40
#include <stdio.h>
41
#ifdef HAVE_LIBUTIL_H
42
#include <libutil.h>
43
#endif
44
#ifdef HAVE_UTIL_H
45
#include <util.h>
46
#endif
47
#include <string.h>
48
#include <ctype.h>
49
#include <inttypes.h>
50
#include <netdb.h>
51
#include <unistd.h>
52
#include <pwd.h>
53
#include <syslog.h>
54
#include <errno.h>
55
#include <stdlib.h>
56
#include <limits.h>
57
#include <ifaddrs.h>
58
#include <arpa/inet.h>
59
#include <netinet/in.h>
60
#include <net/if.h>
61
#include <net/route.h>
62
#include <sys/socket.h>
63
#include <dirent.h>
64
65
#include "bl.h"
66
#include "internal.h"
67
#include "support.h"
68
#include "conf.h"
69
70
71
struct sockaddr_if {
72
uint8_t sif_len;
73
sa_family_t sif_family;
74
in_port_t sif_port;
75
char sif_name[16];
76
};
77
78
#define SIF_NAME(a) \
79
((const struct sockaddr_if *)(const void *)(a))->sif_name
80
81
static int conf_is_interface(const char *);
82
83
#define FSTAR -1
84
#define FEQUAL -2
85
86
static void
87
advance(char **p)
88
{
89
char *ep = *p;
90
while (*ep && !isspace((unsigned char)*ep))
91
ep++;
92
while (*ep && isspace((unsigned char)*ep))
93
*ep++ = '\0';
94
*p = ep;
95
}
96
97
static int
98
conf_getnum(const char *f, size_t l, bool local, void *rp, const char *name,
99
const char *p)
100
{
101
int e;
102
intmax_t im;
103
int *r = rp;
104
105
if (strcmp(p, "*") == 0) {
106
*r = FSTAR;
107
return 0;
108
}
109
if (strcmp(p, "=") == 0) {
110
if (local)
111
goto out;
112
*r = FEQUAL;
113
return 0;
114
}
115
116
im = strtoi(p, NULL, 0, 0, INT_MAX, &e);
117
if (e == 0) {
118
*r = (int)im;
119
return 0;
120
}
121
122
if (f == NULL)
123
return -1;
124
(*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l,
125
name, p);
126
return -1;
127
out:
128
(*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config",
129
__func__, f, l, name);
130
return -1;
131
132
}
133
134
static int
135
conf_getnfail(const char *f, size_t l, bool local, struct conf *c,
136
const char *p)
137
{
138
return conf_getnum(f, l, local, &c->c_nfail, "nfail", p);
139
}
140
141
static int
142
conf_getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p)
143
{
144
int e;
145
char *ep;
146
intmax_t tot, im;
147
148
tot = 0;
149
if (strcmp(p, "*") == 0) {
150
c->c_duration = FSTAR;
151
return 0;
152
}
153
if (strcmp(p, "=") == 0) {
154
if (local)
155
goto out;
156
c->c_duration = FEQUAL;
157
return 0;
158
}
159
again:
160
im = strtoi(p, &ep, 0, 0, INT_MAX, &e);
161
162
if (e == ENOTSUP) {
163
switch (*ep) {
164
case 'd':
165
im *= 24;
166
/*FALLTHROUGH*/
167
case 'h':
168
im *= 60;
169
/*FALLTHROUGH*/
170
case 'm':
171
im *= 60;
172
/*FALLTHROUGH*/
173
case 's':
174
e = 0;
175
tot += im;
176
if (ep[1] != '\0') {
177
p = ep + 2;
178
goto again;
179
}
180
break;
181
}
182
} else
183
tot = im;
184
185
if (e == 0) {
186
c->c_duration = (int)tot;
187
return 0;
188
}
189
190
if (f == NULL)
191
return -1;
192
(*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p);
193
return -1;
194
out:
195
(*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local"
196
" config", __func__, f, l);
197
return -1;
198
199
}
200
201
static int
202
conf_getport(const char *f, size_t l, bool local, void *r, const char *p)
203
{
204
struct servent *sv;
205
206
// XXX: Pass in the proto instead
207
if ((sv = getservbyname(p, "tcp")) != NULL) {
208
*(int *)r = ntohs(sv->s_port);
209
return 0;
210
}
211
if ((sv = getservbyname(p, "udp")) != NULL) {
212
*(int *)r = ntohs(sv->s_port);
213
return 0;
214
}
215
216
return conf_getnum(f, l, local, r, "service", p);
217
}
218
219
static int
220
conf_getmask(const char *f, size_t l, bool local, const char **p, int *mask)
221
{
222
char *d;
223
const char *s = *p;
224
225
if ((d = strchr(s, ':')) != NULL) {
226
*d++ = '\0';
227
*p = d;
228
}
229
if ((d = strchr(s, '/')) == NULL) {
230
*mask = FSTAR;
231
return 0;
232
}
233
234
*d++ = '\0';
235
return conf_getnum(f, l, local, mask, "mask", d);
236
}
237
238
static int
239
conf_gethostport(const char *f, size_t l, bool local, struct conf *c,
240
const char *p)
241
{
242
char *d; // XXX: Ok to write to string.
243
in_port_t *port = NULL;
244
const char *pstr;
245
246
if (strcmp(p, "*") == 0) {
247
c->c_port = FSTAR;
248
c->c_lmask = FSTAR;
249
return 0;
250
}
251
252
if ((d = strchr(p, ']')) != NULL) {
253
*d++ = '\0';
254
pstr = d;
255
p++;
256
} else
257
pstr = p;
258
259
if (conf_getmask(f, l, local, &pstr, &c->c_lmask) == -1)
260
goto out;
261
262
if (d) {
263
struct sockaddr_in6 *sin6 = (void *)&c->c_ss;
264
if (debug)
265
(*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p);
266
if (strcmp(p, "*") != 0) {
267
if (inet_pton(AF_INET6, p, &sin6->sin6_addr) != 1)
268
goto out;
269
sin6->sin6_family = AF_INET6;
270
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
271
sin6->sin6_len = sizeof(*sin6);
272
#endif
273
port = &sin6->sin6_port;
274
}
275
if (!*pstr)
276
pstr = "*";
277
} else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) {
278
if (pstr == p)
279
pstr = "*";
280
struct sockaddr_in *sin = (void *)&c->c_ss;
281
struct sockaddr_if *sif = (void *)&c->c_ss;
282
if (debug)
283
(*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p);
284
if (strcmp(p, "*") != 0) {
285
if (conf_is_interface(p)) {
286
if (!local)
287
goto out2;
288
if (debug)
289
(*lfun)(LOG_DEBUG, "%s: interface %s",
290
__func__, p);
291
if (c->c_lmask != FSTAR)
292
goto out1;
293
sif->sif_family = AF_MAX;
294
strlcpy(sif->sif_name, p,
295
sizeof(sif->sif_name));
296
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
297
sif->sif_len = sizeof(*sif);
298
#endif
299
port = &sif->sif_port;
300
} else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1)
301
{
302
sin->sin_family = AF_INET;
303
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
304
sin->sin_len = sizeof(*sin);
305
#endif
306
port = &sin->sin_port;
307
} else
308
goto out;
309
}
310
}
311
312
if (conf_getport(f, l, local, &c->c_port, pstr) == -1)
313
return -1;
314
315
if (port && c->c_port != FSTAR && c->c_port != FEQUAL)
316
*port = htons((in_port_t)c->c_port);
317
return 0;
318
out:
319
(*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, p);
320
return -1;
321
out1:
322
(*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with "
323
"interface [%s]", __func__, f, l, c->c_lmask, p);
324
return -1;
325
out2:
326
(*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense "
327
"with remote config [%s]", __func__, f, l, p);
328
return -1;
329
}
330
331
static int
332
conf_getproto(const char *f, size_t l, bool local __unused, struct conf *c,
333
const char *p)
334
{
335
if (strcmp(p, "stream") == 0) {
336
c->c_proto = IPPROTO_TCP;
337
return 0;
338
}
339
if (strcmp(p, "dgram") == 0) {
340
c->c_proto = IPPROTO_UDP;
341
return 0;
342
}
343
return conf_getnum(f, l, local, &c->c_proto, "protocol", p);
344
}
345
346
static int
347
conf_getfamily(const char *f, size_t l, bool local __unused, struct conf *c,
348
const char *p)
349
{
350
if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) {
351
c->c_family = p[3] == '6' ? AF_INET6 : AF_INET;
352
return 0;
353
}
354
return conf_getnum(f, l, local, &c->c_family, "family", p);
355
}
356
357
static int
358
conf_getuid(const char *f, size_t l, bool local __unused, struct conf *c,
359
const char *p)
360
{
361
struct passwd *pw;
362
363
if ((pw = getpwnam(p)) != NULL) {
364
c->c_uid = (int)pw->pw_uid;
365
return 0;
366
}
367
368
return conf_getnum(f, l, local, &c->c_uid, "user", p);
369
}
370
371
372
static int
373
conf_getname(const char *f, size_t l, bool local, struct conf *c,
374
const char *p)
375
{
376
if (conf_getmask(f, l, local, &p, &c->c_rmask) == -1)
377
return -1;
378
379
if (strcmp(p, "*") == 0) {
380
strlcpy(c->c_name, rulename, CONFNAMESZ);
381
return 0;
382
}
383
384
if (strcmp(p, "=") == 0) {
385
if (local)
386
goto out;
387
c->c_name[0] = '\0';
388
return 0;
389
}
390
391
snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p);
392
return 0;
393
out:
394
(*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local"
395
" config", __func__, f, l);
396
return -1;
397
}
398
399
static int
400
getvalue(const char *f, size_t l, bool local, void *r, char **p,
401
int (*fun)(const char *, size_t, bool, struct conf *, const char *))
402
{
403
char *ep = *p;
404
405
advance(p);
406
return (*fun)(f, l, local, r, ep);
407
}
408
409
410
static int
411
conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local)
412
{
413
int e;
414
415
c->c_lineno = l;
416
417
while (*p && isspace((unsigned char)*p))
418
p++;
419
420
memset(c, 0, sizeof(*c));
421
e = getvalue(f, l, local, c, &p, conf_gethostport);
422
if (e) return -1;
423
e = getvalue(f, l, local, c, &p, conf_getproto);
424
if (e) return -1;
425
e = getvalue(f, l, local, c, &p, conf_getfamily);
426
if (e) return -1;
427
e = getvalue(f, l, local, c, &p, conf_getuid);
428
if (e) return -1;
429
e = getvalue(f, l, local, c, &p, conf_getname);
430
if (e) return -1;
431
e = getvalue(f, l, local, c, &p, conf_getnfail);
432
if (e) return -1;
433
e = getvalue(f, l, local, c, &p, conf_getsecs);
434
if (e) return -1;
435
436
return 0;
437
}
438
439
static int
440
conf_sort(const void *v1, const void *v2)
441
{
442
const struct conf *c1 = v1;
443
const struct conf *c2 = v2;
444
445
#define CMP(a, b, f) \
446
if ((a)->f > (b)->f) return -1; \
447
else if ((a)->f < (b)->f) return 1
448
449
CMP(c1, c2, c_ss.ss_family);
450
CMP(c1, c2, c_lmask);
451
CMP(c1, c2, c_port);
452
CMP(c1, c2, c_proto);
453
CMP(c1, c2, c_family);
454
CMP(c1, c2, c_rmask);
455
CMP(c1, c2, c_uid);
456
#undef CMP
457
return 0;
458
}
459
460
static int
461
conf_is_interface(const char *name)
462
{
463
const struct ifaddrs *ifa;
464
465
for (ifa = ifas; ifa; ifa = ifa->ifa_next)
466
if (strcmp(ifa->ifa_name, name) == 0)
467
return 1;
468
return 0;
469
}
470
471
#define MASK(m) ((uint32_t)~((1 << (32 - (m))) - 1))
472
473
static int
474
conf_amask_eq(const void *v1, const void *v2, size_t len, int mask)
475
{
476
const uint32_t *a1 = v1;
477
const uint32_t *a2 = v2;
478
uint32_t m;
479
int omask = mask;
480
481
switch (mask) {
482
case FSTAR:
483
if (memcmp(v1, v2, len) == 0)
484
return 1;
485
goto out;
486
case FEQUAL:
487
(*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
488
mask);
489
abort();
490
default:
491
break;
492
}
493
494
for (size_t i = 0; i < (len >> 2); i++) {
495
if (mask > 32) {
496
m = htonl((uint32_t)~0);
497
mask -= 32;
498
} else if (mask) {
499
m = htonl(MASK(mask));
500
mask = 0;
501
} else
502
return 1;
503
if ((a1[i] & m) != (a2[i] & m))
504
goto out;
505
}
506
return 1;
507
out:
508
if (debug > 1) {
509
char b1[256], b2[256];
510
blhexdump(b1, sizeof(b1), "a1", v1, len);
511
blhexdump(b2, sizeof(b2), "a2", v2, len);
512
(*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__,
513
b1, b2, omask);
514
}
515
return 0;
516
}
517
518
/*
519
* Apply the mask to the given address
520
*/
521
static void
522
conf_apply_mask(void *v, size_t len, int mask)
523
{
524
uint32_t *a = v;
525
uint32_t m;
526
527
switch (mask) {
528
case FSTAR:
529
return;
530
case FEQUAL:
531
(*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
532
mask);
533
abort();
534
default:
535
break;
536
}
537
len >>= 2;
538
539
for (size_t i = 0; i < len; i++) {
540
if (mask > 32) {
541
m = htonl((uint32_t)~0);
542
mask -= 32;
543
} else if (mask) {
544
m = htonl(MASK(mask));
545
mask = 0;
546
} else
547
m = 0;
548
a[i] &= m;
549
}
550
}
551
552
/*
553
* apply the mask and the port to the address given
554
*/
555
static void
556
conf_addr_set(struct conf *c, const struct sockaddr_storage *ss)
557
{
558
struct sockaddr_in *sin;
559
struct sockaddr_in6 *sin6;
560
in_port_t *port;
561
void *addr;
562
size_t alen;
563
564
c->c_lmask = c->c_rmask;
565
c->c_ss = *ss;
566
567
if (c->c_ss.ss_family != c->c_family) {
568
(*lfun)(LOG_CRIT, "%s: Internal error: mismatched family "
569
"%u != %u", __func__, c->c_ss.ss_family, c->c_family);
570
abort();
571
}
572
573
switch (c->c_ss.ss_family) {
574
case AF_INET:
575
sin = (void *)&c->c_ss;
576
port = &sin->sin_port;
577
addr = &sin->sin_addr;
578
alen = sizeof(sin->sin_addr);
579
break;
580
case AF_INET6:
581
sin6 = (void *)&c->c_ss;
582
port = &sin6->sin6_port;
583
addr = &sin6->sin6_addr;
584
alen = sizeof(sin6->sin6_addr);
585
break;
586
default:
587
(*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
588
__func__, c->c_ss.ss_family);
589
abort();
590
}
591
592
*port = htons((in_port_t)c->c_port);
593
conf_apply_mask(addr, alen, c->c_lmask);
594
if (c->c_lmask == FSTAR)
595
c->c_lmask = (int)(alen * 8);
596
if (debug) {
597
char buf[128];
598
sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss);
599
(*lfun)(LOG_DEBUG, "Applied address %s", buf);
600
}
601
}
602
603
/*
604
* Compared two addresses for equality applying the mask
605
*/
606
static int
607
conf_inet_eq(const void *v1, const void *v2, int mask)
608
{
609
const struct sockaddr *sa1 = v1;
610
const struct sockaddr *sa2 = v2;
611
size_t size;
612
613
if (sa1->sa_family != sa2->sa_family)
614
return 0;
615
616
switch (sa1->sa_family) {
617
case AF_INET: {
618
const struct sockaddr_in *s1 = v1;
619
const struct sockaddr_in *s2 = v2;
620
size = sizeof(s1->sin_addr);
621
v1 = &s1->sin_addr;
622
v2 = &s2->sin_addr;
623
break;
624
}
625
626
case AF_INET6: {
627
const struct sockaddr_in6 *s1 = v1;
628
const struct sockaddr_in6 *s2 = v2;
629
size = sizeof(s1->sin6_addr);
630
v1 = &s1->sin6_addr;
631
v2 = &s2->sin6_addr;
632
break;
633
}
634
635
default:
636
(*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
637
__func__, sa1->sa_family);
638
abort();
639
}
640
641
return conf_amask_eq(v1, v2, size, mask);
642
}
643
644
static int
645
conf_addr_in_interface(const struct sockaddr_storage *s1,
646
const struct sockaddr_storage *s2, int mask)
647
{
648
const char *name = SIF_NAME(s2);
649
const struct ifaddrs *ifa;
650
651
for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
652
if ((ifa->ifa_flags & IFF_UP) == 0)
653
continue;
654
655
if (strcmp(ifa->ifa_name, name) != 0)
656
continue;
657
658
if (s1->ss_family != ifa->ifa_addr->sa_family)
659
continue;
660
661
bool eq;
662
switch (s1->ss_family) {
663
case AF_INET:
664
case AF_INET6:
665
eq = conf_inet_eq(ifa->ifa_addr, s1, mask);
666
break;
667
default:
668
(*lfun)(LOG_ERR, "Bad family %u", s1->ss_family);
669
continue;
670
}
671
if (eq)
672
return 1;
673
}
674
return 0;
675
}
676
677
static int
678
conf_addr_eq(const struct sockaddr_storage *s1,
679
const struct sockaddr_storage *s2, int mask)
680
{
681
switch (s2->ss_family) {
682
case 0:
683
return 1;
684
case AF_MAX:
685
return conf_addr_in_interface(s1, s2, mask);
686
case AF_INET:
687
case AF_INET6:
688
return conf_inet_eq(s1, s2, mask);
689
default:
690
(*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
691
__func__, s1->ss_family);
692
abort();
693
}
694
}
695
696
static int
697
conf_eq(const struct conf *c1, const struct conf *c2)
698
{
699
if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, FSTAR))
700
return 0;
701
702
#define CMP(a, b, f) \
703
if ((a)->f != (b)->f) \
704
return 0;
705
706
CMP(c1, c2, c_port);
707
CMP(c1, c2, c_proto);
708
CMP(c1, c2, c_family);
709
CMP(c1, c2, c_uid);
710
#undef CMP
711
712
return 1;
713
}
714
715
static int
716
conf_match(const struct conf *c1, const struct conf *c2)
717
{
718
719
if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
720
return 0;
721
722
#define CMP(a, b, f) \
723
if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \
724
if (debug > 1) \
725
(*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \
726
__STRING(f), (a)->f, (b)->f); \
727
return 0; \
728
}
729
CMP(c1, c2, c_port);
730
CMP(c1, c2, c_proto);
731
CMP(c1, c2, c_family);
732
CMP(c1, c2, c_uid);
733
#undef CMP
734
return 1;
735
}
736
737
static const char *
738
conf_num(char *b, size_t l, int n)
739
{
740
switch (n) {
741
case FSTAR:
742
return "*";
743
case FEQUAL:
744
return "=";
745
default:
746
snprintf(b, l, "%d", n);
747
return b;
748
}
749
}
750
751
static const char *
752
fmtname(const char *n) {
753
size_t l = strlen(rulename);
754
if (l == 0)
755
return "*";
756
if (strncmp(n, rulename, l) == 0) {
757
if (n[l] != '\0')
758
return n + l;
759
else
760
return "*";
761
} else if (!*n)
762
return "=";
763
else
764
return n;
765
}
766
767
static void
768
fmtport(char *b, size_t l, int port)
769
{
770
char buf[128];
771
772
if (port == FSTAR)
773
return;
774
775
if (b[0] == '\0' || strcmp(b, "*") == 0)
776
snprintf(b, l, "%d", port);
777
else {
778
snprintf(buf, sizeof(buf), ":%d", port);
779
strlcat(b, buf, l);
780
}
781
}
782
783
static const char *
784
fmtmask(char *b, size_t l, int fam, int mask)
785
{
786
char buf[128];
787
788
switch (mask) {
789
case FSTAR:
790
return "";
791
case FEQUAL:
792
if (strcmp(b, "=") == 0)
793
return "";
794
else {
795
strlcat(b, "/=", l);
796
return b;
797
}
798
default:
799
break;
800
}
801
802
switch (fam) {
803
case AF_INET:
804
if (mask == 32)
805
return "";
806
break;
807
case AF_INET6:
808
if (mask == 128)
809
return "";
810
break;
811
default:
812
break;
813
}
814
815
snprintf(buf, sizeof(buf), "/%d", mask);
816
strlcat(b, buf, l);
817
return b;
818
}
819
820
static const char *
821
conf_namemask(char *b, size_t l, const struct conf *c)
822
{
823
strlcpy(b, fmtname(c->c_name), l);
824
fmtmask(b, l, c->c_family, c->c_rmask);
825
return b;
826
}
827
828
const char *
829
conf_print(char *buf, size_t len, const char *pref, const char *delim,
830
const struct conf *c)
831
{
832
char ha[128], hb[32], b[5][64];
833
int sp;
834
835
#define N(n, v) conf_num(b[n], sizeof(b[n]), (v))
836
837
switch (c->c_ss.ss_family) {
838
case 0:
839
snprintf(ha, sizeof(ha), "*");
840
break;
841
case AF_MAX:
842
snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss));
843
break;
844
default:
845
sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss);
846
break;
847
}
848
849
fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask);
850
fmtport(ha, sizeof(ha), c->c_port);
851
852
sp = *delim == '\t' ? 20 : -1;
853
hb[0] = '\0';
854
if (*delim)
855
snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s"
856
"%s%s" "%s%s%s",
857
pref, sp, sp, ha, delim, N(0, c->c_proto), delim,
858
N(1, c->c_family), delim, N(2, c->c_uid), delim,
859
conf_namemask(hb, sizeof(hb), c), delim,
860
N(3, c->c_nfail), delim, N(4, c->c_duration));
861
else
862
snprintf(buf, len, "%starget:%s, proto:%s, family:%s, "
863
"uid:%s, name:%s, nfail:%s, duration:%s", pref,
864
ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid),
865
conf_namemask(hb, sizeof(hb), c),
866
N(3, c->c_nfail), N(4, c->c_duration));
867
return buf;
868
}
869
870
/*
871
* Apply the local config match to the result
872
*/
873
static void
874
conf_apply(struct conf *c, const struct conf *sc)
875
{
876
char buf[BUFSIZ];
877
878
if (debug) {
879
(*lfun)(LOG_DEBUG, "%s: %s", __func__,
880
conf_print(buf, sizeof(buf), "merge:\t", "", sc));
881
(*lfun)(LOG_DEBUG, "%s: %s", __func__,
882
conf_print(buf, sizeof(buf), "to:\t", "", c));
883
}
884
memcpy(c->c_name, sc->c_name, CONFNAMESZ);
885
c->c_uid = sc->c_uid;
886
c->c_rmask = sc->c_rmask;
887
c->c_nfail = sc->c_nfail;
888
c->c_duration = sc->c_duration;
889
890
if (debug)
891
(*lfun)(LOG_DEBUG, "%s: %s", __func__,
892
conf_print(buf, sizeof(buf), "result:\t", "", c));
893
}
894
895
/*
896
* Merge a remote configuration to the result
897
*/
898
static void
899
conf_merge(struct conf *c, const struct conf *sc)
900
{
901
char buf[BUFSIZ];
902
903
if (debug) {
904
(*lfun)(LOG_DEBUG, "%s: %s", __func__,
905
conf_print(buf, sizeof(buf), "merge:\t", "", sc));
906
(*lfun)(LOG_DEBUG, "%s: %s", __func__,
907
conf_print(buf, sizeof(buf), "to:\t", "", c));
908
}
909
910
if (sc->c_name[0])
911
memcpy(c->c_name, sc->c_name, CONFNAMESZ);
912
if (sc->c_uid != FEQUAL)
913
c->c_uid = sc->c_uid;
914
if (sc->c_rmask != FEQUAL)
915
c->c_lmask = c->c_rmask = sc->c_rmask;
916
if (sc->c_nfail != FEQUAL)
917
c->c_nfail = sc->c_nfail;
918
if (sc->c_duration != FEQUAL)
919
c->c_duration = sc->c_duration;
920
if (debug)
921
(*lfun)(LOG_DEBUG, "%s: %s", __func__,
922
conf_print(buf, sizeof(buf), "result:\t", "", c));
923
}
924
925
static void
926
confset_init(struct confset *cs)
927
{
928
cs->cs_c = NULL;
929
cs->cs_n = 0;
930
cs->cs_m = 0;
931
}
932
933
static int
934
confset_grow(struct confset *cs)
935
{
936
void *tc;
937
938
cs->cs_m += 10;
939
tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c));
940
if (tc == NULL) {
941
(*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__);
942
return -1;
943
}
944
cs->cs_c = tc;
945
return 0;
946
}
947
948
static struct conf *
949
confset_get(struct confset *cs)
950
{
951
return &cs->cs_c[cs->cs_n];
952
}
953
954
static bool
955
confset_full(const struct confset *cs)
956
{
957
return cs->cs_n == cs->cs_m;
958
}
959
960
static void
961
confset_sort(struct confset *cs)
962
{
963
qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort);
964
}
965
966
static void
967
confset_add(struct confset *cs)
968
{
969
cs->cs_n++;
970
}
971
972
static void
973
confset_free(struct confset *cs)
974
{
975
free(cs->cs_c);
976
confset_init(cs);
977
}
978
979
static void
980
confset_merge(struct confset *dc, struct confset *sc)
981
{
982
size_t i, j;
983
char buf[BUFSIZ];
984
985
/* Check each rule of the src confset (sc) */
986
for (i = 0; i < sc->cs_n; i++) {
987
/* Compare to each rule in the dest confset (dc) */
988
for (j = 0; j < dc->cs_n; j++) {
989
if (conf_eq(&dc->cs_c[j], &sc->cs_c[i])) {
990
break;
991
}
992
}
993
994
if (j == dc->cs_n) {
995
/* This is a new rule to add to the dest confset. */
996
if (confset_full(dc) && confset_grow(dc) == -1)
997
return;
998
999
*confset_get(dc) = sc->cs_c[i];
1000
confset_add(dc);
1001
continue;
1002
}
1003
1004
/* We had a match above. */
1005
/*
1006
* Check whether the rule from the src confset is more
1007
* restrictive than the existing one. Adjust the
1008
* existing rule if necessary.
1009
*/
1010
if (sc->cs_c[i].c_nfail == dc->cs_c[j].c_nfail &&
1011
sc->cs_c[i].c_duration && dc->cs_c[j].c_duration) {
1012
(*lfun)(LOG_DEBUG, "skipping existing rule: %s",
1013
conf_print(buf, sizeof (buf), "", "\t", &sc->cs_c[i]));
1014
continue;
1015
}
1016
1017
if (sc->cs_c[i].c_nfail < dc->cs_c[j].c_nfail)
1018
dc->cs_c[j].c_nfail = sc->cs_c[i].c_nfail;
1019
1020
if (sc->cs_c[i].c_duration > dc->cs_c[j].c_duration)
1021
dc->cs_c[j].c_duration = sc->cs_c[i].c_duration;
1022
1023
(*lfun)(LOG_DEBUG, "adjusted existing rule: %s",
1024
conf_print(buf, sizeof (buf), "", "\t", &dc->cs_c[j]));
1025
}
1026
1027
confset_free(sc);
1028
}
1029
1030
static void
1031
confset_list(const struct confset *cs, const char *msg, const char *where)
1032
{
1033
char buf[BUFSIZ];
1034
1035
(*lfun)(LOG_DEBUG, "[%s]", msg);
1036
(*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration",
1037
where);
1038
for (size_t i = 0; i < cs->cs_n; i++)
1039
(*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t",
1040
&cs->cs_c[i]));
1041
}
1042
1043
/*
1044
* Match a configuration against the given list and apply the function
1045
* to it, returning the matched entry number.
1046
*/
1047
static size_t
1048
confset_match(const struct confset *cs, struct conf *c,
1049
void (*fun)(struct conf *, const struct conf *))
1050
{
1051
char buf[BUFSIZ];
1052
size_t i;
1053
1054
for (i = 0; i < cs->cs_n; i++) {
1055
if (debug)
1056
(*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
1057
"check:\t", "", &cs->cs_c[i]));
1058
if (conf_match(c, &cs->cs_c[i])) {
1059
if (debug)
1060
(*lfun)(LOG_DEBUG, "%s",
1061
conf_print(buf, sizeof(buf),
1062
"found:\t", "", &cs->cs_c[i]));
1063
(*fun)(c, &cs->cs_c[i]);
1064
break;
1065
}
1066
}
1067
return i;
1068
}
1069
1070
#ifdef AF_ROUTE
1071
static int
1072
conf_route_perm(int fd) {
1073
#if defined(RTM_IFANNOUNCE) && defined(SA_SIZE)
1074
/*
1075
* Send a routing message that is not supported to check for access
1076
* We expect EOPNOTSUPP for having access, since we are sending a
1077
* request the system does not understand and EACCES if we don't have
1078
* access.
1079
*/
1080
static struct sockaddr_in sin = {
1081
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1082
.sin_len = sizeof(sin),
1083
#endif
1084
.sin_family = AF_INET,
1085
};
1086
char buf[4096];
1087
struct rt_msghdr *rtm = (void *)buf;
1088
char *cp = (char *)(rtm + 1);
1089
size_t l;
1090
1091
#define NEXTADDR(s) \
1092
l = SA_SIZE(sizeof(*s)); memmove(cp, s, l); cp += l;
1093
memset(buf, 0, sizeof(buf));
1094
rtm->rtm_type = RTM_IFANNOUNCE;
1095
rtm->rtm_flags = 0;
1096
rtm->rtm_addrs = RTA_DST|RTA_GATEWAY;
1097
rtm->rtm_version = RTM_VERSION;
1098
rtm->rtm_seq = 666;
1099
NEXTADDR(&sin);
1100
NEXTADDR(&sin);
1101
rtm->rtm_msglen = (u_short)((char *)cp - (char *)rtm);
1102
if (write(fd, rtm, rtm->rtm_msglen) != -1) {
1103
(*lfun)(LOG_ERR, "Writing to routing socket succeeded!");
1104
return 0;
1105
}
1106
switch (errno) {
1107
case EACCES:
1108
return 0;
1109
case EOPNOTSUPP:
1110
return 1;
1111
default:
1112
(*lfun)(LOG_ERR,
1113
"Unexpected error writing to routing socket (%m)");
1114
return 0;
1115
}
1116
#else
1117
return 0;
1118
#endif
1119
}
1120
#endif
1121
1122
static int
1123
conf_handle_inet(int fd, const void *lss, struct conf *cr)
1124
{
1125
char buf[BUFSIZ];
1126
int proto;
1127
socklen_t slen = sizeof(proto);
1128
1129
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) {
1130
(*lfun)(LOG_ERR, "getsockopt failed (%m)");
1131
return -1;
1132
}
1133
1134
if (debug) {
1135
sockaddr_snprintf(buf, sizeof(buf), "%a:%p", lss);
1136
(*lfun)(LOG_DEBUG, "listening socket: %s", buf);
1137
}
1138
1139
switch (proto) {
1140
case SOCK_STREAM:
1141
cr->c_proto = IPPROTO_TCP;
1142
break;
1143
case SOCK_DGRAM:
1144
cr->c_proto = IPPROTO_UDP;
1145
break;
1146
default:
1147
(*lfun)(LOG_ERR, "unsupported protocol %d", proto);
1148
return -1;
1149
}
1150
return 0;
1151
}
1152
1153
const struct conf *
1154
conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss,
1155
struct conf *cr)
1156
{
1157
socklen_t slen;
1158
struct sockaddr_storage lss;
1159
size_t i;
1160
char buf[BUFSIZ];
1161
1162
memset(cr, 0, sizeof(*cr));
1163
slen = sizeof(lss);
1164
memset(&lss, 0, slen);
1165
if (getsockname(fd, (void *)&lss, &slen) == -1) {
1166
(*lfun)(LOG_ERR, "getsockname failed (%m)");
1167
return NULL;
1168
}
1169
1170
switch (lss.ss_family) {
1171
case AF_INET:
1172
cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port);
1173
if (conf_handle_inet(fd, &lss, cr) == -1)
1174
return NULL;
1175
break;
1176
case AF_INET6:
1177
cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port);
1178
if (conf_handle_inet(fd, &lss, cr) == -1)
1179
return NULL;
1180
break;
1181
#ifdef AF_ROUTE
1182
case AF_ROUTE:
1183
if (!conf_route_perm(fd)) {
1184
(*lfun)(LOG_ERR,
1185
"permission denied to routing socket (%m)");
1186
return NULL;
1187
}
1188
cr->c_proto = FSTAR;
1189
cr->c_port = FSTAR;
1190
memcpy(&lss, rss, sizeof(lss));
1191
break;
1192
#endif
1193
default:
1194
(*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family);
1195
return NULL;
1196
}
1197
1198
cr->c_ss = lss;
1199
cr->c_lmask = FSTAR;
1200
cr->c_uid = (int)uid;
1201
cr->c_family = lss.ss_family;
1202
cr->c_name[0] = '\0';
1203
cr->c_rmask = FSTAR;
1204
cr->c_nfail = FSTAR;
1205
cr->c_duration = FSTAR;
1206
1207
if (debug)
1208
(*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
1209
"look:\t", "", cr));
1210
1211
/* match the local config */
1212
i = confset_match(&lconf, cr, conf_apply);
1213
if (i == lconf.cs_n) {
1214
if (debug)
1215
(*lfun)(LOG_DEBUG, "not found");
1216
return NULL;
1217
}
1218
1219
conf_addr_set(cr, rss);
1220
/* match the remote config */
1221
confset_match(&rconf, cr, conf_merge);
1222
/* to apply the mask */
1223
conf_addr_set(cr, &cr->c_ss);
1224
1225
return cr;
1226
}
1227
1228
static void
1229
conf_parsefile(FILE *fp, const char *config_file)
1230
{
1231
char *line;
1232
size_t lineno, len;
1233
struct confset lc, rc, *cs;
1234
1235
lineno = 0;
1236
1237
confset_init(&rc);
1238
confset_init(&lc);
1239
cs = &lc;
1240
for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL;
1241
free(line))
1242
{
1243
if (!*line)
1244
continue;
1245
if (strcmp(line, "[local]") == 0) {
1246
cs = &lc;
1247
continue;
1248
}
1249
if (strcmp(line, "[remote]") == 0) {
1250
cs = &rc;
1251
continue;
1252
}
1253
1254
if (confset_full(cs)) {
1255
if (confset_grow(cs) == -1) {
1256
confset_free(&lc);
1257
confset_free(&rc);
1258
free(line);
1259
return;
1260
}
1261
}
1262
if (conf_parseline(config_file, lineno, line, confset_get(cs),
1263
cs == &lc) == -1)
1264
continue;
1265
confset_add(cs);
1266
}
1267
1268
confset_merge(&rconf, &rc);
1269
confset_merge(&lconf, &lc);
1270
}
1271
1272
1273
static void
1274
conf_parsedir(DIR *dir, const char *config_path)
1275
{
1276
long path_max;
1277
struct dirent *dent;
1278
char *path;
1279
FILE *fp;
1280
1281
if ((path_max = pathconf(config_path, _PC_PATH_MAX)) == -1)
1282
path_max = 2048;
1283
1284
if ((path = malloc((size_t)path_max)) == NULL) {
1285
(*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
1286
__func__);
1287
return;
1288
}
1289
1290
while ((dent = readdir(dir)) != NULL) {
1291
if (strcmp(dent->d_name, ".") == 0 ||
1292
strcmp(dent->d_name, "..") == 0)
1293
continue;
1294
1295
(void) snprintf(path, (size_t)path_max, "%s/%s", config_path,
1296
dent->d_name);
1297
if ((fp = fopen(path, "r")) == NULL) {
1298
(*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
1299
path);
1300
continue;
1301
}
1302
conf_parsefile(fp, path);
1303
fclose(fp);
1304
}
1305
1306
free(path);
1307
}
1308
1309
void
1310
conf_parse(const char *config_path)
1311
{
1312
char *path;
1313
DIR *dir;
1314
FILE *fp;
1315
1316
if ((dir = opendir(config_path)) != NULL) {
1317
/*
1318
* If config_path is a directory, parse the configuration files
1319
* in the directory. Then we're done here.
1320
*/
1321
conf_parsedir(dir, config_path);
1322
closedir(dir);
1323
goto out;
1324
} else if ((fp = fopen(config_path, "r")) != NULL) {
1325
/* If config_path is a file, parse it. */
1326
conf_parsefile(fp, config_path);
1327
fclose(fp);
1328
}
1329
1330
/*
1331
* Append ".d" to config_path, and if that is a directory, parse the
1332
* configuration files in the directory.
1333
*/
1334
if (asprintf(&path, "%s.d", config_path) < 0) {
1335
(*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
1336
__func__);
1337
goto out;
1338
}
1339
1340
if ((dir = opendir(path)) != NULL) {
1341
conf_parsedir(dir, path);
1342
closedir(dir);
1343
}
1344
free(path);
1345
1346
out:
1347
if (dir == NULL && fp == NULL) {
1348
(*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
1349
config_path);
1350
return;
1351
}
1352
1353
confset_sort(&lconf);
1354
confset_sort(&rconf);
1355
1356
if (debug) {
1357
confset_list(&lconf, "local", "target");
1358
confset_list(&rconf, "remote", "source");
1359
}
1360
}
1361
1362