GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/spamd/sdl.c Lines: 0 228 0.0 %
Date: 2017-11-13 Branches: 0 156 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: sdl.c,v 1.24 2017/10/18 17:31:01 millert Exp $ */
2
3
/*
4
 * Copyright (c) 2003-2007 Bob Beck.  All rights reserved.
5
 *
6
 * Permission to use, copy, modify, and 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
/*
20
 * sdl.c - Implement spamd source lists
21
 *
22
 * This consists of everything we need to do to determine which lists
23
 * someone is on. Spamd gets the connecting address, and looks it up
24
 * against all lists to determine what deferral messages to feed back
25
 * to the connecting machine. - The redirection to spamd will happen
26
 * from pf in the kernel, first match will divert to us. Spamd (along with
27
 * setup) must keep track of *all* matches, so as to tell someone all the
28
 * lists that they are on.
29
 */
30
31
#include <sys/types.h>
32
#include <sys/socket.h>
33
#include <netinet/in.h>
34
#include <arpa/inet.h>
35
#include <errno.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include "sdl.h"
40
41
static void sdl_free(struct sdlist *);
42
static void sdl_clear(struct sdlist *);
43
44
extern int debug;
45
struct sdlist *blacklists = NULL;
46
int blc = 0, blu = 0;
47
48
static int
49
compar_v4(const void *va, const void *vb)
50
{
51
	const struct sdentry_v4 *a = va;
52
	const struct sdentry_v4 *b = vb;
53
	struct in_addr aa;
54
	struct in_addr bb;
55
56
	/* The mask has already been applied. */
57
	aa.s_addr = ntohl(a->sda.s_addr);
58
	bb.s_addr = ntohl(b->sda.s_addr);
59
60
	if (aa.s_addr > bb.s_addr)
61
		return (1);
62
	if (aa.s_addr < bb.s_addr)
63
		return (-1);
64
	return (0);
65
}
66
67
static int
68
compar_v6(const void *va, const void *vb)
69
{
70
	const struct sdentry_v6 *a = va;
71
	const struct sdentry_v6 *b = vb;
72
	struct sdaddr_v6 aa;
73
	struct sdaddr_v6 bb;
74
75
	/* The mask has already been applied. */
76
	aa.addr32[0] = ntohl(a->sda.addr32[0]);
77
	aa.addr32[1] = ntohl(a->sda.addr32[1]);
78
	aa.addr32[2] = ntohl(a->sda.addr32[2]);
79
	aa.addr32[3] = ntohl(a->sda.addr32[3]);
80
81
	bb.addr32[0] = ntohl(b->sda.addr32[0]);
82
	bb.addr32[1] = ntohl(b->sda.addr32[1]);
83
	bb.addr32[2] = ntohl(b->sda.addr32[2]);
84
	bb.addr32[3] = ntohl(b->sda.addr32[3]);
85
86
	if (aa.addr32[0] > bb.addr32[0])
87
		return (1);
88
	if (aa.addr32[0] < bb.addr32[0])
89
		return (-1);
90
	if (aa.addr32[1] > bb.addr32[1])
91
		return (1);
92
	if (aa.addr32[1] < bb.addr32[1])
93
		return (-1);
94
	if (aa.addr32[2] > bb.addr32[2])
95
		return (1);
96
	if (aa.addr32[2] < bb.addr32[2])
97
		return (-1);
98
	if (aa.addr32[3] > bb.addr32[3])
99
		return (1);
100
	if (aa.addr32[3] < bb.addr32[3])
101
		return (-1);
102
	return (0);
103
}
104
105
int
106
sdl_add(char *sdname, char *sdstring, char **v4, u_int nv4, char **v6, u_int nv6)
107
{
108
	int i, idx = -1;
109
	char astring[40];
110
	char *addr = NULL;
111
	unsigned int maskbits;
112
113
	/*
114
	 * if a blacklist of same tag name is already there, replace it,
115
	 * otherwise append.
116
	 */
117
	for (i = 0; i < blu; i++) {
118
		if (strcmp(blacklists[i].tag, sdname) == 0) {
119
			idx = i;
120
			break;
121
		}
122
	}
123
	if (idx != -1) {
124
		if (debug > 0)
125
			printf("replacing list %s; %u new entries\n",
126
			    blacklists[idx].tag, nv4 + nv6);
127
		sdl_free(&blacklists[idx]);
128
	} else {
129
		if (debug > 0)
130
			printf("adding list %s; %u entries\n", sdname, nv4 + nv6);
131
		if (blu == blc) {
132
			struct sdlist *tmp;
133
134
			tmp = reallocarray(blacklists, blc + 128,
135
			    sizeof(struct sdlist));
136
			if (tmp == NULL)
137
				return (-1);
138
			blacklists = tmp;
139
			blc += 128;
140
			sdl_clear(&blacklists[blu]);
141
		}
142
		idx = blu;
143
	}
144
145
	if ((blacklists[idx].tag = strdup(sdname)) == NULL)
146
		goto misc_error;
147
	if ((blacklists[idx].string = strdup(sdstring)) == NULL)
148
		goto misc_error;
149
150
	/*
151
	 * Cycle through addrs by family, converting. We assume they are
152
	 * correctly formatted v4 and v6 addrs, if they don't all convert
153
	 * correctly, the add fails. Each address should be address/maskbits.
154
	 */
155
	if (nv4 != 0) {
156
		blacklists[idx].v4.naddrs = nv4;
157
		blacklists[idx].v4.addrs = reallocarray(NULL, nv4,
158
		    sizeof(struct sdentry_v4));
159
		if (blacklists[idx].v4.addrs == NULL)
160
			goto misc_error;
161
		for (i = 0; i < nv4; i++) {
162
			struct in_addr *m, *n;
163
			int j;
164
165
			n = &blacklists[idx].v4.addrs[i].sda;
166
			m = &blacklists[idx].v4.addrs[i].sdm;
167
168
			addr = v4[i];
169
			j = sscanf(addr, "%15[^/]/%u", astring, &maskbits);
170
			if (j != 2)
171
				goto parse_error;
172
			/*
173
			 * sanity check! we don't allow a 0 mask -
174
			 * don't blacklist the entire net.
175
			 */
176
			if (maskbits == 0 || maskbits > 32)
177
				goto parse_error;
178
			j = inet_pton(AF_INET, astring, n);
179
			if (j != 1)
180
				goto parse_error;
181
			if (debug > 0)
182
				printf("added %s/%u\n", astring, maskbits);
183
184
			/* set mask. */
185
			m->s_addr = 0xffffffffU << (32 - maskbits);
186
			m->s_addr = htonl(m->s_addr);
187
188
			/* mask off address bits that won't ever be used */
189
			n->s_addr = n->s_addr & m->s_addr;
190
		}
191
		/* spamd-setup output is sorted in host byte order */
192
		mergesort(blacklists[idx].v4.addrs, nv4,
193
		    sizeof(struct sdentry_v4), compar_v4);
194
	}
195
	if (nv6 != 0) {
196
		blacklists[idx].v6.naddrs = nv6;
197
		blacklists[idx].v6.addrs = reallocarray(NULL, nv6,
198
		    sizeof(struct sdentry_v6));
199
		if (blacklists[idx].v6.addrs == NULL)
200
			goto misc_error;
201
202
		for (i = 0; i < nv6; i++) {
203
			int j, k;
204
			struct sdaddr_v6 *m, *n;
205
206
			n = &blacklists[idx].v6.addrs[i].sda;
207
			m = &blacklists[idx].v6.addrs[i].sdm;
208
209
			addr = v6[i];
210
			j = sscanf(addr, "%39[^/]/%u", astring, &maskbits);
211
			if (j != 2)
212
				goto parse_error;
213
			/*
214
			 * sanity check! we don't allow a 0 mask -
215
			 * don't blacklist the entire net.
216
			 */
217
			if (maskbits == 0 || maskbits > 128)
218
				goto parse_error;
219
			j = inet_pton(AF_INET6, astring, n);
220
			if (j != 1)
221
				goto parse_error;
222
			if (debug > 0)
223
				printf("added %s/%u\n", astring, maskbits);
224
225
			/* set mask, borrowed from pf */
226
			k = 0;
227
			for (j = 0; j < 4; j++)
228
				m->addr32[j] = 0;
229
			while (maskbits >= 32) {
230
				m->addr32[k++] = 0xffffffffU;
231
				maskbits -= 32;
232
			}
233
			for (j = 31; j > 31 - maskbits; --j)
234
				m->addr32[k] |= (1 << j);
235
			if (maskbits)
236
				m->addr32[k] = htonl(m->addr32[k]);
237
238
			/* mask off address bits that won't ever be used */
239
			for (j = 0; j < 4; j++)
240
				n->addr32[j] = n->addr32[j] & m->addr32[j];
241
		}
242
		/* spamd-setup output is sorted in host byte order */
243
		mergesort(blacklists[idx].v6.addrs, nv6,
244
		    sizeof(struct sdentry_v6), compar_v6);
245
	}
246
	if (idx == blu) {
247
		blu++;
248
		sdl_clear(&blacklists[blu]);
249
	}
250
	return (0);
251
 parse_error:
252
	if (debug > 0)
253
		printf("sdl_add: parse error, \"%s\"\n", addr);
254
 misc_error:
255
	sdl_free(&blacklists[idx]);
256
	if (idx != blu) {
257
		memmove(&blacklists[idx], &blacklists[idx + 1],
258
		    (blu - idx) * sizeof(*blacklists));
259
		blu--;
260
	}
261
	return (-1);
262
}
263
264
void
265
sdl_del(char *sdname)
266
{
267
	int i, idx = -1;
268
269
	for (i = 0; i < blu; i++) {
270
		if (strcmp(blacklists[i].tag, sdname) == 0) {
271
			idx = i;
272
			break;
273
		}
274
	}
275
	if (idx != -1) {
276
		if (debug > 0)
277
			printf("clearing list %s\n", sdname);
278
		/* Must preserve tag. */
279
		free(blacklists[idx].string);
280
		free(blacklists[idx].v4.addrs);
281
		free(blacklists[idx].v6.addrs);
282
		blacklists[idx].string = NULL;
283
		blacklists[idx].v4.addrs = NULL;
284
		blacklists[idx].v6.addrs = NULL;
285
		blacklists[idx].v4.naddrs = 0;
286
		blacklists[idx].v6.naddrs = 0;
287
	}
288
}
289
290
/*
291
 * Return 0 if the addresss a (with mask m) matches address key
292
 * otherwise return 1 if a > key or -1 if a < key.  It is assumed
293
 * that address a has been pre-masked out, we only need to mask key.
294
 */
295
static int
296
match_addr_v4(const void *vkey, const void *ventry)
297
{
298
	const struct in_addr *k = vkey;
299
	const struct in_addr *a = &((const struct sdentry_v4 *)ventry)->sda;
300
	const struct in_addr *m = &((const struct sdentry_v4 *)ventry)->sdm;
301
	struct in_addr kk;
302
	struct in_addr aa;
303
304
	kk.s_addr = ntohl(k->s_addr & m->s_addr);
305
	aa.s_addr = ntohl(a->s_addr);
306
	if (kk.s_addr > aa.s_addr)
307
		return (1);
308
	if (kk.s_addr < aa.s_addr)
309
		return (-1);
310
	return (0);
311
}
312
313
/*
314
 * Return 0 if the addresss a (with mask m) matches address key
315
 * otherwise return 1 if a > key or -1 if a < key.  It is assumed
316
 * that address a has been pre-masked out, we only need to mask key.
317
 */
318
static int
319
match_addr_v6(const void *vkey, const void *ventry)
320
{
321
	const struct sdaddr_v6 *k = vkey;
322
	const struct sdaddr_v6 *a = &((const struct sdentry_v6 *)ventry)->sda;
323
	const struct sdaddr_v6 *m = &((const struct sdentry_v6 *)ventry)->sdm;
324
	struct sdaddr_v6 kk;
325
	struct sdaddr_v6 aa;
326
327
	kk.addr32[0] = ntohl(k->addr32[0] & m->addr32[0]);
328
	kk.addr32[1] = ntohl(k->addr32[1] & m->addr32[1]);
329
	kk.addr32[2] = ntohl(k->addr32[2] & m->addr32[2]);
330
	kk.addr32[3] = ntohl(k->addr32[3] & m->addr32[3]);
331
332
	aa.addr32[0] = ntohl(a->addr32[0]);
333
	aa.addr32[1] = ntohl(a->addr32[1]);
334
	aa.addr32[2] = ntohl(a->addr32[2]);
335
	aa.addr32[3] = ntohl(a->addr32[3]);
336
337
	if (kk.addr32[0] > aa.addr32[0])
338
		return (1);
339
	if (kk.addr32[0] < aa.addr32[0])
340
		return (-1);
341
	if (kk.addr32[1] > aa.addr32[1])
342
		return (1);
343
	if (kk.addr32[1] < aa.addr32[1])
344
		return (-1);
345
	if (kk.addr32[2] > aa.addr32[2])
346
		return (1);
347
	if (kk.addr32[2] < aa.addr32[2])
348
		return (-1);
349
	if (kk.addr32[3] > aa.addr32[3])
350
		return (1);
351
	if (kk.addr32[3] < aa.addr32[3])
352
		return (-1);
353
	return (0);
354
}
355
356
#define grow_sdlist(sd, c, l) do {					       \
357
	if (c == l) {							       \
358
		struct sdlist **tmp;					       \
359
									       \
360
		tmp = reallocarray(sd, l + 128, sizeof(struct sdlist *));      \
361
		if (tmp == NULL) {					       \
362
			/*						       \
363
			 * XXX out of memory - return what we have	       \
364
			 */						       \
365
			return (sdnew);					       \
366
		}							       \
367
		sd = tmp;						       \
368
		l += 128;						       \
369
	}								       \
370
} while (0)
371
372
static struct sdlist **
373
sdl_lookup_v4(struct sdlist *sdl, struct in_addr *src)
374
{
375
	int matches = 0;
376
	int sdnewlen = 0;
377
	struct sdlist **sdnew = NULL;
378
379
	while (sdl->tag != NULL) {
380
		if (bsearch(src, sdl->v4.addrs, sdl->v4.naddrs,
381
		    sizeof(struct sdentry_v4), match_addr_v4) != NULL) {
382
			grow_sdlist(sdnew, matches, sdnewlen);
383
			sdnew[matches] = sdl;
384
			matches++;
385
			sdnew[matches] = NULL;
386
			break;
387
		}
388
		sdl++;
389
	}
390
	return (sdnew);
391
}
392
393
static struct sdlist **
394
sdl_lookup_v6(struct sdlist *sdl, struct sdaddr_v6 *src)
395
{
396
	int matches = 0;
397
	int sdnewlen = 0;
398
	struct sdlist **sdnew = NULL;
399
400
	while (sdl->tag != NULL) {
401
		if (bsearch(src, sdl->v6.addrs, sdl->v6.naddrs,
402
		    sizeof(struct sdentry_v6), match_addr_v6) != NULL) {
403
			grow_sdlist(sdnew, matches, sdnewlen);
404
			sdnew[matches] = sdl;
405
			matches++;
406
			sdnew[matches] = NULL;
407
			break;
408
		}
409
		sdl++;
410
	}
411
	return (sdnew);
412
}
413
414
/*
415
 * Given an address and address family
416
 * return list of pointers to matching nodes. or NULL if none.
417
 */
418
struct sdlist **
419
sdl_lookup(struct sdlist *head, int af, void *src)
420
{
421
	if (head == NULL)
422
		return (NULL);
423
424
	switch (af) {
425
	case AF_INET:
426
		return (sdl_lookup_v4(head, src));
427
	case AF_INET6:
428
		return (sdl_lookup_v6(head, src));
429
	default:
430
		return (NULL);
431
	}
432
}
433
434
static int
435
sdl_check_v4(struct sdlist *sdl, struct in_addr *src)
436
{
437
	while (sdl->tag != NULL) {
438
		if (bsearch(src, sdl->v4.addrs, sdl->v4.naddrs,
439
		    sizeof(struct sdentry_v4), match_addr_v4) != NULL)
440
			return (1);
441
		sdl++;
442
	}
443
	return (0);
444
}
445
446
static int
447
sdl_check_v6(struct sdlist *sdl, struct sdaddr_v6 *src)
448
{
449
	while (sdl->tag != NULL) {
450
		if (bsearch(src, sdl->v6.addrs, sdl->v6.naddrs,
451
		    sizeof(struct sdentry_v6), match_addr_v6) != NULL)
452
			return (1);
453
		sdl++;
454
	}
455
	return (0);
456
}
457
458
/*
459
 * Given an address and address family
460
 * returns 1 if address is on a blacklist, else 0.
461
 */
462
int
463
sdl_check(struct sdlist *head, int af, void *src)
464
{
465
	if (head == NULL)
466
		return (0);
467
468
	switch (af) {
469
	case AF_INET:
470
		return (sdl_check_v4(head, src));
471
	case AF_INET6:
472
		return (sdl_check_v6(head, src));
473
	default:
474
		return (0);
475
	}
476
}
477
478
static void
479
sdl_free(struct sdlist *sdl)
480
{
481
	free(sdl->tag);
482
	free(sdl->string);
483
	free(sdl->v4.addrs);
484
	free(sdl->v6.addrs);
485
	sdl_clear(sdl);
486
}
487
488
static void
489
sdl_clear(struct sdlist *sdl)
490
{
491
	sdl->tag = NULL;
492
	sdl->string = NULL;
493
	sdl->v4.addrs = NULL;
494
	sdl->v4.naddrs = 0;
495
	sdl->v6.addrs = NULL;
496
	sdl->v6.naddrs = 0;
497
}